Хотите проверить, как хорошо вы научились распознавать рукописные цифры из набора данных MNIST? Попробуйте свои силы в соревнованиях на сайте kaggle.com!

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

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

Исходные данные

Формат исходных данных в соревнованиях Kaggle отличается от стандартного формата MNIST Яна Лекуна. На Kaggle изображения MNIST представлены в виде обычных текстовых файлов, которые можно скачать на закладке Data страницы соревнования. Данные включают два файла:

  • train.csv - данные для обучения.
  • test.csv - данные для предсказания.

Файл train.csv содержит описание изображений MNIST в текстовом формате по одному изображению в каждой строке файла. Формат файла следующий: в первой позиции цифра, которая представлена на изображении, а затем через запятую 784 кода интенсивности пикселей изображения в оттенках серого (размер изображения 28х28). Вот пример одной строки файла:

6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,181,84,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,166,254,177,...

На первом месте правильный ответ - цифра 6. Затем через запятую коды пикселов (для сокращения показаны не все 784). Таких строк в файле 42 тысячи.

Файл test.csv имеет такой же формат, но в нем нет правильного ответа в первой позиции. Наша программа должна сама определить, какая цифра на изображении. Так как правильные ответы заранее не известны, то проверить качество обучения сети самостоятельно мы не можем. Для этого необходимо загрузить файл с нашими предсказаниями цифр для каждого изображения на страницу соревнования на Kaggle, где правильность распознавания проверится автоматически. За сутки можно загрузить не более 5 вариантов решений. Загружать нужно текстовый csv-файл в следующем формате:

ImageId,Label
1,2
2,0
3,9
4,9
5,3

В первом столбце указывается номер изображения, а во втором - цифра на нем, которую мы распознали.

В файле test.csv 28 тыс. изображений. Всего в двух файлах 70 тыс. изображений, как в оригинальном наборе данных MNIST.

Загрузка данных для обучения

Загрузить данные в формате csv с помощью встроенных средств работы с набором данных MNIST в Keras нельзя. Поэтому будем читать данные из csv-файла с помощью библиотеки numpy, функции loadtxt:

train_dataset = np.loadtxt('train.csv', skiprows=1, dtype='int', delimiter=',')

С помощью этой команды мы загружаем текстовый файл, в котором в качестве разделителя используется запятая (delimiter=','), используем целый тип данных (dtype='int') и пропускаем первую строку, которая содержит заголовок с описанием столбцов файла (skiprows=1). В результате этой команды создается массив numpy, в котором 785 столбцов (первый столбец - цифра на картинке, затем 784 пиксела изображения) и 42 тысячи строк.

Из набора данных выделяем данные о картинках и меняем формат из плоского вектора 784 пикселя на двумерную матрицу 28х28:

# Выделяем данные для обучения
x_train = train_dataset[:, 1:]
# Переформатируем данные в 2D, бэкенд TensorFLow
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
input_shape = (28, 28, 1)
# Нормализуем данные
x_train = x_train.astype("float32")
x_train /= 255.0

Выделяем правильные ответы (метки классов) и преобразуем их в представление по категориям:

# Выделяем правильные ответы
y_train = train_dataset[:, 0]
# Преобразуем правильные ответы в категоризированное представление
y_train = np_utils.to_categorical(y_train)

Создание и обучение нейронной сети

Для распознавания рукописных цифр для Kaggle мы будем использовать точно такую же сверточную нейронную сеть, какую применяли для обычного набора данных MNIST:

# Создаем последовательную модель
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, verbose=2)

Как мы видим, программа не изменилась. Keras для обучения глубокой нейронной сети использует массивы numpy. В каком виде данных хранятся в исходных файлах: текстовом или бинарном, не имеет значения.

Загрузка данных для предсказания

Изображения из файла train.csv, цифры на которых нам нужно распознать, загрузим также с помощью функции loadtxt библиотеки numpy:

#Загружаем данные для предсказания
test_dataset = np.loadtxt('test.csv', skiprows=1, delimiter=",")
# Переформатируем данные в 2D, бэкенд TensorFLow
x_test = test_dataset.reshape(test_dataset.shape[0], 28, 28, 1)
x_test /= 255.0

Распознавание рукописных цифр

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

# Распознаем цифры на изображениях
predictions = model.predict(x_test)
# Преобразуем предсказания из категориального представления в метки классов
predictions = np.argmax(predictions, axis=1)

Обратите внимание, что во второй версии Keras убрали функцию np_utils.categorical_probas_to_classes, поэтому для преобразования из категорий в метки классов приходится использовать функцию argmax библиотеки numpy, которая возвращает индекс максимального элемента в списке.

Записываем результаты распознавания в текстовый файл

Цифры на изображениях мы распознали, теперь нам необходимо записать их в текстовый файл, который мы будем загружать на Kaggle для проверки правильности работы:

out = np.column_stack((range(1, predictions.shape[0]+1), predictions))
np.savetxt('submission.csv', out, header="ImageId,Label", 
            comments="", fmt="%d,%d")

На первом шаге мы используем функцию column_stack библиотеки numpy для подготовки данных в нужном формате: номер изображения и цифра на нем. Затем записываем полученные результаты в файл c помощью функции savetxt.

Полученный файл можно загрузить на страницу “Submit Predictions” соревнований Kaggle и проверить, насколько хорошо ваша программа распознает рукописные цифры по сравнению с другими пользователями.

Полный текст программы есть в репозитории на github.

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

Расскажите в комментариях, какое место в соревновании по распознаванию MNIST на Kaggle вам удалось занять? Какие изменения вы внесли в программу, чтобы этого достичь?

Полезные ссылки

  1. Соревнования по распознаванию рукописных цифр MNIST на Kaggle.
  2. Учебный курс “Программирование глубоких нейронных сетей на Python”.
  3. Инструкция по установке Keras и TensorFlow.
  4. Полносвязная нейронная сеть для распознавания рукописных цифр MNIST.
  5. Сверточная нейронная сеть для распознавания рукописных цифр MNIST.
  6. Репозиторий с примерами программ из статьи.