Keras — это простая в использовании, но мощная библиотека глубокого обучения для Python. В этом посте мы построим простую сверточную нейронную сеть (CNN) и обучим ее решению реальной задачи с помощью Keras.
Этот пост предназначен для начинающих пользователей Keras, но предполагает наличие базовых знаний о CNN.
Нужен только код? Полный исходный код находится в конце.
Друзья, подписывайтесь на наш телеграм канал Pythonist. Там еще больше туториалов, задач и книг по Python.
Проблема: классификация цифр в MNIST
Мы будем решать классическую вводную задачу компьютерного зрения: классификация рукописных цифр по MNIST. Все просто: необходимо классифицировать изображение как цифру.
Каждое изображение в наборе данных MNIST имеет размер 28×28 и содержит центрированную полутоновую цифру. Наша CNN принимает изображение и выдает один из 10 возможных классов (по одному на каждую цифру).
1. Настройка
Я предполагаю, что у вас уже есть базовая установка Python (скорее всего, так и есть). Давайте сначала загрузим некоторые пакеты, которые нам понадобятся:
$ pip install tensorflow numpy mnist
Примечание. Нам не нужно устанавливать пакет keras, поскольку теперь он поставляется вместе с TensorFlow в качестве официального API высокого уровня! Теперь рекомендуется использовать Keras из TensorFlow, а не отдельный пакет keras.
Теперь вы можете импортировать эти пакеты и работать с набором данных MNIST:
import numpy as np import mnist from tensorflow import keras # The first time you run this might be a bit slow, since the # mnist package has to download and cache the data. train_images = mnist.train_images() train_labels = mnist.train_labels() print(train_images.shape) # (60000, 28, 28) print(train_labels.shape) # (60000,)
2. Подготовка данных
Перед началом работы мы нормализуем значения пикселей изображения с [0, 255] до [-0,5, 0,5], чтобы облегчить обучение нашей сети (использование меньших, центрированных значений обычно приводит к лучшим результатам). Мы также изменим форму каждого изображения с (28, 28)
на (28, 28, 1), поскольку Keras требует третьего
измерения.
import numpy as np import mnist train_images = mnist.train_images() train_labels = mnist.train_labels() test_images = mnist.test_images() test_labels = mnist.test_labels() # Normalize the images. train_images = (train_images / 255) - 0.5 test_images = (test_images / 255) - 0.5 # Reshape the images. train_images = np.expand_dims(train_images, axis=3) test_images = np.expand_dims(test_images, axis=3) print(train_images.shape) # (60000, 28, 28, 1) print(test_images.shape) # (10000, 28, 28, 1)
Мы готовы приступить к созданию нашей CNN!
3. Построение модели
Любая модель Keras строится либо с помощью класса Sequential, который представляет собой линейный стек слоев, либо с помощью функционального класса Model, который является более настраиваемым. Мы будем использовать более простую модель Sequential
, поскольку наша CNN будет представлять собой линейный стек слоев.
Начнем с инициализации модели Sequential
:
from tensorflow.keras.models import Sequential # WIP model = Sequential([ # layers... ])
Конструктор Sequential
принимает массив Layers Keras. Для нашей CNN мы будем использовать 3 типа слоев: Convolutional, Max Pooling и Softmax.
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten num_filters = 8 filter_size = 3 pool_size = 2 model = Sequential([ Conv2D(num_filters, filter_size, input_shape=(28, 28, 1)), MaxPooling2D(pool_size=pool_size), Flatten(), Dense(10, activation='softmax'), ])
num_filters
,filter_size
иpool_size
— это необъяснимые переменные, задающие гиперпараметры для нашей CNN.- Первый слой в любой
Sequential
модели должен задавать форму входаinput_shape
, поэтому мы делаем это наConv2D
. После задания формы входа Keras автоматически определяет формы входов для последующих слоев. - Выходной слой Softmax имеет 10 узлов, по одному на каждый класс.
4. Компиляция модели
Прежде чем приступить к обучению, необходимо настроить процесс обучения. На этапе компиляции мы решаем 3 ключевых фактора:
- Оптимизатор. Мы остановимся на довольно хорошем оптимизаторе по умолчанию: градиентном оптимизаторе Adam. В Keras есть множество других оптимизаторов, которые также можно рассмотреть.
- Функция потерь. Поскольку мы используем выходной слой Softmax, мы будем использовать потери от перекрестной энтропии. Keras различает
binary_crossentropy
(2 класса) иcategorical_crossentropy
(>2 классов), поэтому мы будем использовать последнюю. См. все потери Keras. - Список метрик. Поскольку это задача классификации, мы просто попросим Keras вывести метрику точности.
Вот как выглядит эта компиляция:
model.compile( 'adam', loss='categorical_crossentropy', metrics=['accuracy'], )
Вперед!
5. Обучение модели
Обучение модели в Keras буквально сводится к вызову функции fit()
и указанию некоторых параметров. Возможных параметров очень много, но мы ограничимся только этими:
- Данные для обучения (изображения и метки), известные как X и Y соответственно.
- Количество эпох (итераций по всему набору данных) для обучения.
- Данные проверки (или тестовые данные), которые используются в процессе обучения для периодического измерения производительности сети на данных, с которыми она ранее не сталкивалась.
Есть один нюанс: Keras ожидает, что обучающие цели будут 10-мерными векторами, поскольку в нашем выходном слое Softmax 10 узлов. Сейчас наши массивы train_labels
и test_labels
содержат одиночные целые числа, представляющие класс для каждого изображения:
import mnist train_labels = mnist.train_labels() print(train_labels[0]) # 5
Удобно, что в Keras есть метод-утилита, который решает именно эту проблему: to_categorical. Он превращает наш массив целых чисел класса в массив однокатегорийных векторов. Например, 2
превратится в [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
(он индексирован нулями).
Вот как это выглядит:
from tensorflow.keras.utils import to_categorical model.fit( train_images, to_categorical(train_labels), epochs=3, validation_data=(test_images, to_categorical(test_labels)), )
Теперь мы можем собрать все вместе, чтобы обучить нашу сеть:
import numpy as np import mnist from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten from tensorflow.keras.utils import to_categorical train_images = mnist.train_images() train_labels = mnist.train_labels() test_images = mnist.test_images() test_labels = mnist.test_labels() # Normalize the images. train_images = (train_images / 255) - 0.5 test_images = (test_images / 255) - 0.5 # Reshape the images. train_images = np.expand_dims(train_images, axis=3) test_images = np.expand_dims(test_images, axis=3) num_filters = 8 filter_size = 3 pool_size = 2 # Build the model. model = Sequential([ Conv2D(num_filters, filter_size, input_shape=(28, 28, 1)), MaxPooling2D(pool_size=pool_size), Flatten(), Dense(10, activation='softmax'), ]) # Compile the model. model.compile( 'adam', loss='categorical_crossentropy', metrics=['accuracy'], ) # Train the model. model.fit( train_images, to_categorical(train_labels), epochs=3, validation_data=(test_images, to_categorical(test_labels)), )
Выполнение этого кода на полном наборе данных MNIST дает следующие результаты:
Epoch 1 loss: 0.2433 - acc: 0.9276 - val_loss: 0.1176 - val_acc: 0.9634 Epoch 2 loss: 0.1184 - acc: 0.9648 - val_loss: 0.0936 - val_acc: 0.9721 Epoch 3 loss: 0.0930 - acc: 0.9721 - val_loss: 0.0778 - val_acc: 0.9744
С помощью этой простой CNN мы достигаем 97,4% точности тестирования!
6. Использование модели
Теперь, когда у нас есть рабочая, обученная модель, давайте применим ее на практике. Прежде всего, мы сохраним ее на диске, чтобы в любой момент можно было загрузить ее обратно:
model.save_weights('cnn.h5')
Теперь мы можем в любой момент перезагрузить обученную модель, перестроив ее и загрузив сохраненные веса:
from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten num_filters = 8 filter_size = 3 pool_size = 2 # Build the model. model = Sequential([ Conv2D(num_filters, filter_size, input_shape=(28, 28, 1)), MaxPooling2D(pool_size=pool_size), Flatten(), Dense(10, activation='softmax'), ]) # Load the model's saved weights. model.load_weights('cnn.h5')
Использовать обученную модель для прогнозирования очень просто: мы передаем массив входных данных в функцию predict()
, а она возвращает массив выходных данных. Следует помнить, что на выходе нашей сети 10 вероятностей (из-за softmax), поэтому мы будем использовать np.argmax(), чтобы превратить их в реальные цифры.
# Predict on the first 5 test images. predictions = model.predict(test_images[:5]) # Print our model's predictions. print(np.argmax(predictions, axis=1)) # [7, 2, 1, 0, 4] # Check our predictions against the ground truths. print(test_labels[:5]) # [7, 2, 1, 0, 4]
7. Расширения
Мы можем еще много чего сделать для экспериментов и улучшения нашей сети — в этом официальном примере Keras MNIST CNN достигла точности 99 тестов после 15 эпох. Давайте рассмотрим, какие модификации можно внести в нашу CNN.
Глубина сети
Что произойдет, если мы добавим или удалим конволюционные слои? Как это повлияет на обучение и/или конечную производительность модели?
model = Sequential([ Conv2D(num_filters, filter_size, input_shape=(28, 28, 1)), Conv2D(num_filters, filter_size), MaxPooling2D(pool_size=pool_size), Flatten(), Dense(10, activation='softmax'), ])
Dropout
А что если попробовать добавить слои Dropout, которые обычно используются для предотвращения перебора?
from tensorflow.keras.layers import Dropout model = Sequential([ Conv2D(num_filters, filter_size, input_shape=(28, 28, 1)), MaxPooling2D(pool_size=pool_size), Dropout(0.5), Flatten(), Dense(10, activation='softmax'), ])
Полносвязные слои
Что если добавить полностью связанные слои между конволюционными выходами и конечным слоем Softmax? Это обычно делается в CNN, используемых для компьютерного зрения.
from tensorflow.keras.layers import Dense model = Sequential([ Conv2D(num_filters, filter_size, input_shape=(28, 28, 1)), MaxPooling2D(pool_size=pool_size), Flatten(), Dense(64, activation='relu'), Dense(10, activation='softmax'), ])
Параметры конволюции
А что если поиграть с параметрами Conv2D? Например:
# These can be changed, too! num_filters = 8 filter_size = 3 model = Sequential([ # See https://keras.io/layers/convolutional/#conv2d for more info. Conv2D( num_filters, filter_size, input_shape=(28, 28, 1), strides=2, padding='same', activation='relu', ), MaxPooling2D(pool_size=pool_size), Flatten(), Dense(10, activation='softmax'), ])
Заключение
Вы реализовали свою первую CNN с помощью Keras! Мы достигли точности тестирования 97,4% с нашей простой начальной сетью. Ниже я снова приведу полный исходный код для ознакомления.
Если вам интересна работа с Keras, вы можете ознакомиться с официальным руководством по началу работы с Keras.
Спасибо за чтение! Полный исходный код приведен ниже.
Полный код
# The full CNN code! #################### import numpy as np import mnist from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten from tensorflow.keras.utils import to_categorical train_images = mnist.train_images() train_labels = mnist.train_labels() test_images = mnist.test_images() test_labels = mnist.test_labels() # Normalize the images. train_images = (train_images / 255) - 0.5 test_images = (test_images / 255) - 0.5 # Reshape the images. train_images = np.expand_dims(train_images, axis=3) test_images = np.expand_dims(test_images, axis=3) num_filters = 8 filter_size = 3 pool_size = 2 # Build the model. model = Sequential([ Conv2D(num_filters, filter_size, input_shape=(28, 28, 1)), MaxPooling2D(pool_size=pool_size), Flatten(), Dense(10, activation='softmax'), ]) # Compile the model. model.compile( 'adam', loss='categorical_crossentropy', metrics=['accuracy'], ) # Train the model. model.fit( train_images, to_categorical(train_labels), epochs=3, validation_data=(test_images, to_categorical(test_labels)), ) # Save the model to disk. model.save_weights('cnn.h5') # Load the model from disk later using: # model.load_weights('cnn.h5') # Predict on the first 5 test images. predictions = model.predict(test_images[:5]) # Print our model's predictions. print(np.argmax(predictions, axis=1)) # [7, 2, 1, 0, 4] # Check our predictions against the ground truths. print(test_labels[:5]) # [7, 2, 1, 0, 4]
Перевод статьи «Keras for Beginners: Implementing a Convolutional Neural Network».