Будем честны. Глубокое обучение без графических процессоров - большая головная боль! Да, Google Colab и Kaggle есть, но жизнь и работа не всегда сводятся к обучению аккуратному классификатору MNIST.

Введение

Для обучения современных моделей или моделей SOTA, GPU является большой необходимостью. И даже если нам удастся его достать, возникает проблема нехватки памяти. Мы более или менее привыкли видеть ошибку OOM (Out of Memory) всякий раз, когда мы бросаем большой пакет для обучения. Проблема становится гораздо более очевидной, когда мы говорим о современных алгоритмах компьютерного зрения. Мы прошли гораздо более долгий путь со времен VGG или даже ResNet18. Современные более глубокие архитектуры, такие как UNet, ResNet-152, RCNN, Mask-RCNN, чрезвычайно интенсивно потребляют память. Следовательно, существует довольно высокая вероятность того, что у нас закончится память при обучении более глубоких моделей.

Вот ошибка OOM при запуске модели в PyTorch.

RuntimeError: CUDA out of memory. Tried to allocate 44.00 MiB (GPU 0; 10.76 GiB total capacity; 9.46 GiB already allocated; 30.94 MiB free; 9.87 GiB reserved in total by PyTorch)

Обычно есть 2 решения, которые практикующие немедленно принимают при обнаружении ошибки OOM.

  1. Уменьшить размер партии
  2. Уменьшить размер изображения

Более чем в 90% случаев этих двух решений более чем достаточно. Итак, вопрос, который вы хотите задать: зачем оставшимся 5% нужно что-то еще. Чтобы ответить, давайте посмотрим на изображения ниже.

Это из конкурса Kaggle Анализ облаков по спутниковым изображениям. Задача заключалась в том, чтобы правильно сегментировать разные типы облаков. Теперь эти изображения были очень высокого разрешения 1400 x 2100. Как вы понимаете, слишком большое уменьшение размеров изображения будет иметь очень негативное влияние в этом сценарии, поскольку мелкие узоры и текстуры являются важными особенностями, которые здесь необходимо изучить. Следовательно, единственный другой вариант - уменьшить размер партии.

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

Напоминаем, что если вы вспомните градиентный спуск или, в частности, мини-пакетный градиентный спуск в нашем случае, вы вспомните, что вместо вычисления потерь и возможных градиентов для всего набора данных мы выполняем операцию с меньшими партиями. Помимо помощи в размещении данных в памяти, это также помогает нам быстрее сходиться, поскольку параметры обновляются после каждого мини-пакета. Но что происходит, когда размер партии становится слишком маленьким, как в приведенном выше случае. Если сделать приблизительную оценку, что, возможно, 4 таких изображения могут быть помещены в один пакет в графическом процессоре объемом 11 ГБ, вычисленные потери и градиенты не будут точно отражать весь набор данных. В результате модель будет сходиться намного медленнее или, что еще хуже, не будет сходиться вообще.

Накопление градиента

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

Пример: если вы запускаете накопление градиента с шагами по 5 и размером пакета из 4 изображений, он служит почти той же цели, что и запуск с размером пакета из 20 изображений.

Реализация

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

У нас есть optimizer.step(), который обновляет параметры для accumulation_steps количества пакетов. Кроме того, одновременно вызывается model.zero_grad() для сброса накопленных градиентов.

Сделать то же самое для keras / tensorflow немного сложнее. Есть разные версии, написанные людьми, которые вы найдете в Интернете. Вот один из написанных @alexeydevederkin.

Внимание: он намного длиннее и сложнее кода pytorch из-за отсутствия модульности в процессе обучения в keras.

Также доступны чистые коды тензорного потока, которые меньше по размеру. Вы их легко найдете.

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