Обучи свою первую нейросеть: простая классификация

Смотрите на TensorFlow.org Запустите в Google Colab Изучайте код на GitHub Скачайте ноутбук

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

Руководство использует tf.keras, высокоуровневый API для построения и обучения моделей в TensorFlow.

from __future__ import absolute_import, division, print_function, unicode_literals

# TensorFlow и tf.keras
import tensorflow as tf
from tensorflow import keras

# Вспомогательные библиотеки
import numpy as np
import matplotlib.pyplot as plt

print(tf.__version__)
2.0.0

Загружаем датасет Fashion MNIST

Это руководство использует датасет Fashion MNIST который содержит 70,000 монохромных изображений в 10 категориях. На каждом изображении содержится по одному предмету одежды в низком разрешении (28 на 28 пикселей):

Fashion MNIST sprite
Figure 1. Образцы Fashion-MNIST (Zalando, лицензия MIT).
 

Fashion MNIST предназначен для замены классического датасета MNIST который часто используют как "Hello, World" программ машинного обучения для компьютерного зрения. Датасет MNIST содержит изображения рукописных цифр (0, 1, 2, и т.д.) в формате идентичном формату изображений одежды которыми мы будем пользоваться здесь.

Это руководство для разнообразия использует Fashion MNIST, и еще потому, что это проблема немного сложнее чем обычный MNIST. Оба датасета относительно малы, и используются для проверки корректности работы алгоритма. Это хорошие отправные точки для тестирования и отладки кода.

Мы используем 60,000 изображений для обучения нейросети и 10,000 изображений чтобы проверить, насколько правильно сеть обучилась их классифицировать. Вы можете получить доступ к Fashion MNIST прямо из TensorFlow. Импортируйте и загрузите данные Fashion MNIST прямо из TensorFlow:

fashion_mnist = keras.datasets.fashion_mnist

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-labels-idx1-ubyte.gz
32768/29515 [=================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/train-images-idx3-ubyte.gz
26427392/26421880 [==============================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-labels-idx1-ubyte.gz
8192/5148 [===============================================] - 0s 0us/step
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/t10k-images-idx3-ubyte.gz
4423680/4422102 [==============================] - 0s 0us/step

Загрузка датасета возвращает четыре массива NumPy:

  • Массивы train_images и train_labels являются тренировочным сетом — данными, на которых модель будет обучаться.
  • Модель тестируется на проверочном сете, а именно массивах test_images и test_labels.

Изображения являются 28х28 массивами NumPy, где значение пикселей варьируется от 0 до 255. Метки (labels) - это массив целых чисел от 0 до 9. Они соответствуют классам одежды изображенной на картинках:

Label Class
0 T-shirt/top
1 Trouser
2 Pullover
3 Dress
4 Coat
5 Sandal
6 Shirt
7 Sneaker
8 Bag
9 Ankle boot

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

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

Изучите данные

Давайте посмотрим на формат данных перед обучением модели. Воспользовавшись shape мы видим, что в тренировочном датасете 60,000 изображений, каждое размером 28 x 28 пикселей:

train_images.shape
(60000, 28, 28)

Соответственно, в тренировочном сете 60,000 меток:

len(train_labels)
60000

Каждая метка это целое число от 0 до 9:

train_labels
array([9, 0, 0, ..., 3, 0, 5], dtype=uint8)

Проверочный сет содержит 10,000 изображений, каждое - также 28 на 28 пикселей:

test_images.shape
(10000, 28, 28)

И в проверочном сете - ровно 10,000 меток:

len(test_labels)
10000

Предобработайте данные

Данные должны быть предобработаны перед обучением нейросети. Если вы посмотрите на первое изображение в тренировочном сете вы увидите, что значения пикселей находятся в диапазоне от 0 до 255:

plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.grid(False)
plt.show()

png

Мы масштабируем эти значения к диапазону от 0 до 1 перед тем как скормить их нейросети. Для этого мы поделим значения на 255. Важно, чтобы тренировочный сет и проверочный сет были предобработаны одинаково:

train_images = train_images / 255.0

test_images = test_images / 255.0

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

plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    plt.xlabel(class_names[train_labels[i]])
plt.show()

png

Постройте модель

Построение модели нейронной сети требует правильной конфигурации каждого слоя, и последующей компиляции модели.

Настройте слои

Базовым строительным блоком нейронной сети является слой. Слои извлекают образы из данных, которые в них подаются. Надеемся, что эти образы имеют смысл для решаемой задачи.

Большая часть глубокого обучения состоит из соединения в последовательность простых слоев. Большинство слоев, таких как tf.keras.layers.Dense, имеют параметры, которые настраиваются во время обучения.

model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(10, activation='softmax')
])

Первый слой этой сети - tf.keras.layers.Flatten, преобразует формат изображения из двумерного массива (28 на 28 пикселей) в одномерный (размерностью 28 * 28 = 784 пикселя). Слой извлекает строки пикселей из изображения и выстраивает их в один ряд. Этот слой не имеет параметров для обучения; он только переформатирует данные.

После разложения пикселей, нейросеть содержит два слоя tf.keras.layers.Dense. Это полносвязные нейронные слои. Первый Dense слой состоит из 128 узлов (или нейронов). Второй (и последний) 10-узловой softmax слой возвращает массив из 10 вероятностных оценок дающих в сумме 1. Каждый узел содержит оценку указывающую вероятность принадлежности изображения к одному из 10 классов.

Скомпилируйте модель

Прежде чем модель будет готова для обучения, нам нужно указать еще несколько параметров. Они добавляются на шаге compile модели:

  • Функция потерь (Loss function) — измеряет точность модели во время обучения. Мы хотим минимизировать эту функцию чтоб "направить" модель в верном направлении.
  • Оптимизатор (Optimizer) — показывает каким образом обновляется модель на основе входных данных и функции потерь.
  • Метрики (Metrics) — используются для мониторинга тренировки и тестирования модели. Наш пример использует метрику accuracy равную доле правильно классифицированных изображений.
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

Обучите модель

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

  1. Подайте тренировочный данные в модель. В этом примере тренировочные данные это массивы train_images и train_labels.
  2. Модель учится ассоциировать изображения с правильными классами.
  3. Мы просим модель сделать прогнозы для проверочных данных, в этом примере массив test_images. Мы проверяем, соответствуют ли предсказанные классы меткам из массива test_labels.

Для начала обучения, вызовите метод model.fit, который называется так, поскольку "тренирует (fits)" модель на тренировочных данных:

model.fit(train_images, train_labels, epochs=10)
Train on 60000 samples
Epoch 1/10
60000/60000 [==============================] - 4s 72us/sample - loss: 0.4963 - accuracy: 0.8261
Epoch 2/10
60000/60000 [==============================] - 4s 60us/sample - loss: 0.3736 - accuracy: 0.8647
Epoch 3/10
60000/60000 [==============================] - 4s 60us/sample - loss: 0.3347 - accuracy: 0.8789
Epoch 4/10
60000/60000 [==============================] - 4s 60us/sample - loss: 0.3144 - accuracy: 0.8843
Epoch 5/10
60000/60000 [==============================] - 4s 62us/sample - loss: 0.2962 - accuracy: 0.8903
Epoch 6/10
60000/60000 [==============================] - 4s 61us/sample - loss: 0.2829 - accuracy: 0.8957
Epoch 7/10
60000/60000 [==============================] - 4s 60us/sample - loss: 0.2679 - accuracy: 0.9009
Epoch 8/10
60000/60000 [==============================] - 4s 60us/sample - loss: 0.2585 - accuracy: 0.9029
Epoch 9/10
60000/60000 [==============================] - 4s 60us/sample - loss: 0.2493 - accuracy: 0.9066
Epoch 10/10
60000/60000 [==============================] - 4s 60us/sample - loss: 0.2428 - accuracy: 0.9098

<tensorflow.python.keras.callbacks.History at 0x7f4f741a8198>

В процессе обучения модели отображаются метрики потери (loss) и точности (accuracy). Эта модель достигает на тренировочных данных точности равной приблизительно 0.88 (88%).

Оцените точность

Далее, сравните какую точность модель покажет на проверчном датасете:

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)

print('\nТочность на проверочных данных:', test_acc)
10000/1 - 1s - loss: 0.2610 - accuracy: 0.8787

Точность на проверочных данных: 0.8787

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

Сделайте предсказания

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

predictions = model.predict(test_images)

Здесь полученная модель предсказала класс одежды для каждого изображения в проверочном датасете. Давайте посмотрим на первое предсказание:

predictions[0]
array([3.9092092e-06, 5.0955748e-08, 1.0088089e-08, 1.7653681e-09,
       1.0856661e-08, 2.4640898e-04, 3.9162201e-06, 1.8451020e-02,
       1.2714973e-05, 9.8128188e-01], dtype=float32)

Прогноз представляет из себя массив из 10 чисел. Они описывают "уверенность" (confidence) модели в том, насколько изображение соответствует каждому из 10 разных видов одежды. Мы можем посмотреть какой метке соответствует максимальное значение:

np.argmax(predictions[0])
9

Модель полагает, что на первой картинке изображен ботинок (ankle boot), или class_names[9]. Проверка показывает, что классификация верна:

test_labels[0]
9

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

def plot_image(i, predictions_array, true_label, img):
  predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])

  plt.imshow(img, cmap=plt.cm.binary)

  predicted_label = np.argmax(predictions_array)
  if predicted_label == true_label:
    color = 'blue'
  else:
    color = 'red'

  plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
                                100*np.max(predictions_array),
                                class_names[true_label]),
                                color=color)

def plot_value_array(i, predictions_array, true_label):
  predictions_array, true_label = predictions_array[i], true_label[i]
  plt.grid(False)
  plt.xticks([])
  plt.yticks([])
  thisplot = plt.bar(range(10), predictions_array, color="#777777")
  plt.ylim([0, 1])
  predicted_label = np.argmax(predictions_array)

  thisplot[predicted_label].set_color('red')
  thisplot[true_label].set_color('blue')

Давайте посмотрим на нулевое изображение, предсказание и массив предсказаний.

i = 0
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions, test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions,  test_labels)
plt.show()

png

i = 12
plt.figure(figsize=(6,3))
plt.subplot(1,2,1)
plot_image(i, predictions, test_labels, test_images)
plt.subplot(1,2,2)
plot_value_array(i, predictions,  test_labels)
plt.show()

png

Давайте посмотрим несколько изображений с их прогнозами. Цвет верных предсказаний синий, а неверных - красный. Число это процент уверенности (от 100) для предсказанной метки. Отметим, что модель может ошибаться даже если она очень уверена.

# Отображаем первые X тестовых изображений, их предсказанную и настоящую метки.
# Корректные предсказания окрашиваем в синий цвет, ошибочные в красный.
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
  plt.subplot(num_rows, 2*num_cols, 2*i+1)
  plot_image(i, predictions, test_labels, test_images)
  plt.subplot(num_rows, 2*num_cols, 2*i+2)
  plot_value_array(i, predictions, test_labels)
plt.show()

png

Наконец, используем обученную модель для предсказания класса на одном изображении.

# Берем одну картинку из проверочного сета.
img = test_images[0]

print(img.shape)
(28, 28)

Модели tf.keras оптимизированы для предсказаний на пакетах (batch) данных, или на множестве примеров сразу. Таким образом, даже если мы используем всего 1 картинку, нам все равно необходимо добавить ее в список:

# Добавляем изображение в пакет данных, состоящий только из одного элемента.
img = (np.expand_dims(img,0))

print(img.shape)
(1, 28, 28)

Сейчас предскажем правильную метку для изображения:

predictions_single = model.predict(img)

print(predictions_single)
[[3.9092124e-06 5.0955748e-08 1.0088108e-08 1.7653681e-09 1.0856661e-08
  2.4640947e-04 3.9162242e-06 1.8451029e-02 1.2714986e-05 9.8128188e-01]]
plot_value_array(0, predictions_single, test_labels)
_ = plt.xticks(range(10), class_names, rotation=45)

png

Метод model.predict возвращает нам список списков, по одному для каждой картинки в пакете данных. Получите прогнозы для нашего (единственного) изображения в пакете:

np.argmax(predictions_single[0])
9

И, как и ранее, модель предсказывает класс 9.

#@title MIT License
#
# Copyright (c) 2017 François Chollet
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.