BC/NW 2019№ 2 (35):5.2

РАЗРАБОТКА ПРОГРАММНОЙ РЕАЛИЗАЦИИ АЛГОРИТМА ДЛЯ РАСПОЗНАВАНИЯ РУКОПИСНЫХ ЦИФР

Якушенкова Ю.Е.

1. Анализ задания и постановка задачи

Требуется разработать программную реализацию алгоритма для распознавания рукописных цифр. Реализация данного алгоритма должна происходить с помощью построения нейронной сети.

Для обучения и тестирования предоставлена база данных MNIST, из которой будет взято 42 000 образов для обучения и 28 000 образов для тестирования. Каждый образ представлен картинкой 28х28 пикселей с 256 градациями серого цвета.

Пример нескольких неоднозначных в идентификации цифр представлен на рисунке 1.

image

Рисунок 1 - Пример неоднозначных в идентификации цифр

MNIST — объёмная база данных образцов рукописного написания цифр. База данных является стандартом, предложенным Национальным институтом стандартов и технологий США с целью калибрации и сопоставления методов распознавания изображений с помощью машинного обучения в первую очередь на основе нейронных сетей. Данные состоят из заранее подготовленных примеров изображений, на основе которых проводится обучение и тестирование систем.

База данных MNIST содержит 60 000 изображений для обучения и 10 000 изображений для тестирования. Таким образом из 60 000 изображений 28 000 будет использовано для обучений, а остальные 18 000 и 10 000 из другой выборки, для тестирования. Так как по содержанию они эквивалентны.

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

·        Keras – это библиотека, позволяющая на более высоком уровне работать с нейросетями. Она упрощает множество задач, используется в быстрых экспериментах и сильно уменьшает количество однообразного кода. В качестве бекендной библиотеки для вычислений keras может использовать theano и tenzorfow;

·        Jupyter Notebook – это веб-приложение с открытым исходным кодом, которое позволяет создавать документы, которые содержат код, уравнения, визуализацию и текст описания;

·        Python – высокоуровневый язык программирования общего назначения, ориентированный на повышение производительности разработчика и читаемости кода;

·        NumPy – библиотека являющейся инструментом более низкого уровня;

Для ускорения обучения использовалось Nvidia GPU MX150, для работы с которой были установлена соответствующая программа CUDA. CUDA является программно-аппаратная архитектура параллельных вычислений, которая позволяет существенно увеличить вычислительную производительность благодаря использованию графических процессоров фирмы Nvidia. Для обучения нейронной сети с использованием CUDA была установлена библиотека cuDNN, которая содержит оптимизированные для GPU реализации сверточных и рекуррентных сетей, различных функций активации, алгоритма обратного распространения ошибки и т.п.

2. Разработка алгоритма решения задачи

В качестве архитектуры нейронной сети была выбрана свёрточная нейронная сеть (convolutional neural network, CNN).

Свёрточные нейронные сети представляют собой особый класс нейронных сетей, в которых посредством ограничений на веса нейронов попеременно реализуются операции свёртки (convolution) и подразбиения (subsampling). Пример разницы между обычной нейронной сети и сверточной сети представлен на Рисунке 2 и Рисунке 3.

Рисунок 1. - Обычная нейронная сеть

Рисунок 2 - Простая нейронная сеть

Картинки по запросу convolutional neural network depth

Рисунок 3 - Простая сверточная сеть

Предлагается конструкция нейронной сети из 4 слоёв: входной, при­нимающий изображения 28x28 пикселей со значениями от -1 до 1, выходной из 10 элементов и 2 скрытых слоя.

Алгоритм состоит из трех подзадач:

·       выборка данных, которые будут использоваться для обучения и тестирования;

·       обучение нейронной сети;

·       тестирование нейронной сети.

Для данной сети был выбран метод обучения основанный на методе обучения с учителем (на маркированных данных) — метод обратного распространения ошибки.

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

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

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

При разработке программы были определены такие понятия:

Batch – количество обучающих образцов, обрабатываемых одновременно за одну итерацию алгоритма градиентного спуска;

epochs – количество итераций обучающего алгоритма по всему обучающему множеству;

hidden_size – количество нейронов в каждом из двух скрытых слоев MLP.

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

После обучения нейронной сети происходит ее тестирования, результаты предсказаний записываются в соответствующий файл.

Тестовая и обучающая выборки представляют собой файл с расширением .cvs, где значений записаны в таблицу. С помощью соответствующих библиотек, на которых написана функция show_images можно построить каждую картинку, пример которой представлен на Рисунке 4.

Рисунок 4 - Пример обучающей и тестовой выборки

Алгоритм работы программы, обучающей нейронную сеть представлен на Рисунке 5.

Рисунок 5 - Алгоритм работы программы

Алгоритм работы дополнительной программы, тестирующей обученную нейронную сеть и определяющей оценку обучения нейронной сети представлен на Рисунке 6.

Рисунок 6 - Алгоритм работы дополнительной программы

 

3. Разработка программной реализации алгоритма

Разработка программной реализации алгоритма велась на языке Python с использованием открытой программной библиотеки для машинного обучения Keras.

Основная программа представлена в листинге 1. В Листинге 2 представлен вид данных, составляющих обучающую и тестовую выборки. Листинг 3 представляет собой дополнительную программу, где тестируется уже обученная нейронная сеть и вычисляется точность предсказаний.

Листинг 1. Основная программа

#!/usr/bin/env python
# coding: utf-8

# In[1]:

import numpy as np
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

import sys
import os.path

# In[2]:

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

TRAIN_FILE = "./data/train.csv"
TEST_FILE = "./data/test.csv"
OUTPUT_FILE = "./data/submission.csv"

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

if not os.path.isfile(TRAIN_FILE) or not os.path.isfile(TEST_FILE):
    print("""
Загрузите с kaggle данные для обучения и тестирования
             (
файлы train.csv и test.csv) и запишите их в текущий каталог
          """)
    sys.exit()

# In[3]:

#
Загружаем данные для обучения
train_dataset = np.loadtxt(TRAIN_FILE, skiprows=1, dtype='int', delimiter=",")
#
Выделяем данные для обучения
x_train = train_dataset[:, 1:]
#
Переформатируем данные в 2D, бэкенд Tensorflow
x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1)
input_shape = (img_rows, img_cols, 1)
#
Нормализуем данные
x_train = x_train.astype("float32")
x_train /= 255.0
#
Выделяем правильные ответы
y_train = train_dataset[:, 0]
#
Преобразуем правильные ответы в категоризированное представление
y_train = np_utils.to_categorical(y_train)

# In[4]:

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

# In[5]:

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

# In[6]:

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

print(model.summary())

# In[7]:

#
Обучаем сеть
history = model.fit(x_train, y_train, validation_split=0.3, batch_size=50, epochs=10, verbose=2)

# In[15]:

# Making predictions
predictions = model.predict(x_test)
# Converting from categorical to classed
predictions = np.argmax(predictions, axis=1)

# Saving predictions to the file
out = np.column_stack((range(1, predictions.shape[0]+1), predictions))
np.savetxt(OUTPUT_FILE, out, header="ImageId,Label", comments="", fmt="%d,%d")

# In[16]:

import matplotlib.pyplot as plt
print(history.history.keys())
accuracy = history.history['acc']
val_accuracy = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(accuracy))
plt.plot(epochs, accuracy, 'bo', label='Training accuracy')
plt.plot(epochs, val_accuracy, 'b', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.legend()
plt.show()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()

# In[17]:

#
Сохраняем сеть
model_json = model.to_json()
with open("./model/mnist_model.json", "w") as json_file:
    json_file.write(model_json)

# In[18]:

import pprint
import json

# In[19]:

with open("./model/mnist_model.json", "r") as json_file:
    pprint.pprint(json.loads(json_file.read()))

# In[20]:

model.save_weights("./model/mnist_model.h5")

        

 

 

 

Листинг 2. Вывод вида выборок

# In[15]:

import pandas as pd
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

trainer = pd.read_csv("./train.csv")
test = pd.read_csv("./test.csv")

# In[16]:

trainer.head(), test.head()


# In[17]:

train_features, train_labels = trainer.drop("label", axis=1).values.astype('float32'), trainer["label"].values.astype('int32')
train_features_images = train_features.reshape(train_features.shape[0], 28, 28)
train_labels = trainer["label"].values.astype('int32')

test_features = test.values.astype('float32')
test_features_images = test_features.reshape(test_features.shape[0], 28, 28)



def show_images(features_images, labels, length):
    start = 42
    for i in range(start, start+length):
        plt.subplot(330 + (i+1))
        plt.imshow(features_images[i], cmap=plt.get_cmap('gray'))
        plt.title(labels[i])
    plt.show()
 
       
show_images(train_features_images, train_labels, 5)
show_images(test_features_images, np.zeros(test_features_images.shape[0]), 5)

 

 

Листинг 3. Дополнительная программа

from keras.datasets import mnist

from keras.utils import np_utils

from keras.models import model_from_json

 

print("Загружаю сеть из файлов")

# Загружаем данные об архитектуре сети

json_file = open("./model/mnist_model.json", "r")

loaded_model_json = json_file.read()

json_file.close()

# Создаем модель

loaded_model = model_from_json(loaded_model_json)

# Загружаем сохраненные веса в модель

loaded_model.load_weights("./model/mnist_model.h5")

print("Загрузка сети завершена")

 

 

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

(_, _), (X_test, y_test) = mnist.load_data()

 

# Размер изображения

img_rows, img_cols = 28, 28

 

 

# Преобразование размерности изображений

X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)

#X_test = X_test.reshape(img_rows, img_cols, 1)

input_shape = (img_rows, img_cols, 1)

 

 

# Нормализация данных

X_test = X_test.astype('float32')

X_test /= 255

 

# Преобразуем метки в категории

Y_test = np_utils.to_categorical(y_test, 10)

 

 

# Компилируем загруженную модель

loaded_model.compile(loss="categorical_crossentropy", optimizer="SGD", metrics=["accuracy"])

# Оцениваем качество обучения сети загруженной сети на тестовых данных

scores = loaded_model.evaluate(X_test, Y_test, verbose=0)

print("Точность работы загруженной сети на тестовых данных: %.2f%%" % ((scores[1]*100)))

 

 

 

4. Результаты тестирования

Нейросеть была обучена на выборке из 42 000 изображений рукописный цифр и протестирована на выборке из 28 000 изображений. Тестирование проводилось на ноутбуке со следующими характеристиками:

·       CPU: intel Сore i7-8550U CPU @1.80GHZ;

·       ОЗУ: 8 ГБ;

·       ОС: Windows 10 x64;

Версии установленного ПО:

·       Python 3.6

·       CUDA 9.0

·       cUDNN 7.4.1.5

·       Keras 2.2.4

Результаты тестирования выводятся в Jupyter Notebook и сохраняются в файл submission.csv.

Результат обучения нейронной сети представлен на Рисунке 7.

Рисунок 7а -  Представление точности верных ответов и представление не предсказанных данных

 

 

Рисунок 7б -  Представление не предсказанных данных

 

Результаты тестирования сети приведены на Рисунке 8.

Рисунок 8 - Результаты предсказания на основе тестовой выборки

Результат вывода дополнительной программы представлены на рисунке 9.

Рисунок 9 - Результаты работы дополнительной программы

Заключение

В результате работы были построены сверточные нейронные сети с использованием библиотеки Keras. Были использованы способы построения сети, способы обучения и тестирования.

В результате тестирования данной нейронной сети был получен процент распознавания тестовой выборки равный 96,55%, что является удовлетворительным результатом.

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

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

 Литература

1.     Л. Шапиро и Дж. Стокман. «Компьютерное зрение». В: М.: Бином. Ла­боратория знаний 752 с., (2006).

2.      Т. Рашид. «Создаем нейронную сеть». Диалектика 274 с., (2017).

3.      М. В. Сысоева, И. В. Сысоев. «Программирование для «нормальных» с нуля на языке Python». Базальт СПО 180 с., (2018).