Как повысить точность классификатора с Transfer Learning

Продолжаем говорить о сверточных нейронных сетях (CNN). Сегодня расскажем вам об одном из методов Transfer Learning — выделение признаков (feature extraction). Читайте в этой статье: использование предварительно обученной модели (VGG16) от TensorFlow для решения задачи классификации в Python, а также передача результатов сверточной основы в собственный классификатор.

Выделение признаков как один из методов Transfer Learning

Transfer Learning — это набирающий популярность метод обучения моделей машинного обучения для задач компьютерного зрения (Computer Vision), NLP и не только. В основе обучения лежит использование предобученных нейронных сетей. Кто-то, обладая большими вычислительными мощностями, сконструировал большую архитектуру нейронной сети, обучил в рамках решения своей задачи и опубликовал её на просторах Интернета. Мы, не имея в наличии множества GPU и больших данных (Big Data), используем предобученную модель для решения своей задачи. Нам не важно, что изначально обученная модель научена распознавать образы самолётов и автомобилей, мы можем использовать её для распознавания кошек и собак.

Сверточные нейронные сети (CNN) являются универсальным инструментом для работы с изображениями. Тем не менее предобученная модель без каких-либо изменений вряд ли покажет высокие результаты при решении текущей задачи (напр., распознавание животных). Поэтому мы дополняем её своими данными и получаем перепрофилированную модель, которая решает нашу задачу.

Transfer Learning предлагает два подхода использования предварительно обученных сетей:

  • Выделение признаков (feature extraction)
  • Дообучение (fine tuning)

Мы рассмотрим feature extraction на примере простой архитектуры VGG16 с использованием Python-фреймворка TensorFlow [1]. Архитектура VGG16 обучена на данных из ImageNet, которая имеет больше миллиона изображений.

Выделение признаков для решения задачи классификации изображений

Выделение признаков заключается в использовании неизменной сверточной основы архитектуры и создании своего собственного классификатора. Как правило, в моделях CNN последний слой —  полносвязный. В feature extraction мы отказываемся от этого слоя и реализуем свой, например через TensorFlow.

При создании своего полносвязного классификатора можно пойти несколькими способами:

  1. Пропустить входные данные через сверточную основу, получить выходные данные, сохранить их, а затем передать в свой классификатор. В TensorFlow для этого метода вызывается метод predict, а затем полученный прогноз сохраняем в массив NumPy
  2. Сразу же соединить сверточную основу с нашим классификатором. Этот способ разрешает использовать расширение данных Data Augmentation, о котором говорили тут, но он более затратный

В этой статье мы рассмотрим 1-й способ.

Датасет и извлечение сверточной основы

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

Прежде всего скачаем архитектуру VGG16, которая доступна в TensorFlow. Вот так выглядит извлечение архитектуры VGG16 в Python:

from tensorflow.keras.applications import VGG16

conv_base = VGG16(weights='imagenet', # Источник весов
                  include_top=False, # Не подключать полносвязный слой
                  input_shape=(150, 150, 3)) # Форма входных тензоров

Аргумент include_top определяет включение полносвязного слоя. ImageNet имеет 1000 классов, но нам не нужно 1000 нейронов на выходе, для бинарной классификации можно обойтись одним. К тому же в основе feature extraction лежит избавление этот этого слоя.

Извлечение и сохранение признаков

Ниже Python-функция, которая извлекает признаки, выдаваемые сверточной основой архитектуры VGG16. Мы указали, что выходной признак должен иметь форму (образцы,4,4,512). Это связано с тем, что в VGG16 последний слой сверточной основы имеет именно такую форму (можно проверить вызовом model.summary) . Аргумент sample_count указывает количество передаваемых изображений.

import numpy as np
from tensorflow.keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(rescale=1./255)

def extract_features(directory, sample_count, batch_size=20):
    features = np.zeros(shape=(sample_count, 4, 4, 512))
    labels = np.zeros(shape=(sample_count))
    generator = datagen.flow_from_directory(
        directory,
        target_size=(150, 150),
        batch_size=batch_size,
        class_mode='binary')
    for i, (inputs_batch, labels_batch) in enumerate(generator):
        if i * batch_size >= sample_count:
            # Генераторы могут находится в цикле до бесконечности,
            # поэтому нужно прервать после передачи всех изображений
            break
        # Предказываем на основе сверточной основы VGG16:
        features_batch = conv_base.predict(inputs_batch)
        features[i * batch_size : (i + 1) * batch_size] = features_batch
        labels[i * batch_size : (i + 1) * batch_size] = labels_batch

    return features, labels

Для каждого набора изображений (размера набора определяется batch_size) вызывается conv_base.predict для получения результата от сверточной основы. Этот результат мы сохраняем в features. Далее, остаётся только вызвать этот метод применительно к папкам с картинками:

train_features, train_labels = extract_features(train_dir, 2000)
valid_features, valid_labels = extract_features(valid_dir, 1000)
test_features, test_labels = extract_features(test_dir, 1000)

Поскольку полученные признаки имеют форму (образцы,4,4,512), то для передачи полносвязному классификатору требуется их преобразовать в форму (образцы,8192). Пример кода на Python для изменения формы массива NumPy:

train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
valid_features = np.reshape(valid_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4 * 512))

Создание собственного классификатора в TensorFlow

Ниже пример создания в TensorFlow полносвязного классификатора, который имеет на входе 256 нейронов и 1 на выходе (кошка/не кошка). Ещё мы добавили слой Dropout, чтобы избежать переобучения (overfitting). Также используем TensorBoard для визуализации полученных результатов. Код на Python для создания классификатора:

from tensorflow.keras import (
    models, layers, optimizers)
from tensorflow.keras.callbacks import TensorBoard
from datetime import datetime

model = models.Sequential([
    layers.Dense(256, activation='relu', input_dim=4 * 4 * 512),
    layers.Dropout(0.5),
    layers.Dense(1, activation='sigmoid')
])

log_dir = "logs/fit/" + datetime.now().strftime("%Y%m%d-%H%M%S")
tensorboard_callback = TensorBoard(log_dir=log_dir, histogram_freq=1)

model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
              loss='binary_crossentropy',
              metrics=['acc'])

model.fit(train_features, train_labels,
          epochs=30,
          batch_size=20,
          validation_data=(valid_features, valid_labels),
          callbacks=[tensorboard_callback])
Графики точности и функции потерь в TensorBoard
Точность и функция потерь (синие линии — валидационная выборка)

Как видим, на 30 эпохах обучения графики TensorBoard показывают, что на валидационной выборке точность достигает 90% для задачи распознавания образов кошек и собак в рамках компьютерного зрения на Python .

 

 

В следующей статье рассмотрим 2-й способ выделения признаков. Ещё больше подробностей о Transfer Learning и использовании предварительно обученных моделей на примерах реальных задач Computer Vision на языке Python, вы узнаете на нашем специализированном курсе «VISI: Computer Vision» в лицензированном учебном центре обучения и повышения квалификации IT-специалистов в Москве.

Источники
  1. https://www.tensorflow.org/api_docs/python/tf/keras/applications

Добавить комментарий

Поиск по сайту