В этой практической работе по курсу “Глубокое обучение на Python” вы научитесь обучать нейронную сеть распознавать объекты на изображениях из набора данных CIFAR-10.

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

Обновление от 10.11.2017. Размерность изображений изменена на формат TensorFlow.

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

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

Перед выполнением работы рекомендуется посмотреть видео с объяснением, как работает программа распознавания объектов на изображениях из набора данных CIFAR-10.

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

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

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

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

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

import numpy
from keras.datasets import cifar10
from keras.models import Sequential
from keras.layers import Dense, Flatten, Activation
from keras.layers import Dropout
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.utils import np_utils
from keras.optimizers import SGD

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

# Загружаем данные
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
# Размер мини-выборки
batch_size = 32
# Количество классов изображений
nb_classes = 10
# Количество эпох для обучения
nb_epoch = 25
# Размер изображений
img_rows, img_cols = 32, 32
# Количество каналов в изображении: RGB
img_channels = 3

# Нормализуем данные
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, nb_classes)
Y_test = np_utils.to_categorical(y_test, nb_classes)

# Создаем последовательную модель
model = Sequential()
# Первый сверточный слой
model.add(Conv2D(32, (3, 3), padding='same',
                        input_shape=(32, 32, 3), activation='relu'))
# Второй сверточный слой
model.add(Conv2D(32, (3, 3), activation='relu', padding='same'))
# Первый слой подвыборки
model.add(MaxPooling2D(pool_size=(2, 2)))
# Слой регуляризации Dropout
model.add(Dropout(0.25))

# Третий сверточный слой
model.add(Conv2D(64, (3, 3), padding='same', activation='relu'))
# Четвертый сверточный слой
model.add(Conv2D(64, (3, 3), activation='relu'))
# Второй слой подвыборки
model.add(MaxPooling2D(pool_size=(2, 2)))
# Слой регуляризации Dropout
model.add(Dropout(0.25))
# Слой преобразования данных из 2D представления в плоское
model.add(Flatten())
# Полносвязный слой для классификации
model.add(Dense(512, activation='relu'))
# Слой регуляризации Dropout
model.add(Dropout(0.5))
# Выходной полносвязный слой
model.add(Dense(nb_classes, activation='softmax'))

# Задаем параметры оптимизации
sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)
model.compile(loss='categorical_crossentropy',
              optimizer=sgd,
              metrics=['accuracy'])
# Обучаем модель
model.fit(X_train, Y_train,
              batch_size=batch_size,
              epochs=nb_epoch,
              validation_split=0.1,
              shuffle=True,
              verbose=2)

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

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

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

python cifar10.py

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

Using Theano backend.
Train on 45000 samples, validate on 5000 samples
Epoch 1/25
45000/45000 [==============================] - 370s - loss: 1.7926 - acc: 0.3386 - val_loss: 1.3840 - val_acc: 0.4936
Epoch 2/25
45000/45000 [==============================] - 359s - loss: 1.3837 - acc: 0.4957 - val_loss: 1.2487 - val_acc: 0.5492
Epoch 3/25
45000/45000 [==============================] - 366s - loss: 1.2151 - acc: 0.5635 - val_loss: 1.0457 - val_acc: 0.6304
Epoch 4/25
45000/45000 [==============================] - 363s - loss: 1.0905 - acc: 0.6127 - val_loss: 0.9365 - val_acc: 0.6718
Epoch 5/25
45000/45000 [==============================] - 365s - loss: 1.0009 - acc: 0.6431 - val_loss: 0.9371 - val_acc: 0.6790
Epoch 6/25
45000/45000 [==============================] - 365s - loss: 0.9458 - acc: 0.6671 - val_loss: 0.8311 - val_acc: 0.7106
Epoch 7/25
45000/45000 [==============================] - 369s - loss: 0.8889 - acc: 0.6875 - val_loss: 0.8002 - val_acc: 0.7248
Epoch 8/25
45000/45000 [==============================] - 362s - loss: 0.8435 - acc: 0.7069 - val_loss: 0.7832 - val_acc: 0.7272
Epoch 9/25
45000/45000 [==============================] - 361s - loss: 0.8077 - acc: 0.7150 - val_loss: 0.7468 - val_acc: 0.7442
Epoch 10/25
45000/45000 [==============================] - 370s - loss: 0.7806 - acc: 0.7253 - val_loss: 0.7952 - val_acc: 0.7234
Epoch 11/25
45000/45000 [==============================] - 364s - loss: 0.7620 - acc: 0.7346 - val_loss: 0.7465 - val_acc: 0.7508
Epoch 12/25
45000/45000 [==============================] - 362s - loss: 0.7391 - acc: 0.7425 - val_loss: 0.7384 - val_acc: 0.7488
Epoch 13/25
45000/45000 [==============================] - 370s - loss: 0.7212 - acc: 0.7472 - val_loss: 0.6871 - val_acc: 0.7618
Epoch 14/25
45000/45000 [==============================] - 376s - loss: 0.7049 - acc: 0.7523 - val_loss: 0.7163 - val_acc: 0.7588
Epoch 15/25
45000/45000 [==============================] - 358s - loss: 0.7016 - acc: 0.7537 - val_loss: 0.7000 - val_acc: 0.7574
Epoch 16/25
45000/45000 [==============================] - 356s - loss: 0.6888 - acc: 0.7622 - val_loss: 0.6692 - val_acc: 0.7770
Epoch 17/25
45000/45000 [==============================] - 360s - loss: 0.6751 - acc: 0.7629 - val_loss: 0.7003 - val_acc: 0.7694
Epoch 18/25
45000/45000 [==============================] - 359s - loss: 0.6568 - acc: 0.7714 - val_loss: 0.7218 - val_acc: 0.7576
Epoch 19/25
45000/45000 [==============================] - 359s - loss: 0.6539 - acc: 0.7730 - val_loss: 0.6795 - val_acc: 0.7730
Epoch 20/25
45000/45000 [==============================] - 356s - loss: 0.6422 - acc: 0.7786 - val_loss: 0.6876 - val_acc: 0.7648
Epoch 21/25
45000/45000 [==============================] - 356s - loss: 0.6487 - acc: 0.7773 - val_loss: 0.7264 - val_acc: 0.7636
Epoch 22/25
45000/45000 [==============================] - 360s - loss: 0.6380 - acc: 0.7805 - val_loss: 0.6889 - val_acc: 0.7684
Epoch 23/25
45000/45000 [==============================] - 362s - loss: 0.6306 - acc: 0.7804 - val_loss: 0.7058 - val_acc: 0.7674
Epoch 24/25
45000/45000 [==============================] - 371s - loss: 0.6254 - acc: 0.7855 - val_loss: 0.6801 - val_acc: 0.7748
Epoch 25/25
45000/45000 [==============================] - 354s - loss: 0.6231 - acc: 0.7850 - val_loss: 0.6644 - val_acc: 0.7750
Точность работы на тестовых данных: 76.29%

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

Точность работы на тестовых данных: 76.29%

Проанализируйте точность на проверочной выборке в процессе обучения. Она указывается после заголовка val_acc. Началось ли переобучение нейронной сети?

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

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

  1. Скорость обучения. Оценим влияние параметра скорости обучения в методе стохастического градиентного спуска на качество обучения нейронной сети. Скорость обучения задается в параметре lr при создании экземпляра оптимизатора, работающего по методу стохастического градиентного спуска (Stochastic Gradient Descent, SGD):

     sgd = SGD(lr=XXX, decay=1e-6, momentum=0.9, nesterov=True)
    

    Попробуйте использовать значения 0.02, 0.05 и 0.1. Оцените время обучения сети при разных значениях параметра.

    Выберите скорость обучения, при которой самая высокая точность работы сети на тестовых данных.

  2. Количество эпох обучения. Оценим влияние количества эпох обучения на качество обучения сети. Количество эпох задается в константе nb_epoch:

     nb_epoch = 25
    

    Попробуйте обучать сеть в течение 15, 20 и 30. эпох. Выберите количество эпох, при котором самая высокая точность работы сети на тестовых данных.

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

     # Третий сверточный слой
     model.add(Conv2D(64, (3, 3), padding='same', activation='relu'))
     # Четвертый сверточный слой
     model.add(Conv2D(64, (3, 3), activation='relu'))
     # Второй слой подвыборки
     model.add(MaxPooling2D(pool_size=(2, 2)))
     # Слой регуляризации Dropout
     model.add(Dropout(0.25))
    

    Запустите программу и оцените точность работы сети на тестовых данных, а также время обучения сети.

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

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

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

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