Повторяйте до сходимости (нейронные сети и обратное распространение)

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

Обзор нейронной сети

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

Однако не все распределения данных можно так четко разделить. Скажем, например, это ваше распределение красных точек по сравнению с синими точками:

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

Фу.

В этой ситуации было бы идеально использовать два (или более) линейных классификатора и объединить их вместе, чтобы получить такой результат:

Это оказывается основой для нейронных сетей, также известных как многослойные персептроны. Однако это не так просто, как просто объединить пару линейных классификаторов. На изображении выше мы используем два из них. Однако, если бы вы взяли эти два отдельных классификатора:

…и сложите их вместе, вы не получите красивую V-образную форму на диаграмме. На самом деле вы получаете сумму/среднее значение двух гиперплоскостей, поэтому результат будет таким:

Чтобы заставить базовый персептрон создавать формы, которые не являются простыми гиперплоскостями, мы используем «скрытый слой», который использует функцию активации для преобразования простого линейного классификатора в более интересную форму. Существует несколько типов функций активации, но самая популярная из них, безусловно, называется ReLU (Rectified Linear Unit). ReLU в основном берет результаты ваших рандомизированных вычислений wx+b, устанавливает y = x для всех положительных значений и устанавливает все отрицательные значения равными 0. Это создает форму с изломом или шарниром.

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

И теперь, когда вы сложите эти функции вместе, вы получите что-то, что сильно отличается от стандартного линейного классификатора:

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

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

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

Теперь, когда мы поняли цель, пришло время выяснить, как мы ее достигаем.

Обратное распространение

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

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

Последние должны быть первыми

Поскольку это «обратное» распространение, первый слой, который мы оцениваем, на самом деле является последним слоем, который мы вычислили в прямом проходе. Я буду называть веса и значения смещения на этом слое WL. Предыдущий слой будет WL-1, затем слой, предшествующий этому WL-2, и так далее.

На первом уровне мы вычисляем производную нашего убытка (сумма квадратов остатков или SSR) по отношению к нашему окончательному весу WL. Это еще один способ сказать: если изменится WL, как это повлияет на SSR? Скажем еще один способ — каков наклон линии, если WL находится на нашей оси x, а SSR — на нашей оси y?

Если вы помните, когда мы рассматривали логистическую регрессию, мы хотели изменить наши значения веса и смещения до тех пор, пока совокупный наклон не приблизится к 0, что означает, что дальнейшие улучшения SSR невозможны. Цель здесь та же, только мы должны сделать это для связанной комбинации гирь, а не для одной гири. К счастью, мы можем начать с одного веса — WL — и двигаться в обратном направлении.

Чтобы вычислить этот градиент, мы можем использовать цепное правило. Оказывается, производная SSR по WL равна произведению двух других производных: производной SSR по нашим прогнозируемым значениям (также известной как наша функция потерь) и производной наших прогнозируемых значений по нашей функции активации по отношению к ВЛ.

Мы используем квадратную потерю в качестве нашей функции потерь, которая представляет собой просто квадрат нашей фактической метки за вычетом нашей предсказанной метки. Градиент/производная этой функции равен -2*(фактическое — предсказанное). [Если вы не знаете, как вычислять базовые производные, подобные этому, есть множество отличных онлайн-руководств, подобных этому, которые могут показать вам веревки.]

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

Обновление WL

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

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

Экзамен WL-1

**Примечание. Приведенное ниже объяснение было обновлено, чтобы исправить ошибку в исходной версии.

Теперь, когда мы рассчитали обновление веса для первого (или последнего, в зависимости от того, как вы на это смотрите) слоя, пришло время перейти к предыдущему слою. На этом уровне нам нужно вычислить производную SSR относительно WL-1. Используя цепное правило еще раз, мы вычисляем это следующим образом:

Давайте рассмотрим каждый из них по отдельности:

Помните, что ReLU преобразует отрицательные значения в 0, а положительные значения y = x * вес на этом уровне. Следовательно, производная по X будет либо равна 0 для любых отрицательных значений, либо WL-1 для положительных значений.

Технически математика разваливается при x = 0, так как на 0 делить нельзя. Однако практически это не проблема. Вы получите производный ответ 0 или WL-1 от функции активации, поэтому вы можете выбрать любой из них и применить его, когда x = 0. Это действительно зависит от характера вашей проблемы и данных, но что я? Пока что мы видели значение по умолчанию, при котором ответ равен 0 для всех значений 0 и ниже и WL-1 для всех значений больше нуля.

Прекрасная простота ReLU — часть 1

Я хочу особо отметить здесь, какова роль ReLU на данном этапе в этом процессе обратного распространения: он служит переключателем. Если на этом шаге у вас есть положительное значение для ввода (которое равно wx+b из предыдущего шага), тогда производная будет равна WL-1, и произойдет обновление. Однако, если у вас есть отрицательное значение для ваших входных данных, производная будет равна 0, что означает **обновление WL-1 не произойдет** в этой итерации. Кроме того, это информация, которая может быть собрана в точке ввода во время прямого прохода. Поэтому, когда приходит время для обратного распространения, все, что вам нужно вычислить, — это производную функции потерь по отношению к прогнозируемым значениям. Остальные значения уже известны, и если производная функции активации равна 0, можно просто игнорировать/пропускать этот шаг в процессе при обратном распространении.

Обновление WL-1

Собрав все вместе, наша логика обновления будет выглядеть так:

И снова, если X = 0, мы просто выбираем использовать 0 или WL-1 (и обычно 0 используется для создания разреженности).

Теперь, какие фактические вычисления должны выполняться как часть обратного распространения? Итак, мы оценили выходные данные ReLU, но в остальном все остальные переменные в этой формуле уже известны. WL и WL-1 были частью прямого прохода, мы уже вычислили производную SSR по отношению к предсказаниям на предыдущем уровне, и даже WL, умноженное на X, было значением, рассчитанным во время прямого прохода.

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

Подождите, это действительно все, что нужно?

Да. Да, это.

WL-2 и далее

Когда вы перемещаетесь по слоям обратно к первому, выполнение цепного правила означает, что вы просто продолжаете добавлять больше продуктов к предыдущему выходу — в частности, к предыдущему WL-? значение умножается на X. ОДНАКО, поскольку здесь мы будем продолжать умножать, если предыдущее обновление веса было равно нулю, вы можете просто остановиться. После того, как вы получили ноль в наборе из обновления WL-1 и выше, вы не будете выполнять дополнительные обновления на этом этапе, поскольку этот ноль всегда будет одним из оцениваемых продуктов, независимо от того, как оцениваются вещи выше по течению. На практике расчеты выглядят так:

С соответствующими обновлениями веса WL-? - Альфа * Выход. И снова, если вы получили обновление 0 на WL-1, все готово, и вам, строго говоря, не нужно продолжать вычисление обновлений для этого прохода для последующих весов, независимо от количества скрытых слоев.

Прекрасная простота ReLU — часть 2

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

Примечание о реальном мире

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

Два шага вперед, два шага назад

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

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