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

Цель работы: научиться применять предварительно обученные нейронные сети для решения задач классификации изображений.

Предварительные сведения

Перед выполнением работы рекомендуется посмотреть два видео. Первое видео о подготовке набора данных с фотографиями собак и кошек для обучения нейронной сети:

Второе видео о технологии переноса обучения:

Необходимое программное обеспечение

Используется библиотека Keras, а также TensorFLow в качестве вычислительного бэкенда.

Инструкция по установке Keras и TensorFlow с дистрибутивом Anaconda.

Подготовка данных

Перед началом выполнения работы скачайте набор данных с фотографиями собак и кошек с сайта Kaggle. Вам нужен файл train.zip.

Разделите фотографии собак и кошек на три части: набор данных для обучения (train), проверки (validation) и тестирования (test). Для обучения можно использовать 70% фотографий, а для проверки и тестирования по 15%. В каждом наборе данных скопируйте фотографии собак в каталог dogs, а фотографии котов – в каталог cats. У вас должна получится такая структура каталогов:

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

Подробная инструкция с примером скрипта, который выполняет распределение фото по каталогам автоматически, есть в статье “Как подготовить набор изображений для обучения нейронной сети в Keras”.

Базовая версия программы

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

from tensorflow.python.keras.preprocessing.image import ImageDataGenerator
from tensorflow.python.keras.models import Sequential
from tensorflow.python.keras.layers import Activation, Dropout, Flatten, Dense
from tensorflow.python.keras.applications import VGG16
from tensorflow.python.keras.optimizers import Adam

# Каталог с данными для обучения
train_dir = 'train'
# Каталог с данными для проверки
val_dir = 'val'
# Каталог с данными для тестирования
test_dir = 'test'
# Размеры изображения
img_width, img_height = 150, 150
# Размерность тензора на основе изображения для входных данных в нейронную сеть
# backend Tensorflow, channels_last
input_shape = (img_width, img_height, 3)
# Размер мини-выборки
batch_size = 64
# Количество изображений для обучения
nb_train_samples = 17500
# Количество изображений для проверки
nb_validation_samples = 3750
# Количество изображений для тестирования
nb_test_samples = 3750

# Загружаем предварительно обученную нейронную сеть VGG16
vgg16_net = VGG16(weights='imagenet', include_top=False, 
                  input_shape=(150, 150, 3))

# "Замораживаем" веса предварительно обученной нейронной сети VGG16
vgg16_net.trainable = False

# Создаем составную нейронную сеть на основе VGG16
# Создаем последовательную модель Keras
model = Sequential()
# Добавляем в модель сеть VGG16 вместо слоя
model.add(vgg16_net)
# Добавляем в модель новый классификатор
model.add(Flatten())
model.add(Dense(256))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(1))
model.add(Activation('sigmoid'))

# Компилируем составную нейронную сеть
model.compile(loss='binary_crossentropy',
              optimizer=Adam(lr=1e-5), 
              metrics=['accuracy'])

# Создаем генератор изображений на основе класса ImageDataGenerator. 
# Генератор делит значения всех пикселов изображения на 255.
datagen = ImageDataGenerator(rescale=1. / 255)

# Генератор данных для обучения на основе изображений из каталога
train_generator = datagen.flow_from_directory(
    train_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode='binary')

# Генератор данных для проверки на основе изображений из каталога
val_generator = datagen.flow_from_directory(
    val_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode='binary')

# Генератор данных для тестирования на основе изображений из каталога
test_generator = datagen.flow_from_directory(
    test_dir,
    target_size=(img_width, img_height),
    batch_size=batch_size,
    class_mode='binary')

# Обучаем модель с использованием генераторов
model.fit_generator(
    train_generator,
    steps_per_epoch=nb_train_samples // batch_size,
    epochs=10,
    validation_data=val_generator,
    validation_steps=nb_validation_samples // batch_size)

# Оцениваем качество работы сети с помощью генератора
scores = model.evaluate_generator(test_generator, nb_test_samples // batch_size)
print("Аккуратность на тестовых данных: %.2f%%" % (scores[1]*100))

Также базовую версию программы можно найти в репозитории примеров курса.

Для начала запустите базовую версию программы:

python cats_dogs.py

Примерный вывод программы:

Using TensorFlow backend.
Found 17500 images belonging to 2 classes.
Found 3750 images belonging to 2 classes.
Found 3750 images belonging to 2 classes.
Epoch 1/10
273/273 [==============================] - 462s - loss: 0.2524 - acc: 0.8857 - val_loss: 0.1329 - val_acc: 0.9472
Epoch 2/10
273/273 [==============================] - 458s - loss: 0.0975 - acc: 0.9630 - val_loss: 0.1237 - val_acc: 0.9512
Epoch 3/10
273/273 [==============================] - 454s - loss: 0.0606 - acc: 0.9762 - val_loss: 0.0804 - val_acc: 0.9710
Epoch 4/10
273/273 [==============================] - 454s - loss: 0.0446 - acc: 0.9840 - val_loss: 0.0791 - val_acc: 0.9696
Epoch 5/10
273/273 [==============================] - 454s - loss: 0.0254 - acc: 0.9915 - val_loss: 0.0976 - val_acc: 0.9699
Epoch 6/10
273/273 [==============================] - 453s - loss: 0.0169 - acc: 0.9942 - val_loss: 0.0883 - val_acc: 0.9693
Epoch 7/10
273/273 [==============================] - 451s - loss: 0.0125 - acc: 0.9960 - val_loss: 0.0702 - val_acc: 0.9740
Epoch 8/10
273/273 [==============================] - 452s - loss: 0.0146 - acc: 0.9951 - val_loss: 0.1078 - val_acc: 0.9704
Epoch 9/10
273/273 [==============================] - 450s - loss: 0.0049 - acc: 0.9986 - val_loss: 0.1971 - val_acc: 0.9563
Epoch 10/10
273/273 [==============================] - 451s - loss: 0.0054 - acc: 0.9983 - val_loss: 0.1101 - val_acc: 0.9756
Аккуратность на тестовых данных: 97.31%

Запишите точность работы сети после обучения на тестовых данных. Она приводится в последней строке вывода:

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

Проанализируйте точность на проверочной выборке в процессе обучения. Она указывается после заголовка val_acc. Началось ли переобучение нейронной сети?

Экспериментируем с гиперпараметрами обучения

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

  1. Изменение параметров классификатора. Попробуйте изменить параметры нового классификатора, который мы добавили к модели VGG16:

     # Добавляем в модель новый классификатор
     model.add(Flatten())
     model.add(Dense(256))
     model.add(Activation('relu'))
     model.add(Dropout(0.5))
     model.add(Dense(1))
     model.add(Activation('sigmoid'))
    

    Измените количество нейронов в первом полносвязном (Dense) слое, попробуйте добавить еще один полносвязный слой в классификатор (или несколько полносвязных слоев) с разным количеством нейронов. Оцените, как это влияет на качество распознавания.

    Оцените роль слоя регуляризации Dropout. Попробуйте убрать его и сети и посмотреть, что произойдет. Попробуйте изменить значение параметра слоя Dropout с 0.5 на другое и посмотреть, как это влияет на качество распознавания.

  2. Другие предварительно обученные сети. Попробуйте вместо VGG16 использовать другую предварительно обученную нейронную сеть. Список доступных сетей есть на странице Keras Applications. Например, для использования сети Google Inceptionv3:

     # Подключаем модуль с сетью Inceptionv3
     from tensorflow.python.keras.applications import InceptionV3
     ...
     # Загружаем предварительно обученную нейронную сеть Inceptionv3
     inception_net = InceptionV3(weights='imagenet', include_top=False, 
                                 input_shape=(150, 150, 3))
     # "Замораживаем" веса предварительно обученной нейронной сети Inceptionv3
     inception_net.trainable = False
     ...
     # Создаем составную нейронную сеть на основе Inceptionv3
     # Создаем последовательную модель Keras
     model = Sequential()
     # Добавляем в модель сеть Inceptionv3 вместо слоя
     model.add(inception_net)
     ...
    

    Оцените качество работы сети на основе Inceptionv3 и сравните с качеством сети VGG16. Попробуйте также использовать сети ResNet50 и InceptionResNetV2. Если есть желание, то исследуйте качество работы других предварительно обученных нейронных сетей, входящих в Keras.

    С сетями Inceptionv3, ResNet50 и другими, более новыми, вы можете в классификаторе вместо слоя Flatten использовать слой GlobalAveragePooling2D. Оцените, как это влияет на качество обучения.

  3. Время обучения сети. Сети Inceptionv3 и ResNet50 обеспечивают лучшее качество распознавания, но содержат гораздо больше слоев, чем сеть VGG16. У ResNet50, как понятно из названия, 50 слоев, у Inceptionv3 - 159 слоев. Сеть InceptionResNetV2 содержит 578 слоев. Чем большое слоев в сети, тем больше времени требуется для ее дообучения. Постарайтесь подобрать компромиссный вариант предварительно обученной нейронной сети с учетом точности распознавания и времени обучения.

Выбираем лучшие гиперпараметры

Создайте сеть с лучшими значениями всех гиперпараметров обучения, которые вы определили на предыдущем шаге. Учитывайте не только точность работы, но и время обучения сети. Что можно сделать, чтобы еще больше увеличить точность?

Расскажите о своих результатах

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