Предварительное условие: Учебник 0 (настройка Google Colab, времени выполнения TPU и облачного хранилища)
Трансферное обучение - обязательный навык для практиков глубокого обучения. Классическим набором данных для демонстрации эффективности трансферного обучения является соревнование Kaggle Собаки против кошек, в котором каждая запись данных представляет собой изображение кошки или собаки:
Решение Кераса. В официальном блоге Кераса есть старый учебник о собаках против кошек. Однако способ, которым они это сделали, довольно сложен. На высоком уровне их руководство состоит из двух основных этапов.
- Обучите простую двухуровневую сеть поверх замороженной сети VGG16, предварительно обученной на ImageNet. Это приводит к точности проверки около 90%.
- Разморозьте последние 5 сверточных слоев предварительно обученного VGG16 и повторно обучите их вместе, когда появятся 2 новых слоя из шага 1. Это дает точность 94%.
У них также есть довольно запутанная реализация шага 1. Обычно, когда мы выполняем переносное обучение, мы меняем заголовок предварительно обученного уровня. Здесь «голова» ConvNet - это последний блок полностью связанных слоев. В исходной модели VGG16 головка выводит прогноз с 1001 классом. Для нашей задачи у нас есть только два класса: собаки и кошки, поэтому мы меняем голову на бинарный классификатор.
В Руководстве по Keras шаг 1 гораздо более жестокий: они отрезают голову VGG16, не давая ему новую голову. Затем они позволили обезглавленному VGG16 выполнять прогнозы на основе набора данных Собаки против кошек. Поскольку у модели нет головы, она не может выводить какие-либо ярлыки; вместо этого из его шеи поступает какой-то материал, который должен питать головной блок в качестве входных данных. В Руководстве по Keras они собирают эту кровь шеи (которую они называют узкими местами), рассматривают ее как новый набор данных и обучают неглубокую модель с 2 слоями.
Наше решение. В этом руководстве мы следуем гораздо более простому подходу - напрямую обучаем всю модель с помощью нового бинарного классификатора головы (то есть он предсказывает вероятности «собака» и «кошка». ”), И мы фиксируем базовую модель на протяжении всего обучения. Как мы скоро увидим, это приводит к 99% точности за одну эпоху обучения.
Кроме того, мы не будем использовать VGG16, как в блоге Keras, который старый (2014 г.) и небольшой (16 слоев). Мы находимся в 2019 году, и у нас есть гораздо более мощное оружие: InceptionResNetV2. Взгляните на этот BFN:
Подготовка предварительно обученной модели. Сначала мы настроили Fenwicks:
import numpy as np import tensorflow as tf import os if tf.io.gfile.exists('./fenwicks'): tf.io.gfile.rmtree('./fenwicks') !git clone -q https://github.com/fenwickslab/fenwicks.git import fenwicks as fw fw.colab_utils.setup_gcs()
И варианты гиперпараметров:
ROOT_DIR = 'gs://gs_colab' PROJECT = 'tutorial4' MODEL = "InceptionResNetV2" #@param ["InceptionResNetV2", "ResNet50", "ResNet50V2", "InceptionV3", "MobileNetV2", "Xception"] BATCH_SIZE = 128 #@param ["128", "256", "512"] {type:"raw"} EPOCHS = 1 #@param {type:"slider", min:0, max:50, step:1} LEARNING_RATE = 0.001 #@param ["0.001", "0.01"] {type:"raw"} WARMUP = 0.05 #@param {type:"slider", min:0, max:0.5, step:0.05}
В этой серии руководств мы всегда используем переменные data_dir
, work_dir
и model_dir
для хранения файлов данных, временных файлов, созданных во время обучения, и предварительно обученных весов моделей соответственно.
fw.colab_tpu.setup_gcs() data_dir, work_dir = fw.io.get_project_dirs(ROOT_DIR, PROJECT)
Теперь давайте создадим базовую модель: InceptionResNetV2, предварительно обученный в ImageNet, предоставленный Керасом.
base_model = fw.keras_models.get_model(MODEL, BUCKET)
Внутри Fenwicks сначала загружает веса модели на локальный диск машины Colab, а затем сохраняет веса в формате файла Tensorflow checkpoint
. После этого он загружает контрольную точку в model_dir
на GCS. Возвращаемое значение base_model
содержит:
- Функция
model_func
, которая создает структуру InceptionResNet. - Путь к файлу весов модели,
weight_dir
. - Имена переменных веса модели,
weight_vars
. - Размер изображения по умолчанию базовой модели. Для InceptionResNetV2 размер изображения по умолчанию составляет 299x299. Для многих других моделей, таких как ResNet50, размер изображения по умолчанию составляет 224x224.
- Функция нормализатора изображения. Для InceptionResNetV2 нормализатор просто (i) делит каждый пиксель на 255, (ii) вычитает на 0,5 и (iii) умножает на 2. Это значение по умолчанию для большинства моделей Google. В других моделях, таких как ResNeXt, используется стандартный масштабатор, который вычитает среднее значение, а затем делит на стандартное отклонение.
Подготовка набора данных. Сначала мы загружаем файлы данных на локальный диск с сайта fast.ai:
data_dir_local = fw.datasets.untar_data(fw.datasets.URLs.DVC, os.path.join('.', PROJECT_NAME)) train_dir_local = os.path.join(data_dir_local, 'dogscats/train') valid_dir_local = os.path.join(data_dir_local, 'dogscats/valid')
Файлы данных fast.ai в основном представляют собой обучающий набор (25k изображений) от Kaggle, далее разделенный на обучающий набор из 23k изображений и набор для проверки 2k. Затем мы преобразуем их в TFRecord
и загружаем в GCS с помощью единственного лайнера Фенвикса:
train_fn = os.path.join(data_dir, 'train.tfrec') valid_fn = os.path.join(data_dir, 'valid.tfrec') paths_train, y_train, labels_train = fw.data.data_dir_tfrecord(train_dir_local, train_fn) paths_valid, y_valid, labels_valid = fw.data.data_dir_tfrecord(valid_dir_local, valid_fn) n_train, n_valid = len(y_train), len(y_valid) n_classes = len(labels_train)
Создание нашей новой ConvNet. Мы строим нашу модель трансферного обучения, добавляя слои один за другим к Sequential
модели Фенвика. Первый уровень - это базовая модель, для которой мы фиксируем веса, устанавливая для его свойства trainable
значение False
, как в Keras. Затем мы добавляем слой DenseBN
(полностью связанный слой, за которым следует пакетная нормализация) и классификатор:
def build_nn(c=256): base = base_model.model_func() base.trainable = False model = fw.Sequential() model.add(base) model.add(tf.keras.layers.Flatten()) model.add(fw.layers.DenseBN(c)) model.add(fw.layers.Classifier(n_classes)) return model
Мы будем обучать нашу модель с Адамом и графиком скорости обучения косинусом:
steps_per_epoch = n_train // BATCH_SIZE total_steps = steps_per_epoch * EPOCHS warmup_steps = int(total_steps * WARMUP) cosine_decay = tf.train.cosine_decay_restarts lr_func = fw.train.one_cycle_lr(LEARNING_RATE, total_steps, warmup_steps, cosine_decay) fw.plt.plot_lr_func(lr_func, total_steps) opt_func = fw.train.adam_optimizer(lr_func) model_func = fw.train.get_clf_model_func(build_nn, opt_func) fw.plt.plot_lr_func(lr_func, total_steps=total_steps)
Входной конвейер. Наш входной конвейер читает из подготовленных ранее TFRecord
. Мы используем преобразования данных из Начального руководства Google:
tfms = fw.transform.get_inception_transforms(h, w, training=training, normalizer=base_model.normalizer)
Преобразования Inception включают случайную обрезку, горизонтальный поворот и быстрое искажение цвета. Мы визуализируем их в Уроке 7 и сравним с другими преобразованиями в Учебнике 8. На основе этих преобразований мы создаем входной синтаксический анализатор, а затем входные функции, одну для обучающих данных и одну для данных проверки.
def get_input_func(fn, training): h = w = base_model.img_size tfms = fw.transform.get_inception_transforms(h, w, training=training, normalizer=base_model.normalizer) parser = fw.data.get_tfexample_image_parser(tfms) return lambda params: fw.data.tfrecord_ds( fn, parser, params['batch_size'], training=training) train_input_func = get_input_func(train_fn, True) valid_input_func = get_input_func(valid_fn, False)
Обучение и оценка модели. Наконец, мы готовы построить TPUEstimator
и обучить модель:
est = fw.train.get_tpu_estimator(steps_per_epoch, model_func, work_dir, base_model.weight_dir, base_model.weight_vars, trn_bs=BATCH_SIZE, val_bs = n_valid) est.train(train_input_func, steps=total_steps)
Само обучение занимает мало времени, так как мы тренируем модель только за 1 эпоху. Однако компиляция гигантской сети InceptionResNetV2 в машинный код TPU и загрузка предварительно обученных весов из GCS в TPU занимает очень и очень много времени. В общей сложности весь процесс занимает около 4 минут.
Проверим точность валидации:
result = est.evaluate(valid_input_func, steps=1) print(f'Test results: accuracy={result["accuracy"] * 100: .2f}%, loss={result["loss"]: .2f}.')
Наконец, давайте немедленно очистим файлы, чтобы сэкономить место на GCS, поскольку Google начинает взимать с нас плату, когда мы используем более 5 ГБ. При желании мы также можем очистить data_dir
и model_dir
.
fw.io.create_clean_dir(work_dir)
Вот полный блокнот.
Все уроки: