На этом шаге мы рассмотрим построение простой нейронной сети.
Рассмотрим конкретный пример нейронной сети, которая обучается классификации рукописных цифр и создана с помощью библиотеки Keras для Python. Если у вас нет опыта использования Keras или других подобных библиотек, возможно, вы не все здесь поймете. Но пусть вас это не пугает. В дальнейшем мы рассмотрим и подробно объясним каждый элемент в примере.
Перед нами стоит задача: реализовать классификацию черно-белых изображений рукописных цифр (28 * 28 пикселей) по десяти категориям (от 0 до 9). Мы будем использовать набор данных MNIST, популярный в сообществе исследователей глубокого обучения, который существует практически столько же, сколько сама область машинного обучения, и широко используется для обучения. Этот набор содержит 60 000 обучающих изображений и 10 000 контрольных изображений, собранных Национальным институтом стандартов и технологий США (National Institute of Standards and Technology - часть NIST в аббревиатуре MNIST) в 1980-х годах. "Решение" задачи MNIST можно рассматривать как своеобразный аналог Hello World в глубоком обучении - часто это первое действие, которое выполняется для уверенности, что алгоритмы действуют в точности как ожидалось. По мере углубления в практику машинного обучения вы увидите, что MNIST часто упоминается в научных статьях, блогах и т. д. Некоторые образцы изображений из набора MNIST можно видеть на рисунке 1.

Рис.1. Образцы изображений MNIST
В машинном обучении категория в задаче классификации называется классом. Элементы исходных данных именуются образцами.
Класс, связанный с конкретным образцом, называется меткой.
Не пытайтесь сразу же воспроизвести пример на своем компьютере. Чтобы его опробовать, нужно сначала установить библиотеку Keras - а это будет рассмотрено позже.
Набор данных MNIST уже входит в состав Keras в форме набора из четырех массивов NumPy.
from tensorflow.keras.datasets import mnist (train_images, train_labels), (test_images, test_labels) = mnist.load_data()
Здесь train_images и train_labels - это обучающий набор, то есть данные, на которых модель обучается. После обучения модель будет проверяться тестовым (или контрольным) набором, test_images и test_labels.
Изображения хранятся в массивах NumPy, а метки - в массиве цифр от 0 до 9. Изображения и метки находятся в прямом соответствии, один к одному.
Рассмотрим обучающие данные:
>>> train_images.shape(60000, 28, 28) >>> from tensorflow import keras >>> len(train_labels) 60000 >>> train_labels [5 0 4 ... 5 6 8]
И контрольные данные:
>>> test_images.shape(10000, 28, 28) >>> len(test_labels) 10000 >>> test_labels [7 2 1 ... 4 5 6]
Вот как мы будем действовать дальше: сначала передадим нейронной сети обучающие данные, train_images и train_labels. Сеть обучится подбирать правильные метки для изображений. А затем мы предложим ей классифицировать изображения в test_images и проверим точность классификации по меткам из test_labels.
from tensorflow import keras from tensorflow.keras import layers model = keras.Sequential([ layers.Dense(512, activation="relu"), layers.Dense(10, activation="softmax") ])
Основным строительным блоком нейронных сетей является слой. Слой можно рассматривать как фильтр для данных: он принимает их и выводит в некоторой более полезной форме. В частности, слои извлекают представления из входных данных, которые, как мы надеемся, будут иметь больше смысла для решаемой задачи. Фактически методика глубокого обучения заключается в объединении простых слоев, реализующих некоторую форму поэтапной очистки данных.
Модель глубокого обучения можно сравнить с ситом, состоящим из последовательности фильтров - слоев - все более тонкой работы с данными.
В нашем случае сеть состоит из последовательности двух слоев Dense, которые являются тесно связанными (их еще называют полносвязными) нейронными слоями. Второй (и последний) слой - это десятипеременный слой классификации softmax, возвращающий массив с десятью оценками вероятностей (в сумме дающих 1). Каждая оценка определяет вероятность принадлежности текущего изображения к одному из десяти классов цифр.
Чтобы подготовить модель к обучению, нужно настроить еще три параметра для этапа компиляции:
Назначение функции потерь и оптимизатора мы проясним в последующих шагах.
model.compile(optimizer="rmsprop", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
Перед обучением мы выполним предварительную обработку данных, преобразовав в форму, которую ожидает получить нейронная сеть, и масштабируем их так, чтобы все значения оказались в интервале [0, 1]. Исходные данные - обучающие изображения - хранятся в трехмерном массиве (60000, 28, 28) типа uint8, значениями в котором являются числа в интервале [0, 255]. Мы преобразуем его в массив (60000, 28 * 28) типа float32 со значениями в интервале [0, 1].
train_images = train_images.reshape((60000, 28 * 28)) train_images = train_images.astype('float32') / 255 test_images = test_images.reshape((10000, 28 * 28)) test_images = test_images.astype('float32') / 255
Теперь можно начинать обучение сети, для чего в случае библиотеки Keras достаточно вызвать метод fit() модели - он попытается адаптировать (fit) модель под обучающие данные.
>>> model.fit(train_images, train_labels, epochs=5, batch_size=128)
Epoch 1/5
60000/60000 [==============================] - 9s - loss: 0.2524 - acc: 0.9273
Epoch 2/5
51328/60000 [========================> ] - ETA: 1s - loss: 0.1035 - acc: 0.9692
В процессе обучения отображаются две величины: потери сети на обучающих данных и точность сети на обучающих данных. Мы быстро достигли точности 0,989 (98,9 %).
Теперь у нас есть обученная модель, которую можно использовать для прогнозирования вероятностей принадлежности новых цифр к классам - изображений, которые не входили в обучающую выборку, как те из контрольного набора.
>>> test_digits = test_images[0:10]
>>> predictions = model.predict(test_digits)
>>> predictions[0]
array([1.0726176e-10, 1.6918376e-10, 6.1314843e-08, 8.4106023e-06,
2.9967067e-11, 3.0331331e-09, 8.3651971e-14, 9.9999106e-01,
2.6657624e-08, 3.8127661e-07], dtype=float32)
Каждое число в элементе массива с индексом i соответствует вероятности принадлежности изображения цифры test_digits[0] к классу i.
Наивысшая оценка вероятности (0,99999106 - почти 1) для этого тестового изображения цифры находится в элементе с индексом 7, то есть согласно нашей модели - перед нами изображение цифры 7:
>>> predictions[0].argmax() 7 >>> predictions[0][7] 0.99999106
Прогноз можно проверить по массиву меток:
>>> test_labels[0]
7
В целом, насколько хорошо справляется наша модель с классификацией прежде не встречавшихся ей цифр? Давайте проверим, вычислив среднюю точность по всему контрольному набору изображений.
>>> test_loss, test_acc = model.evaluate(test_images, test_labels) >>> print(f"test_acc: {test_acc}") test acc: 0.9785
Точность на контрольном наборе составила 97,8% - немного меньше, чем на обучающем (98,9%) . Эта разница демонстрирует пример переобучения (overfitting), когда модели машинного обучения показывают точность на новом наборе данных худшую, чем на обучающем.
На этом мы завершаем наш первый пример - вы только что увидели, как создать и обучить нейронную сеть классификации рукописных цифр, написав меньше 15 строк кода на Python. Далее мы подробнее остановимся на всех встретившихся здесь деталях. Вы узнаете о тензорах, объектах хранения данных в сети; об операциях с тензорами, выполняемых слоями; о градиентном спуске, позволяющем сети совершенствоваться на учебных примерах.
На следующем шаге мы рассмотрим подготовку данных для нейронных сетей.