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

Мы будем использовать известную задачу классификации изображений кошек и собак (скажите, что изображение является изображением кошки или изображения собаки). Для начала у Tensorflow есть хороший учебник (с блокнотом colab), и мы дополним его дальнейшими пояснениями.



В приведенном выше примере вы увидите два способа настройки предварительно обученной модели:

  1. Извлечение признаков: используйте представления, изученные предыдущей сетью, для извлечения значимых признаков из новых образцов. Вы просто добавляете новый классификатор, который будет обучаться с нуля, поверх предварительно обученной модели, чтобы вы могли повторно использовать карты объектов, изученные ранее, для набора данных.
    Вам не нужно (повторно) обучать всю модель. . Базовая сверточная сеть уже содержит функции, которые обычно полезны для классификации изображений. Однако окончательная классификационная часть предварительно обученной модели специфична для исходной задачи классификации, а затем специфична для набора классов, на которых была обучена модель.
  2. Тонкая настройка: разморозьте несколько верхних слоев замороженной базовой модели и совместно обучите как недавно добавленные слои классификатора, так и последние слои базовой модели. Это позволяет нам «тонко настроить» представления функций более высокого порядка в базовой модели, чтобы сделать их более подходящими для конкретной задачи.

В настоящее время доминирующей архитектурой модели для компьютерного зрения является архитектура сверточной нейронной сети/CNN. Обратитесь к приложению для получения дополнительной информации о CNN, но теперь просто поймите, что модель обучена автоматически захватывать такие функции, как края, контур, ориентация, текстура, которые можно использовать для задач верхнего уровня.

Общие этапы обучения переносу классификации изображений

  1. Загрузчик данных
  2. Предварительная обработка
  3. Загрузите предварительно обученную модель, заморозьте слои модели в соответствии с вашими потребностями
  4. Добавьте дополнительные слои в соответствии с вашими потребностями, чтобы сформировать окончательную модель.
  5. Скомпилируйте модель, настройте оптимизатор и функцию потерь
  6. Обучите модель с помощью model.fit

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

Перед обучением основной модели немного кода для загрузки набора данных, настройки предварительной обработки. Вы можете сразу перейти к части «Создать базовую модель».

Импорт библиотеки и загрузка изображений

Эти шаги просто импортируют библиотеки и загружают обучающие изображения в папку «train» и «validation».

import matplotlib.pyplot as plt
import numpy as np
import os
import tensorflow as tf
_URL = 'https://storage.googleapis.com/mledu-datasets/cats_and_dogs_filtered.zip'
path_to_zip = tf.keras.utils.get_file('cats_and_dogs.zip', origin=_URL, extract=True)
PATH = os.path.join(os.path.dirname(path_to_zip), 'cats_and_dogs_filtered')
train_dir = os.path.join(PATH, 'train')
validation_dir = os.path.join(PATH, 'validation')

Вы видите следующие папки в загруженной папке keras: /root/.keras/datasets/cats_and_dogs_filtered

тренироваться

— кошки

— собаки

Проверка

— кошки

— собаки

Вы можете использовать инструмент Linux для проверки исходного размера изображения.

!apt install file
!apt install -y imagemagick
!file /root/.keras/datasets/cats_and_dogs_filtered/train/cats/cat.199.jpg
!identify /root/.keras/datasets/cats_and_dogs_filtered/train/cats/cat.199.jpg

Исходный размер изображения 270x319.

Загрузить поезд и набор данных проверки

Мы загрузим набор обучающих данных из папки поезда и набор данных проверки из папки проверки. Обратите внимание на два параметра: shuffle, Перетасовывать ли данные. По умолчанию: Истина. Если установлено значение False, данные сортируются в алфавитно-цифровом порядке; image_size, Размер для изменения размера изображений после их чтения с диска. Поскольку конвейер обрабатывает пакеты изображений, которые должны иметь одинаковый размер, это необходимо обеспечить.

BATCH_SIZE = 32
IMG_SIZE = (160, 160)
train_dataset = tf.keras.utils.image_dataset_from_directory(train_dir,
                                                            shuffle=True,
                                                            batch_size=BATCH_SIZE,
                                                            image_size=IMG_SIZE)

validation_dataset = tf.keras.utils.image_dataset_from_directory(validation_dir,
                                                                 shuffle=True,
                                                                 batch_size=BATCH_SIZE,
                                                                 image_size=IMG_SIZE)

Поскольку исходный набор данных не содержит тестового набора, вы создадите его. Для этого определите, сколько пакетов данных доступно в проверочном наборе, используя tf.data.experimental.cardinality, а затем переместите 20% из них в тестовый набор.

val_batches = tf.data.experimental.cardinality(validation_dataset)
test_dataset = validation_dataset.take(val_batches // 5)
validation_dataset = validation_dataset.skip(val_batches // 5)

Увеличение данных и предварительная обработка

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

data_augmentation = tf.keras.Sequential([
  # A preprocessing layer which randomly flips images during training.
  tf.keras.layers.RandomFlip('horizontal_and_vertical'),
  # A preprocessing layer which randomly rotates images during training.
  tf.keras.layers.RandomRotation(0.2),
])

Изменение масштаба значений пикселей
Модель tf.keras.applications.MobileNetV2 ожидает значения пикселей в [-1, 1], но на данный момент значения пикселей в ваших изображениях находятся в [0, 255]. Чтобы масштабировать их, используйте метод предварительной обработки, включенный в модель.

preprocess_input = tf.keras.applications.mobilenet_v2.preprocess_input

Создать базовую модель

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

# Create the base model from the pre-trained model MobileNet V2
IMG_SHAPE = IMG_SIZE + (3,)
base_model = tf.keras.applications.MobileNetV2(input_shape=IMG_SHAPE,
                                               include_top=False,
                                               weights='imagenet')
image_batch, label_batch = next(iter(train_dataset))
feature_batch = base_model(image_batch)
# 32 images, since our batch_size is 32
print(feature_batch.shape)

Заморозить базовую модель

Когда вы используете базовую модель в качестве слоя извлечения признаков, важно заморозить сверточную базу перед компиляцией и обучением модели. Замораживание (путем установки layer.trainable = False) предотвращает обновление весов в данном слое во время обучения. MobileNet V2 имеет много слоев, поэтому установка для обучаемого флага всей модели значения False заморозит их все.

base_model.trainable = False

Настройка архитектуры модели

Когда вы определяете модель, вам нужно сообщить Keras, как сопоставлять входные данные с выходными, в нашем сценарии входные данные → слой увеличения данных → слой предварительной обработки (перемасштабирования) → MobileV2Net → слой GlobalAveragePooling2D → слой Dropout → плотный слой .

Чтобы сгенерировать прогнозы из блока функций, усредните пространственные пространственные местоположения 5x5, используя слой tf.keras.layers.GlobalAveragePooling2D, чтобы преобразовать функции в один вектор из 1280 элементов для каждого изображения. Это похоже на то, как GlobalAveragePooling2D применяет усреднение к пространственным измерениям до тех пор, пока каждое пространственное измерение не станет единым.

global_average_layer = tf.keras.layers.GlobalAveragePooling2D()
feature_batch_average = global_average_layer(feature_batch)
# (32, 5, 5, 1280) -> (32, 1280)
print(feature_batch_average.shape)

Примените слой tf.keras.layers.Dense, чтобы преобразовать эти функции в один прогноз для каждого изображения.

prediction_layer = tf.keras.layers.Dense(1)
prediction_batch = prediction_layer(feature_batch_average)
print(prediction_batch.shape)

Цепь слоев

inputs = tf.keras.Input(shape=(160, 160, 3))
x = data_augmentation(inputs)
x = preprocess_input(x)
x = base_model(x, training=False)
x = global_average_layer(x)
x = tf.keras.layers.Dropout(0.2)(x)
outputs = prediction_layer(x)
model = tf.keras.Model(inputs, outputs)

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

Укажите оптимизатор и функция потерь

base_learning_rate = 0.0001
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=base_learning_rate),
              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              metrics=['accuracy'])

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

Мы записываем историю тренировок, чтобы потом продолжить обучение

initial_epochs = 10
history = model.fit(train_dataset,
                    epochs=initial_epochs,
                    validation_data=validation_dataset)

Тонкая настройка

Основная идея тонкой настройки заключается в том, что вы хотите настроить некоторые веса в предварительно обученной модели, особенно в последних нескольких слоях, чтобы настроить веса из общих карт объектов на функции, связанные конкретно с вашим набором данных. Вам следует попытаться настроить небольшое количество верхних слоев, а не всю модель MobileNet. В большинстве сверточных сетей чем выше уровень, тем более он специализирован. Первые несколько слоев изучают очень простые и общие функции, которые применимы практически ко всем типам изображений. По мере того, как вы поднимаетесь выше, функции становятся все более специфичными для набора данных, на котором была обучена модель. Цель тонкой настройки — адаптировать эти специализированные функции для работы с новым набором данных, а не перезаписывать общее обучение.

Разморозить верхние слои модели

Теперь мы хотим настроить веса предварительно обученной модели, но только после определенного слоя. Следующий код сначала устанавливает базовую модель как обучаемую, а затем устанавливает все слои до слоя 100 как необучаемые (замораживание предыдущего слоя, который содержит простые и общие функции).

base_model.trainable = True
# Fine-tune from this layer onwards
fine_tune_at = 100
# Freeze all the layers before the `fine_tune_at` layer
for layer in base_model.layers[:fine_tune_at]:
  layer.trainable = False

Настройка архитектуры модели

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

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

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

model.compile(loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),
              optimizer = tf.keras.optimizers.RMSprop(learning_rate=base_learning_rate/10),
              metrics=['accuracy'])

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

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

fine_tune_epochs = 10
total_epochs =  initial_epochs + fine_tune_epochs
history_fine = model.fit(train_dataset,
                         epochs=total_epochs,
                         initial_epoch=history.epoch[-1],
                         validation_data=validation_dataset)

Прогноз

# Retrieve a batch of images from the test set
image_batch, label_batch = test_dataset.as_numpy_iterator().next()
predictions = model.predict_on_batch(image_batch).flatten()
# Apply a sigmoid since our model returns logits
predictions = tf.nn.sigmoid(predictions)
predictions = tf.where(predictions < 0.5, 0, 1)
print('Predictions:\n', predictions.numpy())
print('Labels:\n', label_batch)
plt.figure(figsize=(10, 10))
for i in range(9):
  ax = plt.subplot(3, 3, i + 1)
  plt.imshow(image_batch[i].astype("uint8"))
  plt.title(class_names[predictions[i]])
  plt.axis("off")

Приложение