На этом шаге мы рассмотрим построение простой нейронной сети.
Рассмотрим конкретный пример нейронной сети, которая обучается классификации рукописных цифр и создана с помощью библиотеки 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. Далее мы подробнее остановимся на всех встретившихся здесь деталях. Вы узнаете о тензорах, объектах хранения данных в сети; об операциях с тензорами, выполняемых слоями; о градиентном спуске, позволяющем сети совершенствоваться на учебных примерах.
На следующем шаге мы рассмотрим подготовку данных для нейронных сетей.