Ранее в курсе “Программирование глубоких нейронных сетей на Python” мы работали только с наборами данных, встроенными в Keras. В этой статья я расскажу, как подготовить свой собственный набор изображений для обучения в Keras глубоких нейронных сетей.

Набор данных

В качестве примера мы рассмотрим набор данных с фотографиями кошек и собак с соревнования Kaggle “Cats vs Dogs”. Задача соревнования - определить, кто находится на фотографии: кот или собака. С точки зрения машинного обучения это задача бинарной классификации.

На сайте соревнования Cats vs Dogs есть два набора данных: train.zip и test1.zip. Для обучения нам нужен набор train.zip, т.к. он содержит метки с правильными ответами. Этот набор данных включает 25 тысяч фотографий, из них 12,5 тыс. фотографий котов и 12,5 тыс. фотографий собак. Вот несколько примеров:

Примеры фотографий кошек и собак с соревнования Kaggle

Скачайте файл train.zip на свой компьютер и распакуйте его.

Метки классов указаны в именах файлов: все фотографии с котами начинаются на cat, а все фотографии с собаками - на dog.

Набор данных test1.zip содержит фотографии, для которых нет правильных ответов. Цель соревнования Cats vs Dogs: разметить фотографии в этом наборе и загрузить результаты разметки на сайт Kaggle. Но так как мы не будем участвовать в соревнованиях, то этот набор использовать не будем.

Подготовка набора изображений

Для загрузки фотографий в Keras мы будем использовать генераторы изображений (ImageDataGenerator). Генераторы позволяют автоматически загружать изображения с диска в память компьютера, преобразовывать их в вид, необходимый Keras, и передавать в модель для обучения. Также генераторы предоставляют возможность расширения данных (data augmentation), но пока мы не будем ее использовать.

Данные для генераторов изображений Keras нужно подготовить специальным образом. Это делается достаточно просто: создается каталог, в котором будут подкаталоги по количеству классов объектов. В нашем случае таких классов два: кот и собака. Таким образом, создаем каталог с именем cats, в который записываем фотографии котов, и dogs, в котором будут фотографии собак. Имена каталогов могут быть любыми. При обучении нейронной сети, генератор изображений Keras будет читать картинки из обоих каталогов и автоматически готовить метки с правильными ответами, например, 0 для кота и 1 для собаки.

Для обучения нейронной сети нам нужно три набора изображений:

  • Набор данных для обучения.
  • Набор данных для проверки (оценки качества обучения сети в процессе обучения).
  • Набор данных для тестирования (оценки качества обучения сети после завершения обучения).

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

cats_vs_dogs/
|----train/
|    |----cats/
|    |----dogs/
|
|----validation/
|    |----cats/
|    |----dogs/
|
|----test/
     |----cats/
     |----dogs/

У нас есть три каталога, в каталоге train содержатся данные для обучения нейронной сети, в каталоге validation - данные для проверки, и в каталоге test - данные для тестирования. В каждом из этих каталогов есть подкаталоги cats и dogs, в которых находятся фотографии котов и собак соответственно.

Можно распределить фотографии по всем каталогом вручную, но я подготовил Jupyter ноутбук, который делает это автоматически. Этот ноутбук копирует 70% изображений в каталог для обучения и по 15% в каталоги для проверки или тестирования. Вы можете воспользоваться моим Jupyter ноутбуком, написать свой, или распределить фотографии вручную.

Создание генераторов

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

На первом этапе нам нужно создать ImageDataGenerator, который в общем виде задает работу генераторов. Это делается следующим образом:

datagen = ImageDataGenerator(rescale=1. / 255)

Здесь мы указываем, что наш генератор при загрузке изображения будет выполнять операцию rescale - делить значение каждого пиксела на 255. Таким образом, все входные данные будут в диапазоне от 0 до 1, что хорошо для обучения нейронной сети.

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

train_generator = datagen.flow_from_directory(
    'train',
    target_size=(150, 150),
    batch_size=64,
    class_mode='binary')

Метод flow_from_directory объекта datagen создает генератор, который читает данные из каталога на диске. Название каталога указывается в первом аргументе - train. Второй параметр target_size - размер, к которому преобразуются загружаемые с диска изображения. В примере все изображения будут приведены к размеру 150x150 пикселей. batch_size - размер мини-выборки, количество изображений, которое генератор читает с диска за один раз и передает нейронной сети для обучения.

Параметр class_mode задает, каким образом генерируются правильные ответы для загруженных генератором изображений. Возможные варианты binary, categorical и sparse. При class_mode='binary' используется бинарная классификация - есть всего два класса, они задаются одним битом. Именно такой тип классификации мы используем чтобы распознавать котов и собак. Если классов больше, чем два, то нужно использовать режим categorical, при котором правильные ответы задаются в формате one-hot encoding. Режим sparse тоже подходит для случая, когда классов больше двух. В этом режиме генератор формирует правильные ответы в виде целых чисел - номеров классов.

Таким образом, мы создали генератор train_generator, который читает изображения из каталога train, преобразует их размер на 150х150 пикселей, правильные ответы генерируются в бинарном виде. За одно обращение генератор выдает 64 изображения с правильными метками классов.

Подобным образом создаем генератор данных для проверки:

val_generator = datagen.flow_from_directory(
    'validation',
    target_size=(150, 150),
    batch_size=64,
    class_mode='binary')

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

Генератор тестовых данных также отличается только каталогом с фотографиями:

test_generator = datagen.flow_from_directory(
    'test',
    target_size=(150, 150),
    batch_size=64,
    class_mode='binary')

Нейронная сеть для распознавания кошек и собак

Для решения нашей задачи мы будем использовать сверточную нейронную сеть, которая определяется следующим образом:

model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape=(150, 150, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))

model.add(Flatten())
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))

Сеть включает три каскада свертки и подвыборки. Размер сверточных ядер 3х3, размер подвыборки 2х2, используется выбор максимального значения (Max Pooling). Сверточная часть сети предназначена для выделения характерных признаков в изображении.

После сверточной части идет полносвязная часть нейронной сети, которая отвечает за классификацию. Для этой цели используются два полносвязных слоя. На первом слое 64 нейрона, функция активации полулинейная. Затем идет слой Dropout, который используется для уменьшения переобучения. Выходной полносвязный слой включает всего один нейрон, что соответствует задаче бинарной классификации. 0 на выходе из сети означает, что на фотографии кот, а 1 - собака. Функция активации на выходном слое сигмоидальная. Эта функция плавно меняет свое значение от 0 до 1, поэтому ее удобно использовать для бинарной классификации.

Я использую бэкенд TensorFlow, формат хранения изображений channels_last. Для Theano нужно поменять входную размерность нейронной сети на input_shape=(3, 150, 150) (количество каналов в изображении на первом месте, а не на последнем).

Компилируем нейронную сеть:

model.compile(loss='binary_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])

В качестве функции ошибки используем binary_crossentropy, т.к. у нас бинарная классификация. Если классов больше двух, то нужно использовать categorical_crossentropy. Оптимизатор Adam, метрика качества обучения - аккуратность.

Обучение нейронной сети с использованием генераторов изображений

Обучать нейронную сеть мы будем с помощью метода model.fit_generator:

# Размер мини-выборки
batch_size = 64
# Количество изображений для обучения
nb_train_samples = 17500
# Количество изображений для проверки
nb_validation_samples = 3750
# Количество изображений для тестирования
nb_test_samples = 3750

model.fit_generator(
    train_generator,
    steps_per_epoch=nb_train_samples // batch_size,
    epochs=30,
    validation_data=val_generator,
    validation_steps=nb_validation_samples // batch_size)

Методу fit_generator передаем два генератора: train_generator с данными для обучения и val_generator с данными для проверки. Обучение выполняется в течение 30 эпох.

Генераторы в Keras устроены так, что могут выдавать изображения бесконечно. После того, как изображения в каталоге закончатся, происходит переход в начало каталога, и генератор начинает работать снова. Поэтому нужно указать, сколько будет обращений к генератору на каждой эпохе обучения. Для этого используется параметры steps_per_epoch (данные для обучения) и validation_steps (данные для проверки). За одно обращение генератор выдает не одно изображение, а несколько, в соответствии с размером его мини-выборки (batch_size). Чтобы рассчитать количество обращений к генератору, при котором мы сможем получить все изображения из набора данных по одному разу, мы делим количество изображений в наборе на размер мини-выборки.

Диагностический вывод при обучении модели у метода model.fit_generator такой же, как и у model.fit. Вот пример вывода первых трех эпох обучения:

Epoch 1/30
1093/1093 [==============================] - 53s - loss: 0.6566 - acc: 0.6033 - val_loss: 0.5830 - val_acc: 0.6923
Epoch 2/30
1093/1093 [==============================] - 52s - loss: 0.5338 - acc: 0.7383 - val_loss: 0.4687 - val_acc: 0.7724
Epoch 3/30
1093/1093 [==============================] - 51s - loss: 0.4472 - acc: 0.7938 - val_loss: 0.4104 - val_acc: 0.8125

Аккуратность на данных для обучения обозначена acc, а аккуратность на проверочном наборе данных - val_acc. Видно, что при обучении аккуратность увеличивается как на обучающем, так и на проверочном наборах данных.

Проверка качества обучения сети

Оценивать качество обучения сети мы также будем с помощью генератора. Для этого у модели есть метод evaluate_generator:

scores = model.evaluate_generator(test_generator, nb_test_samples // batch_size)
print("Аккуратность на тестовых данных: %.2f%%" % (scores[1]*100))

Аккуратность на тестовых данных: 84.30%

В метод model.evaluate_generator передаем генератор test_generator с изображениями, которые нейронная сеть не видела в процессе обучения. Этот генератор также может работать бесконечно, поэтому вторым аргументом указываем, сколько раз нужно обратиться к генератору (количество изображений в тестовом наборе данных, деленное на размер мини-выборки).

После 30 эпох обучения аккуратность на тестовых данных составила 84.30%. Точность распознавания можно еще повысить, если использовать предварительно обученные нейронные сети.

Итоги

Мы научились готовить собственный набор изображений для обучения глубокой нейронной сети в Keras. Описанный метод подходит не только для распознавания собак и кошек, но и для других задач классификации изображений. Нужно просто распределить изображения разных классов по отдельным каталогам, настроить генераторы брать изображения из этих каталогов, и обучать сеть при помощи генераторов.

Полный текст программы, с подключением необходимых модулей Python и другой подготовительной работой, можно найти в Jupyter ноутбуке.

Не забудьте сохранить обученную нейронную сеть для последующего использования!

Полезные ссылки

  1. Соревнования Kaggle “Cats vs Dogs”.
  2. Jupyter ноутбук с подготовкой данных для распознавания кошек и собак.
  3. Jupyter ноутбук с обучением сверточной нейронной сети для распознавания кошек и собак на фотографиях.
  4. Keras Image Preprocessing.
  5. Учебный курс “Программирование глубоких нейронных сетей на Python”.