Ускорение тренировки ваших моделей глубокого обучения

Улучшение эффективности тренировки моделей глубокого обучения

Супер сходимость с обучением одним циклом

Фото от Philip Swinburn на Unsplash

Вы сталкивались с ситуациями, когда легко достичь начального уровня точности, но как только достигаете 90%, нужно действительно сильно толкать себя, чтобы выжать из модели какое-либо улучшение в производительности? Ваша модель тренируется слишком долго?

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

Изначально упомянутая в статье Лесли Смита, стратегия обучения с одним циклом скорости обучения[1], [2] сосредотачивается на уникальной стратегии динамического обновления скорости обучения во время процесса обучения. Звучит сложно, не беспокойтесь, давайте сначала рассмотрим типичную установку для обучения, а затем постепенно разберемся, как мы можем улучшить результаты, используя обучение с одним циклом скорости обучения.

Тренировка классификатора изображений

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

Фото от Markus Spiske на Unsplash

Постановка задачи

Игра в камень-ножницы-бумага – это классическая детская игра, в которой два игрока используют жесты рук (камень, ножницы или бумагу) и соревнуются между собой в том, чтобы одолеть оппонента. Например, жест камня побеждает ножницы, а жест бумаги побеждает камень. Интересно, не так ли?

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

Набор данных

Мы имеем удачу, что у нас уже есть размеченный набор данных, которым мы можем воспользоваться для обучения классификационной модели с большим эффектом. Набор данных размещен в каталоге TensorFlow dataset, который сделал доступным Laurence Moroney (CC BY 2.0). Он обладает следующими особенностями:

  • Количество точек данных: 2800
  • Количество классов: 3
  • Доступен разделение на обучающую и тестовую выборки: Да
  • Размер набора данных: 220 МБ

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

import tensorflow_datasets as tfdsDATASET_NAME = 'rock_paper_scissors'(dataset_train_raw, dataset_test_raw), dataset_info = tfds.load(    name=DATASET_NAME,    data_dir='tmp',    with_info=True,    as_supervised=True,    split=[tfds.Split.TRAIN, tfds.Split.TEST],)# Построение примеров из набора данныхfig = tfds.show_examples(dataset_train_raw, dataset_info)

Ниже приведены несколько образцов изображений из самого этого набора данных:

Фигура: Примеры точек данных в наборе данных Rock Paper Scissors

Скорость обучения

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

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

Иллюстрация: Влияние скорости обучения на способность модели изучать цель (минимум). Источник: Автор

Градиентный спуск и оптимизаторы

Градиентный спуск – это стандартный способ обучения/оптимизации нейронных сетей. Он работает путем минимизации целевой функции путем обновления параметров сети в обратном направлении градиента. Без детального изучения, это помогает перемещаться вниз по склону целевой функции. Подробное введение в градиентный спуск доступно здесь для справки.

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

AdaGrad Adaptive Gradient algorithm – алгоритм оптимизации, который адаптирует скорости обучения для каждого параметра на основе их исторических градиентов, позволяя более крупные обновления для редких параметров и меньшие обновления для частых. Он разработан для эффективной работы с разреженными данными. Он подходит, когда имеет дело с разреженными данными.

RMSProp Root Mean Square Propagation оптимизирует обучение путем настройки скоростей обучения для каждого параметра индивидуально. Он решает проблему уменьшения скоростей обучения в AdaGrad, используя скользящее среднее квадратов градиентов. Это помогает адаптивно масштабировать скорости обучения на основе последних величин градиента.

ADAM Adaptive Moment Estimation – это алгоритм оптимизации, который комбинирует идеи как из RMSProp, так и из методов момента. Он поддерживает экспоненциально затухающие средние градиентов и квадратов градиентов прошлого, используя их для адаптивного обновления параметров. ADAM известен своей эффективностью и эффективностью обучения глубоких нейронных сетей.

Линейно-декрементный график обучения и суперсходимость

Линейно-декрементный график обучения – это простой двухшаговый процесс для улучшения скорости обучения и импульса с развитием обучения. Он работает следующим образом:

  • Шаг 1: Мы начинаем увеличивать скорость обучения сначала от низкого значения до высокого линейным инкрементом в течение нескольких эпох
  • Шаг 2: Мы поддерживаем наивысшее значение скорости обучения в течение нескольких эпох
  • Шаг 3: Затем мы снова переходим к низкой скорости обучения с течением времени

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

Применение линейно-декрементного графика обучения

Давайте сначала разберемся с простой реализацией для линейно-декрементного графика обучения, а затем используем его для обучения нашей модели. Мы воспользуемся готовой реализацией для графика скорости обучения с одной эпохой от мини-конференции TensorFlow World 2019 Мартина Горнера, как показано в листинге 2.

def lr_function(epoch):    # установите начальное, минимальное и максимальное значения для скорости обучения    start_lr = 1e-3; min_lr = 1e-3; max_lr = 2e-3    # определите количество эпох для линейного увеличения # скорости обучения и затем фактор затухания     rampup_epochs = 6; sustain_epochs = 0; exp_decay = .5    # метод для обновления значения скорости обучения на основе текущей эпохи    def lr(epoch, start_lr, min_lr, max_lr, rampup_epochs,           sustain_epochs, exp_decay):        if epoch < rampup_epochs:            lr = ((max_lr - start_lr) / rampup_epochs                        * epoch + start_lr)        elif epoch < rampup_epochs + sustain_epochs:            lr = max_lr        else:            lr = ((max_lr - min_lr) *                      exp_decay**(epoch - rampup_epochs -                                    sustain_epochs) + min_lr)        return lr    return lr(epoch, start_lr, min_lr, max_lr,              rampup_epochs, sustain_epochs, exp_decay)

Мы выполняем эту функцию (см. листинг 2) на фиксированное количество эпох, чтобы показать, как изменяется скорость обучения в соответствии с двумя шагами, о которых мы ранее говорили. Здесь мы начинаем с начальной скорости обучения 1e-3 и увеличиваем ее до 2e-3 в первые несколько эпох. Затем она опять снижается до 1e-3 в течение остальных эпох. Эта динамическая кривая скорости обучения показана на следующем графике для примера с 24 эпохами.

Политика скорости обучения One-cycle на 24 эпохи. Скорость обучения линейно увеличивается, а затем медленно уменьшается на оставшихся эпохах. Источник изображения: автор

Теперь мы проверим наш планировщик скорости обучения one-cycle, применяя его при использовании модели MobileNetV2 в качестве извлекателя признаков при обучении головы классификации для нашего текущего случая с игрой камень-ножницы-бумага. Затем мы сравним его с простым CNN и MobileNetV2 + головой классификации с обычной оптимизацией Adam. Полный ноутбук доступен для ознакомления на GitHub. Для краткого обзора следующий отрывок представляет собой обзор того, как мы используем обратные вызовы TensorFlow, чтобы подключить нашу утилиту с однократной скоростью 1-цикла.

# Установка формы изображения INPUT_IMG_SHAPE= (128, 128, 3)# Получаем предварительно обученную модель MobileNetV2base_model = tf.keras.applications.MobileNetV2(  input_shape=INPUT_IMG_SHAPE,  include_top=False,  weights='imagenet',  pooling='avg')# Подключаем голову классификацииmodel_lr = tf.keras.models.Sequential()model_lr.add(base_model)model_lr.add(tf.keras.layers.Dropout(0.5))model_lr.add(tf.keras.layers.Dense(    units=NUM_CLASSES,    activation=tf.keras.activations.softmax,    kernel_regularizer=tf.keras.regularizers.l2(l=0.01)))# компилируем модельmodel_lr.compile(    optimizer=tf.keras.optimizers.Adam(),    loss=tf.keras.losses.sparse_categorical_crossentropy,    metrics=['accuracy'])# устанавливаем количество эпохinitial_epochs = 24# Устанавливаем модель для обучения# Обратный вызов LearningRateScheduler - это то место, где мы# подключаем нашу пользовательскую функцию скорости 1-циклаtraining_history_lr = model_lr.fit(    x=dataset_train_augmented_shuffled.repeat(),    validation_data=dataset_test_shuffled.repeat(),    epochs=initial_epochs,    steps_per_epoch=steps_per_epoch,    validation_steps=validation_steps,    callbacks=[        tf.keras.callbacks.LearningRateScheduler(lambda epoch: \                                             lr_function(epoch),                                             verbose=True)    ],    verbose=1)

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

MobileNetV2 с использованием скорости обучения с одним циклом (mobileNetV2_lr) превосходит MobileNetV2 и простые архитектуры CNN, достигая сходимости всего за 5 эпох

Мы достигаем стабильных значений точности на проверке в диапазоне от 90% до 92% в течение 10 эпох, что пока является лучшим результатом среди всех наших моделей. При оценке производительности модели на тестовом наборе данных она также показывает ту же историю – MobileNetV2_lr легко превосходит другие две модели.

# Простая CNNTest loss:  0.7511898279190063Test accuracy:  0.7768816947937012# MobileNetV2Test loss:  0.24527719616889954Test accuracy:  0.9220430254936218# MobileNetV2_LRTest loss:  0.27864792943000793Test accuracy:  0.9166666865348816

Заключение

Преодоление плато в модельной производительности, превышающей точность 90%, и оптимизация времени обучения могут быть достигнуты с помощью применения метода One-Cycle Learning Rate. Эта техника, разработанная Лесли Смитом и его командой, динамически регулирует скорость обучения во время тренировки, предлагая стратегический подход к повышению производительности модели. Приняв этот метод, вы сможете эффективно управлять сложностями настройки обучения и раскрыть потенциал более быстрых и эффективных моделей глубокого обучения. Потрясающая мощь One-Cycle Learning Rate поможет повысить опыт обучения и достичь превосходных результатов!