В этой статье мы будем опираться на базовую нейронную сеть, представленную в части 3. Мы добавим несколько драгоценных камней, которые улучшат сеть. Небольшие изменения с большим влиянием. Понятия, которые мы встретим, такие как Инициализация, Мини-пакеты, Распараллеливание, Оптимизаторы и Регуляризация - это, без сомнения, то, с чем вы быстро столкнетесь при изучении нейронных сетей. В этой статье приводится пошаговая инструкция.

Это четвертая часть цикла статей:

Инициализация

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

Как правило, достаточно просто рандомизировать параметры от -0,5 до 0,5 со средним значением 0.

Однако в глубоких сетях исследования показали, что мы можем улучшить сходимость, если позволим значениям весов обратно зависеть от количества нейронов в связанном слое (или иногда двух связанных слоях). Другими словами: много нейронов ⇒ начальный вес ближе к 0. Меньшее количество нейронов ⇒ более высокая дисперсия. Вы можете узнать больше о том, почему здесь и здесь.

Я реализовал несколько наиболее популярных из них и сделал возможным внедрить их в качестве стратегии (см. Строку 5) при создании нейронной сети:

Реализованные стратегии инициализации: XavierNormal, XavierUniform, LeCunNormal, LeCunUniform, HeNormal, HeUniform и Random. Вы также можете явно указать веса для слоя, указав весовую матрицу при создании слоя.

Дозирование

В прошлой статье мы кратко коснулись того факта, что мы можем передавать образцы через сеть столько, сколько захотим, а затем, по нашему собственному усмотрению, выбрали когда для обновления веса. Разделение в API между методом Assessment () (который собирает информацию об обучении / впечатлениях) и методом updateFromLearning () (который позволяет этому обучению осваиваться) существует. чтобы позволить именно это. Он оставляет открытым для пользователя API, какую из следующих стратегий использовать:

Стохастический градиентный спуск

В стохастическом градиентном спуске мы обновляем веса и смещения после каждой оценки. Помните из части 2, что функция стоимости зависит от ввода и математического ожидания: C = C (W, b, Sσ, x, exp). Как следствие, ландшафт затрат будет немного отличаться для каждого образца, а отрицательный градиент будет указывать в направлении наискорейшего спуска в этом уникальном ландшафте для каждого образца.

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

Пока скорость обучения достаточно мала, ходьба в среднем, тем не менее, снижается до локального минимума.

Проблема в том, что SGD сложно эффективно распараллелить. Каждое обновление весов должно быть частью следующего расчета.

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

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

Мини-пакетный градиентный спуск

Мини-пакетный градиентный спуск - это компромисс между SGD и BGD - пакеты из N образцов проходят через сеть до обновления весов. Обычно N находится в диапазоне от 16 до 256. Таким образом мы получаем что-то достаточно стабильное в своем спуске (хотя и не оптимальное):

  • По сравнению с пакетным градиентным спуском мы получаем более быструю обратную связь и можем работать с управляемыми наборами данных.
  • По сравнению со стохастическим градиентным спуском мы можем запустить весь пакет параллельно, используя все ядра ЦП, графического процессора или TPU. Лучшее из двух миров.

Распараллеливание

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

С этим можно справиться, используя ограничение потока.

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

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

Типичный способ распараллеливания выполнения мини-пакетов в нейронной сети выглядит так:

Особо обратите внимание на конструкцию для распределения обработки всех выборок в пакете в виде параллельного потока в строке 2.

Это все, что нужно.

Лучше всего: значительное ускорение.

Здесь следует иметь в виду, что при таком распределении выполнения (выполнение пакета по нескольким параллельным потокам) к вычислениям добавляется энтропия, которая иногда нежелательна. Для иллюстрации: когда дельты ошибок суммируются в методе addDeltaWeightsAndBiases, они могут добавляться в несколько ином порядке при каждом запуске. Хотя все участвующие в пакете термины одинаковы, измененный порядок их суммирования может давать небольшие небольшие различия, которые со временем начинают проявляться в больших нейронных сетях, что приводит к невоспроизводимым прогонам . Это, вероятно, нормально в такой реализации нейронной сети на игровой площадке ... но если вы хотите провести исследование, вам придется подойти к параллелизму по-другому (обычно создавая большие матрицы входного пакета и распараллеливая все умножения матриц обоих каналов с прямой связью. и обратное распространение на GPU / TPU).

Оптимизаторы

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

Помните, это:

Что в коде выглядит так:

Хотя стремление к спуску SGD (и его пакетные варианты) страдает от того факта, что величина уклона пропорциональна тому, насколько крутым является уклон в точке оценки. Более плоская поверхность означает меньшую длину градиента… что дает меньший шаг. Как следствие, он может застрять в седловых точках - например, на полпути по этому пути:

(как видите, есть лучшие локальные минимумы в нескольких направлениях, чем застревание в этой седловой точке)

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

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

Неудивительно, что один из простейших оптимизаторов называется…

Импульс

В импульс мы вводим другую константу, чтобы указать, сколько эхо мы хотим из предыдущих вычислений. Этот фактор, γ, называется импульсом. Уравнение 1, приведенное выше, теперь принимает следующий вид:

Как видите, оптимизатор Momentum сохраняет последнюю дельту как v и вычисляет новую v на основе предыдущего один. Обычно γ может находиться в диапазоне 0,7–0,9, что означает, что 70–90% предыдущих вычислений градиента учитываются в этом новом вычислении.

В коде он настроен как (строка 4):

И применяется в оптимизаторе Momentum следующим образом:

Это небольшое изменение в способе обновления весов часто улучшает сходимость.

Мы можем сделать даже лучше. Еще один простой, но мощный оптимизатор - это ускоренный градиент Нестерова (NAG).

Нестеров ускоренный градиент

Единственное отличие от импульса состоит в том, что мы в NAG вычисляем Стоимость в позиции, которая уже учла эхо из последнего вычисления градиента.

С точки зрения программирования это плохие новости. До сих пор приятным свойством оптимизаторов было то, что они применялись только при обновлении весов. Здесь внезапно нам нужно включить последний градиент v уже при вычислении функции стоимости. Конечно, мы могли бы расширить эту идею. того, что такое оптимизатор, и позволить ему, например, также увеличивать поиск по весу. Это было бы непоследовательно, поскольку все остальные оптимизаторы (о которых я знаю) сосредоточены только на обновлении.

С помощью небольшого приема мы можем обойти эту проблему. Введя другую переменную x = W - γ v, мы можем переписать уравнения в терминах x. В частности, это введение означает, что проблемная функция стоимости будет зависеть только от x, а не от исторического v -значения ( по крайней мере, не явно зависимым. Часть v фактически все еще будет использоваться, но теперь неявно сохраняется для всех весов).

После переписывания мы можем снова переименовать x обратно в w, чтобы он больше походил на то, что мы узнаем. Уравнение становится:

Намного лучше. Теперь оптимизатор Нестерова можно применять только во время обновления:

Чтобы узнать больше о том, как и почему NAG часто работает лучше, чем Momentum, загляните здесь и здесь.

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

Регуляризация

Когда я только закончил писать первую версию кода нейронной сети, мне не терпелось ее протестировать. Я сразу загрузил набор данных MNIST и начал экспериментировать. Практически сразу я пришел к весьма примечательным цифрам с точки зрения точности. По сравнению с тем, чего достигли другие при создании аналогичных сетей, мой был лучше - намного лучше. Это, конечно, вызвало у меня подозрения.

Потом меня осенило. Я сравнил свою статистику в наборе обучающих данных с их статистикой в ​​тестовом наборе данных. Я обучил свою сеть в бесконечном цикле (почти) на данных обучения и даже не загружал набор данных test. И когда я это сделал, я испытал шок. Моя сеть была полностью испорчена с невидимыми данными, хотя она была близка к полубогу по обучающему набору данных. Я только что проглотил горькую пилюлю переобучения.

Переоснащение

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

Это когда вы зашли слишком далеко.

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

Но есть и другие способы снизить риск переобучения. Я завершу эту статью, представив простой, но эффективный подход.

L2 регуляризация

В регуляризации L2 мы стараемся избегать того, чтобы отдельные веса в сети становились слишком большими (или, если быть более точным: слишком далеко от нуля). Идея состоит в том, что вместо того, чтобы позволить нескольким весам иметь очень сильное влияние на результат, мы вместо этого предпочитаем, чтобы множество весов взаимодействовали, каждый и каждый с умеренным вкладом. Основываясь на обсуждении в Части 1: Основа, не кажется слишком большим, что несколько меньших весов ( по сравнению с меньшими крупными) дадут более гладкую и менее резкую / резкое разделение входных данных. Более того, кажется разумным, что более плавное разделение сохранит общие характеристики входных данных и, наоборот: резкое и острое то же самое могло бы очень точно вырезать конкретные характеристики входных данных.

Так как же нам избежать слишком больших индивидуальных весов в сети?

В L2-регуляризации это достигается путем добавления еще одного стоимостного члена к функции стоимости:

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

Фактор λ покажет, сколько L2-регуляризации мы хотим. Установка его в ноль полностью отменит эффект L2. Если установить, скажем, 0,5, регуляризация L2 будет составлять существенную часть общей стоимости сети. На практике вам нужно будет найти наилучшую возможную λ с учетом схемы вашей сети и ваших данных.

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

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

В коде регуляризация L2 может быть сконфигурирована как (см. Строку 9):

И применяется во время обновления таким образом (см. Строки 3 и 4):

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

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

В следующей статье, которая будет последней в этой серии, мы увидим, насколько хорошо сеть работает с классическим набором данных - Mnist: Часть 5: Обучение сети чтению рукописных цифр.

Обратная связь приветствуется!

Первоначально опубликовано на сайте machinelearning.tobiashill.se 17 декабря 2018 г.
Посмотрите мой новый проект Q'n'A-listicle best.ways.to.