Категории: PythonНейронные сети

Сверточная нейронная сеть для распознавания цифр

Добрый день, уважаемые читатели. Целью этой статьи является классификация изображений с помощью сверточных слоёв Keras. Всё это дело мы будем тестировать на стандартном наборе — MNIST. Этот набор содержит 70.000 изображений рукописных цифр (от 0 до 9-ти). Наша цель проста — сделать так, чтобы сверточная нейронная сеть правильно предсказала изображенную цифру.

Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.

А на моем YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!

Свёрточный слой нейросети

Сверточная нейронная сеть использует довольно интересный математический приём. Ядро — по факту матрица, содержащие веса, «исследует» исходное изображение путём выделения под-матрицы своего размера и выделения определённых признаков. Устройство данного алгоритма очень громоздко, потому рекомендую ознакомиться с русскоязычными статьями других авторов на эту тему:

Самая простая сверточная нейронная сеть

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

In [1]:
# MNIST dataset of handwritten digits
from keras.datasets import mnist

# Model
from keras.models import Sequential
from keras.layers import Dense, Conv2D, Flatten, MaxPooling2D, Dropout
from keras.optimizers import RMSprop

# Preprocessing
from keras.utils import to_categorical

# Visualization
%matplotlib inline
import matplotlib.pyplot as plt

Мы загрузим MNIST прямо из наборов Keras, довольно удобно. Там же импортируем класс последовательность модели (Sequential) и необходимые слои: Dense (обычный персептрон), Flatten (преобразователь матриц в вектор), Dropout (один из методов регуляризации для противостояния переобучению), Conv2D (свёрточный слой) и MaxPooling2D — уменьшает размер исходной матрицы.

Загрузим данные и оценим их кол-во:

In [2]:
(X_train, y_train), (X_test, y_test) = mnist.load_data()
X_train.shape, X_test.shape

Downloading data from https://s3.amazonaws.com/img-datasets/mnist.npz
11493376/11490434 [==============================] - 0s 0us/step

Out[2]:
((60000, 28, 28), (10000, 28, 28))

60.000 обучающих и 10.000 тестовых образцов, каждый из которых имеет форму (28, 28).

Позже мы преобразуем исходные массивы таким образом, как этого требует Keras. Однако сейчас взглянем, какие же изображения нам даны. Для этого выведем на экран первые девять картинок, используя matplotlib.

In [3]: 
fig, axes = plt.subplots(nrows=3, ncols=3, figsize=(20, 10))
axes = axes.flat

for i in range(9):
    axes[i].imshow(X_train[i])
    axes[i].axis('off')

Добавляем сетку из 9 изображений, изменив размер фигуры для отображения. Теперь с помощью цикла изобразим 9 образцов, выключив оси для каждого объекта Axes.

Результат:

Теперь преобразуем наборы образцов, а также обработаем вектора с целевыми значениями:

In [4]:
X_train = X_train.reshape((60000,28, 28, 1)).astype('float32') / 255
X_test = X_test.reshape((10000, 28, 28, 1)).astype('float32') / 255

y_train = to_categorical(y_train, 10)
y_test = to_categorical(y_test, 10)

Мы изменяем тип массива на float32, чтобы нормализировать его путём деления на 255. А «цели» мы преобразуем в категории от 0 до 9.

Теперь задумаемся над архитектурой модели:

In[5]:
def get_model():
    model = Sequential()
    
    model.add(Conv2D(64, 
                     kernel_size=(3, 3), 
                     activation='relu', 
                     input_shape=(28, 28, 1)))
    
    model.add(Conv2D(128, 
                     (3,3), 
                     activation='relu'))
    
    model.add(MaxPooling2D(pool_size=(2, 2)))
    model.add(Dropout(0.4))
    model.add(Flatten())
    
    model.add(Dense(256, 
                    activation='relu'))
    
    model.add(Dropout(0.4))
    
    model.add(Dense(10, 
                    activation='softmax'))
    
    model.compile(optimizer=RMSprop(lr=0.01),
                  loss='categorical_crossentropy',
                  metrics=['accuracy'])
    
    return model

Как она будет работать? Сначала у нас 2 свёрточных слоя, которые будут работать с поступающим им изображением. Потом мы уменьшим размер исходной матрицы с помощью слоя MaxPooling. Затем изменим несколько значений на ноль, развернём многомерный массив в вектор, проведём его через один линейный слой, заново откинем несколько значений и произведём классификацию с помощью функции активации softmax. В остальных слоях используя самая популярная активация — relu.

Создадим отчёт об архитектуре модели (именно для этого мы указывали форму ввода для первого слоя):

In [6]:
model = get_model()

model.summary()

________________________________________________________________
Layer (type)                 Output Shape              Param #   
================================================================
conv2d_1 (Conv2D)            (None, 26, 26, 64)        640       
________________________________________________________________
conv2d_2 (Conv2D)            (None, 24, 24, 128)       73856     
________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 12, 12, 128)       0         
________________________________________________________________
dropout_1 (Dropout)          (None, 12, 12, 128)       0         
________________________________________________________________
flatten_1 (Flatten)          (None, 18432)             0         
________________________________________________________________
dense_1 (Dense)              (None, 256)               4718848   
________________________________________________________________
dropout_2 (Dropout)          (None, 256)               0         
________________________________________________________________
dense_2 (Dense)              (None, 10)                2570      
================================================================
Total params: 4,795,914
Trainable params: 4,795,914
Non-trainable params: 0
________________________________________________________________

Обратите внимания на форму вывода каждого слоя (в частности для третьего и пятого слоёв).

Т. к. наши данные подготовлены, то нам остается обучить саму модель.

In [7]:
history = model.fit(X_train,
                    y_train,
                    epochs=15,
                    batch_size=500,
                    validation_data=(X_test, y_test))

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

Мы не даром сохранили историю обучения, стандартным образом визуализируем её:

In [8]:
x = range(15)

plt.grid(True)

plt.plot(x, 
         history.history['acc'], 
         'bo-', 
         label='Train accuracy')

plt.plot(x, 
         history.history['val_acc'],
         'ro-',
         label='Validation accuracy')

plt.xlabel('Epoch')
plt.ylabel('Accuracy')

plt.legend(loc='lower right')

Out[8]:
In [9]:
plt.grid(True)

plt.plot(x, 
         history.history['loss'], 
         'bo-', 
         label='Train losses')

plt.plot(x, 
         history.history['val_loss'],
         'ro-',
         label='Validation losses')

plt.xlabel('Epoch')
plt.ylabel('Loss')

plt.legend(loc='upper right')

Out[9]:

Если чтение отчёта вызывает у Вас трудности, то лучше вывести отдельно конечные точность и ошибку на тестовых данных:

In [10]:
score = model.evaluate(X_test, y_test)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

10000/10000 [==============================] - 1s 75us/step
Test loss: 0.03508325981858243
Test accuracy: 0.9909

Мы достигли точности в 0.9909. В официальном примере от Keras была достигнута точность в 0.9925. Могу сказать, что при многократной адаптации модели она показывала и лучшие результаты. Так я однажды получил точность в 0.9927. Это всё из-за того, что изначальные веса модели задаются случайно, что влияет на градиент обучения.

Заключение

Сегодня мы рассмотрели самую простую свёрточную сеть (можно сказать, даже на «Hello, world» в плане обработки изображений с помощью Keras), которая показала просто отличный результат на, казалось бы, довольно трудном наборе данных. Это в очередной раз доказывает, что появление cnn начало новую эпоху в сфере компьютерного зрения.

Оставляю ссылку на .ipynb документ, который вы сможете просмотреть и изменить на своё усмотрение.

Также рекомендую прочитать статью Анализ настроения с помощью Keras и Python. Подпишитесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для разработчиков.

shwan @shwanoff

Программист .NET

Disqus Comments Loading...

Свежие публикации

Принципы SOLID C#

Принципы SOLID C# представляют собой набор утверждений, которые описывают архитектуру программных продуктов. То есть, следуя им можно разработать стабильно работающее…

5 дней тому назад

Функциональный API библиотеки Keras

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

1 неделя тому назад

Сеточный поиск лучшей архитектуры нейронной сети с помощью Keras и Sklearn

Добрый день, уважаемые читатели. Темой нашей сегодняшней статьи станет объединение библиотек Keras и Sklearn. Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного…

3 недели тому назад

Использование конвейера в sklearn

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

1 месяц тому назад

Английский язык для программиста

Английский язык считается языком международного общения, но еще больше он распространён в IT сфере, где его знание является одним из…

3 месяца тому назад

Анализ настроения с помощью Keras и Python

Добрый день, уважаемые читатели. Сегодня мы реализуем анализ настроения, а именно определим, является ли комментарий на русском языке "токсичным". Мы…

4 месяца тому назад