В этой практической работе по курсу “Глубокое обучение на Python” вы научитесь обучать нейронную сеть распознавать рукописные цифры из набора данных MNIST.

Обновление от 08.05.2017. Программы обновлены на Keras версии 2.

Цель работы: научится оценивать влияние гиперпараметров обучения (количество эпох обучения, размер мини-выборки, количество нейронов во входном слое, количество скрытых слоев) на качество обучения нейронной сети.

Предварительные сведения

Перед выполнением работы рекомендуется посмотреть видео с объяснением, как работает программа распознавания рукописных цифр из набора MNIST.

Еще одно видео - как оценивать качество обучения нейронной сети.

Необходимое программное обеспечение

Используется библиотека Keras, а также Theano в качестве вычислительного бэкенда.

Инструкция по установке Keras и Theano с дистрибутивом Anaconda.

Базовая версия программы

Базовая версия программы, которая реализует обучение нейронной сети для распознавания рукописных цифр.

import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import np_utils

# Устанавливаем seed для повторяемости результатов
numpy.random.seed(42)

# Загружаем данные
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# Преобразование размерности изображений
X_train = X_train.reshape(60000, 784)
X_test = X_test.reshape(10000, 784)
# Нормализация данных
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255

# Преобразуем метки в категории
Y_train = np_utils.to_categorical(y_train, 10)
Y_test = np_utils.to_categorical(y_test, 10)

# Создаем последовательную модель
model = Sequential()

# Добавляем уровни сети
model.add(Dense(800, input_dim=784, activation="relu", kernel_initializer="normal"))
model.add(Dense(10, activation="softmax", kernel_initializer="normal"))

# Компилируем модель
model.compile(loss="categorical_crossentropy", optimizer="SGD", metrics=["accuracy"])

print(model.summary())

# Обучаем сеть
model.fit(X_train, Y_train, batch_size=200, epochs=100, validation_split=0.2, verbose=2)

# Оцениваем качество обучения сети на тестовых данных
scores = model.evaluate(X_test, Y_test, verbose=0)
print("Точность работы на тестовых данных: %.2f%%" % (scores[1]*100))

Также базовую версию программы можно найти в репозитории примеров курса.

Для начала запустите базовую версию программы:

python mnist.py

Примерный вывод программы:

Downloading data from https://s3.amazonaws.com/img-datasets/mnist.pkl.gz
15286272/15296311 [============================>.] - ETA: 0s____________________________________________________________________________________________________
Layer (type)                     Output Shape          Param #     Connected to                     
====================================================================================================
dense_1 (Dense)                  (None, 800)           628000      dense_input_1[0][0]              
____________________________________________________________________________________________________
dense_2 (Dense)                  (None, 10)            8010        dense_1[0][0]                    
====================================================================================================
Total params: 636010
____________________________________________________________________________________________________
None
Train on 48000 samples, validate on 12000 samples
Epoch 1/100
48000/48000 [==============================] - 3s - loss: 1.3189 - acc: 0.6915 - val_loss: 0.7800 - val_acc: 0.8505
Epoch 2/100
48000/48000 [==============================] - 3s - loss: 0.6738 - acc: 0.8465 - val_loss: 0.5386 - val_acc: 0.8773
Epoch 3/100
48000/48000 [==============================] - 3s - loss: 0.5245 - acc: 0.8703 - val_loss: 0.4507 - val_acc: 0.8889
Epoch 4/100
48000/48000 [==============================] - 3s - loss: 0.4562 - acc: 0.8827 - val_loss: 0.4029 - val_acc: 0.8979
Epoch 5/100
48000/48000 [==============================] - 3s - loss: 0.4152 - acc: 0.8908 - val_loss: 0.3727 - val_acc: 0.9028
Epoch 6/100
48000/48000 [==============================] - 3s - loss: 0.3874 - acc: 0.8961 - val_loss: 0.3521 - val_acc: 0.9070
Epoch 7/100
48000/48000 [==============================] - 4s - loss: 0.3667 - acc: 0.9011 - val_loss: 0.3358 - val_acc: 0.9108
Epoch 8/100
48000/48000 [==============================] - 3s - loss: 0.3506 - acc: 0.9044 - val_loss: 0.3229 - val_acc: 0.9130
Epoch 9/100
48000/48000 [==============================] - 2s - loss: 0.3372 - acc: 0.9076 - val_loss: 0.3129 - val_acc: 0.9160
Epoch 10/100
48000/48000 [==============================] - 2s - loss: 0.3261 - acc: 0.9103 - val_loss: 0.3035 - val_acc: 0.9180
…
Epoch 90/100
48000/48000 [==============================] - 3s - loss: 0.1241 - acc: 0.9662 - val_loss: 0.1395 - val_acc: 0.9627
Epoch 91/100
48000/48000 [==============================] - 4s - loss: 0.1232 - acc: 0.9666 - val_loss: 0.1388 - val_acc: 0.9630
Epoch 92/100
48000/48000 [==============================] - 3s - loss: 0.1222 - acc: 0.9669 - val_loss: 0.1382 - val_acc: 0.9627
Epoch 93/100
48000/48000 [==============================] - 3s - loss: 0.1212 - acc: 0.9672 - val_loss: 0.1375 - val_acc: 0.9632
Epoch 94/100
48000/48000 [==============================] - 3s - loss: 0.1203 - acc: 0.9674 - val_loss: 0.1369 - val_acc: 0.9630
Epoch 95/100
48000/48000 [==============================] - 3s - loss: 0.1194 - acc: 0.9676 - val_loss: 0.1359 - val_acc: 0.9632
Epoch 96/100
48000/48000 [==============================] - 4s - loss: 0.1185 - acc: 0.9681 - val_loss: 0.1352 - val_acc: 0.9638
Epoch 97/100
48000/48000 [==============================] - 3s - loss: 0.1176 - acc: 0.9683 - val_loss: 0.1347 - val_acc: 0.9635
Epoch 98/100
48000/48000 [==============================] - 3s - loss: 0.1167 - acc: 0.9685 - val_loss: 0.1340 - val_acc: 0.9637
Epoch 99/100
48000/48000 [==============================] - 3s - loss: 0.1159 - acc: 0.9688 - val_loss: 0.1334 - val_acc: 0.9639
Epoch 100/100
48000/48000 [==============================] - 3s - loss: 0.1150 - acc: 0.9691 - val_loss: 0.1328 - val_acc: 0.9638
Точность модели на тестовых данных: 96.34%

Для сокращения из вывода удалены данные об обучении с 11 по 89 эпохи.

Запишите точность работы сети после обучения на тестовых данных. Она приводится в последней строке вывода:

Точность модели на тестовых данных: 96.34%

Проанализируйте точность на проверочной выборке в процессе обучения. Она указывается после заголовка val_acc. В примере видно, что на начальных эпохах точность на проверочной выборке быстро увеличивается c 0.8505 на 1 эпохе до 0.9180 на 10 эпохе. Но ближе к окончанию обучения точность на проверочной выборке почти не меняется. На 98 эпохе точность составляет 0.9637, на 99 – 0.9639, на 100 – 0.9638. Это означает, что мы близки к переобучению и обучение необходимо останавливать.

Экспериментируем с гиперпараметрами обучения

Мы попытаемся улучшить качество обучения сети путем изменения гиперпараметров: количество эпох обучения, размер мини-выборки, количество нейронов во входном слое, количество скрытых слоев. Для этого проведем серию экспериментов, в каждом из которых будем менять один из гиперпараметров, и анализировать, как изменилось качество работы сети.

  1. Количество эпох обучения. Оценим влияние количества эпох обучения на качество обучения сети. Количество эпох задается в аргументе epochs метода model.fit:

     model.fit(X_train, Y_train, batch_size=200, epochs=XXX, validation_split=0.2, verbose=2)
    

    Попробуйте обучать сеть в течение 50, 75, 100 и 125 эпох. Выберите количество эпох, при котором самая высокая точность работы сети на тестовых данных.

  2. Размер мини-выборки. Оценим влияние размера мини-выборки на качество обучения сети. Размер задается в аргументе batch_size метода model.fit:

     model.fit(X_train, Y_train, batch_size=XXX, epochs=100, validation_split=0.2, verbose=2)
    

    Используйте размер мини-выборки 50, 100, 200 и 400. Выберите значение, при котором самая высокая точность работы сети на тестовых данных.

  3. Количество нейронов входного слоя. Изменяйте количество нейронов во входном слое и оцените, как оно влияет на качество обучения сети. Количество нейронов задается при создании входного слоя:

     model.add(Dense(XXX, input_dim=784, activation="relu", kernel_initializer="normal"))
    

    Используйте значения 500, 700, 900, 1200. Выберите значение, при котором самая высокая точность работы сети на тестовых данных.

  4. Добавляем скрытый слой. Добавим в нашу сеть скрытый слой, чтобы она стала глубокой:

     model.add(Dense(800, input_dim=784, activation="relu", kernel_initializer="normal"))
     model.add(Dense(600, activation="relu", kernel_initializer="normal"))
     model.add(Dense(10, activation="softmax", kernel_initializer="normal"))
    

    Попробуйте добавить скрытый слой с разным количеством нейронов: 500, 700, 900 и 1200. Выберите наиболее подходящее количество нейронов скрытого слоя.

    Оцените, как изменяется время обучения, при добавлении скрытого слоя с разным количеством нейронов.

Выбираем лучшие гиперпараметры

Создайте сеть с лучшими значениями всех гиперпараметров обучения, которые вы определили на предыдущем шаге. Увеличилась ли точность работы сети? Что можно сделать, чтобы еще больше увеличить точность?

Расскажите о своих результатах

Пишите в комментариях, какой точности вам удалось достичь, и какие гиперпараметры вы при этом использовали.

Попробуйте свои силы в соревнованиях Kaggle

Если у вас получилось увеличить точность обучения нейронной сети, то можете попробовать принять участие в соревнованиях по распознаванию рукописных цифр на сайте Kaggle. Это самый крупный сайт Data Scientist’ов, на котором проводятся соревнования по анализу данных, размещаются открытые наборы данных, а также ведется форум, посвященный практическому применению анализа данных для различных задач. На Kaggle есть соревнования по распознаванию рукописных цифр MNIST. В этом соревнования данные представлены не в традиционном формате MNIST, а в виде текстовых csv файлов. Как работать с такими файлами, а также как готовить данные для отправки на соревнования Kaggle, я описал в отдельной статье.

Для получения конкуретоспособных результатов вам может понадобиться использовать сверточные нейронные сети. Можете сначала выполнить вторую практическую работу этого курса, а затем изменить пример в работе для распознавания рукописных цифр. Если не получится сделать самостоятельно, то можете почитать отдельную статью о распознавании рукописных цифр с помощью сверточных нейронных сетей.