Распознавание предметов одежды на Keras

Практика по оптимизации качества работы нейросети
В этой практической работе по курсу "Программирование нейросетей на Python" вы научитесь обучать нейронную сеть распознавать предметы одежды из набора данных Fashin MNIST.

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

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

Перед выполнением работы рекомендуется посмотреть видео с объяснением, как работает программа распознавания предметов одежды из набора данных Fashion MNIST.
Еще одно видео - как оценивать качество обучения нейронной сети.

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

Используется библиотека TensorFlow, интерфейс для программирования нейросетей Keras.

Для выполнения практического задания рекомендуется воспользоваться бесплатной облачной платформой для машинного обучения Google Colaboratory, ссылка на ноутбук с базовой версией программы. Но также можно установить Keras и TensorFlow с дистрибутивом Anaconda на свой компьютер и запускать задание у себя.

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

Базовая версия программы, которая реализует обучение нейронной сети для распознавания предметов одежды.
from tensorflow.keras.datasets import fashion_mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from tensorflow.keras import utils
import numpy as np

# Загружаем данные
(x_train, y_train), (x_test, y_test) = fashion_mnist.load_data()

# Список с названиями классов
classes = ['футболка', 'брюки', 'свитер', 'платье', 'пальто', 
           'туфли', 'рубашка', 'кроссовки', 'сумка', 'ботинки']

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

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

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

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

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

print(model.summary())

# Обучаем сеть
history = model.fit(x_train, y_train, 
                    batch_size=200, 
                    epochs=100,
                    validation_split=0.2,
                    verbose=1)

# Оцениваем качество обучения сети на тестовых данных
scores = model.evaluate(x_test, y_test, verbose=1)
print("Доля верных ответов на тестовых данных, в процентах:", round(scores[1] * 100, 4))
Если вы используете Google Colab, то можете сразу же запускать ноутбук с базовой версией программы. Также базовую версию программы можно найти в репозитории примеров курса. Скопируйте ноутбук себе и запустите.

Запишите долю верных ответов работы сети после обучения на тестовых данных. Она выводится следующим образом:
Доля верных ответов на тестовых данных, в процентах: 86.87
Проанализируйте долю верных ответов на проверочном наборе данных в процессе обучения. Она указывается после заголовка "val_acc". Вот фрагмент вывода обучения нейронной сети (эпохи с 11 по 89 удалены для сокращения места):
Train on 48000 samples, validate on 12000 samples
Epoch 1/100
48000/48000 [==============================] - 2s 37us/sample - loss: 1.1875 - acc: 0.6657 - val_loss: 0.8279 - val_acc: 0.7548
Epoch 2/100
48000/48000 [==============================] - 1s 21us/sample - loss: 0.7540 - acc: 0.7721 - val_loss: 0.6858 - val_acc: 0.7850
Epoch 3/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.6573 - acc: 0.7947 - val_loss: 0.6244 - val_acc: 0.8001
Epoch 4/100
48000/48000 [==============================] - 1s 20us/sample - loss: 0.6061 - acc: 0.8084 - val_loss: 0.5850 - val_acc: 0.8112
Epoch 5/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.5729 - acc: 0.8158 - val_loss: 0.5609 - val_acc: 0.8153
Epoch 6/100
48000/48000 [==============================] - 1s 20us/sample - loss: 0.5491 - acc: 0.8212 - val_loss: 0.5447 - val_acc: 0.8175
Epoch 7/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.5304 - acc: 0.8255 - val_loss: 0.5242 - val_acc: 0.8238
Epoch 8/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.5159 - acc: 0.8289 - val_loss: 0.5133 - val_acc: 0.8267
Epoch 9/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.5037 - acc: 0.8328 - val_loss: 0.5026 - val_acc: 0.8287
Epoch 10/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.4933 - acc: 0.8355 - val_loss: 0.4942 - val_acc: 0.8294
...
Epoch 90/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.3249 - acc: 0.8881 - val_loss: 0.3607 - val_acc: 0.8731
Epoch 91/100
48000/48000 [==============================] - 1s 20us/sample - loss: 0.3244 - acc: 0.8871 - val_loss: 0.3592 - val_acc: 0.8745
Epoch 92/100
48000/48000 [==============================] - 1s 20us/sample - loss: 0.3228 - acc: 0.8884 - val_loss: 0.3581 - val_acc: 0.8756
Epoch 93/100
48000/48000 [==============================] - 1s 20us/sample - loss: 0.3222 - acc: 0.8890 - val_loss: 0.3612 - val_acc: 0.8743
Epoch 94/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.3210 - acc: 0.8889 - val_loss: 0.3588 - val_acc: 0.8743
Epoch 95/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.3203 - acc: 0.8888 - val_loss: 0.3577 - val_acc: 0.8742
Epoch 96/100
48000/48000 [==============================] - 1s 20us/sample - loss: 0.3192 - acc: 0.8896 - val_loss: 0.3585 - val_acc: 0.8739
Epoch 97/100
48000/48000 [==============================] - 1s 20us/sample - loss: 0.3183 - acc: 0.8899 - val_loss: 0.3566 - val_acc: 0.8764
Epoch 98/100
48000/48000 [==============================] - 1s 20us/sample - loss: 0.3175 - acc: 0.8899 - val_loss: 0.3574 - val_acc: 0.8740
Epoch 99/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.3171 - acc: 0.8906 - val_loss: 0.3569 - val_acc: 0.8758
Epoch 100/100
48000/48000 [==============================] - 1s 19us/sample - loss: 0.3160 - acc: 0.8905 - val_loss: 0.3532 - val_acc: 0.8768
В примере видно, что на начальных эпохах доля правильных ответов на проверочной выборке быстро увеличивается c 0.7548 на первой эпохе до 0.8294 на десятой эпохе. Но ближе к окончанию обучения доля правильных ответов на проверочной выборке почти не меняется. На 97-й эпохе она составляет 0.8764, затем снижается до 0.8740 (эпоха 98) и 0.8758 (эпоха 99), а после этого незначительно увеличивается до 0.8768 на сотой эпохе. Это означает, что мы близки к переобучению и обучение необходимо останавливать.

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

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

1. Количество эпох обучения. Оценим влияние количества эпох обучения на качество обучения сети. Количество эпох задается в аргументе epochs метода model.fit:
history = model.fit(x_train, y_train, 
                    batch_size=200, 
                    epochs=100,            # Количество эпох
                    validation_split=0.2,
                    verbose=1)
Попробуйте обучать сеть в течение 50, 75, 100 и 125 эпох. Выберите количество эпох, при котором самая высокая доля верных ответов нейросети на тестовых данных.

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

history = model.fit(x_train, y_train, 
                    batch_size=200,       # Размер мини-выборки
                    epochs=100,            
                    validation_split=0.2,
                    verbose=1)
Используйте размер мини-выборки 50, 100, 200 и 400. Выберите значение, при котором самая высокая доля верных ответов нейросети на тестовых данных.

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

model.add(Dense(XXX, input_dim=784, activation="relu"))
Используйте значения 500, 700, 900, 1200. Выберите значение, при котором самая высокая доля верных ответов нейросети на тестовых данных.

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

model.add(Dense(800, input_dim=784, activation="relu"))
model.add(Dense(600, activation="relu"))  # Новый скрытый слой
model.add(Dense(10, activation="softmax"))
Попробуйте добавить скрытый слой с разным количеством нейронов: 500, 700, 900 и 1200.

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

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

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