В курсе “Программирование глубоких нейронных сетей на Python” для распознавания рукописных цифр из набора данных MNIST мы использовали полносвязную сеть (слой типа Dense в Keras). Сеть получалась простая, обучалась быстро и работала с достаточно высокой точностью (96.34% в базовой версии программы).

Однако для распознавания рукописных цифр можно также использовать и сверточную нейронную сеть, которую мы применяли для набора данных CIFAR-10. Обучать такую сеть придется дольше, но качество распознавания будет выше.

Архитектура сверточной нейронной сети для распознавания рукописных цифр

Мы будем использовать сверточную нейронную сеть, которая состоит из 6 слоев:

  1. Слой свертки, 75 карт признаков, размер ядра свертки: 5х5.
  2. Слой подвыборки, размер пула 2х2.
  3. Слой свертки, 100 карт признаков, размер ядра свертки 5х5.
  4. Слой подвыборки, размер пула 2х2.
  5. Полносвязный слой, 500 нейронов.
  6. Полносвязный выходной слой, 10 нейронов, которые соответствуют классам рукописных цифр от 0 до 9.

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

Программа на Keras

Программа, которая реализует сверточную нейронную сеть из 6 уровней:

import numpy
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.utils import np_utils

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

# Размер изображения
img_rows, img_cols = 28, 28

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

# Преобразование размерности изображений
X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)

# Нормализация данных
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(Conv2D(75, kernel_size=(5, 5),
                 activation='relu',
                 input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Conv2D(100, (5, 5), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(500, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

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

print(model.summary())

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

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

Программу также можно найти в репозитории примеров для курса.

Программа рассчитана на бэкенд Theano. Если вы используете Tensorflow, то необходимо поменять формат хранения изображений:

# Преобразование размерности изображений
X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)

В качестве функции активации все слои, кроме последнего, используют ReLU. Последний слой выдает значения вероятности появления каждой цифры, поэтому используется функция активации Softmax (эта функция обеспечивает, что сумма выходных значений всех нейронов слоя равна единице).

Для сокращения переобучения в сеть добавлены слои регуляризации Dropout.

Результаты работы программы

После 10 эпох обучения получена следующая точность:

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

Это на 3% больше, чем результат 96.34%, полученный полносвязной сетью.

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

Полученные результаты можно еще улучшить. В сверточной нейронной сети гораздо больше гиперпараметров, которые можно менять, по сравнению с полносвязной. Попробуйте поменять следующее:

  1. Архитектуру сети: количество слоев свертки и подвыборки, а также полносвязных слоев. Какие слои оказывают большее влияние на качество распознавания?
  2. Описание слоев сети: количество карт признаков, размеры сверточных узлов, размеры пула подвыборки, количество нейронов в полносвязном слое.
  3. Количество эпох обучения.
  4. Тип оптимизатора.
  5. Размер мини-выборки.

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