Что такое оптимизация нейронной сети
Объясняем на пальцах принцип действия оптимизаторов для нейронных сетей: основные алгоритмы, и зачем они нужны
Оптимизаторы — важный компонент архитектуры нейронных сетей. Они играют важную роль в процессе тренировки нейронных сетей, помогая им делать всё более точные прогнозы. Специально к старту нового потока расширенного курса по машинному и глубокому обучению, делимся с вами простым описанием основных методик, используемых оптимизаторами градиентного спуска, такими как SGD, Momentum, RMSProp, Adam и др.
Оптимизаторы определяют оптимальный набор параметров модели, таких как вес и смещение, чтобы при решении конкретной задачи модель выдавала наилучшие результаты.
Самой распространённой техникой оптимизации, используемой большинством нейронных сетей, является алгоритм градиентного спуска.
Большинство популярных библиотек глубокого обучения, например PyTorch и Keras, имеют множество встроенных оптимизаторов, базирующихся на использовании алгоритма градиентного спуска, например SGD, Adadelta, Adagrad, RMSProp, Adam и пр.
Но почему алгоритмов оптимизации так много? Как выбрать из них правильный?
В документации к каждому из таких оптимизаторов приводится формула, с помощью которой такие оптимизаторы обновляют параметры модели. Что же означает каждая формула, и в чём её смысл?
Хочу уточнить, что данная статья написана для того, чтобы вы поняли общую картину и как в такую картину вписывается каждый алгоритм. Я не буду здесь погружаться в глубинные смыслы самих формул, так как рассуждать о математических тонкостях лучше всего в отдельной статье.
Обзор методов оптимизации на базе алгоритма градиентного спуска
Кривая потерь
Начнём с того, что рассмотрим на 3D-изображении, как работает стандартный алгоритм градиентного спуска.
Кривая потерь для алгоритма градиентного спуска
На рисунке показана сеть с двумя весовыми параметрами:
На горизонтальной плоскости размещаются две оси для весов w1 и w2, соответственно.
На вертикальной оси откладываются значения потерь для каждого сочетания весов.
Другими словами, форма кривой является отражением “ландшафта потерь” в нейронной сети. Кривая представляет потери для различных значений весов при сохранении неизменным фиксированного набора входных данных.
Синяя линия соответствует траектории алгоритма градиентного спуска при оптимизации:
Работа алгоритма начинается с выбора некоторых случайных значений обоих весов и вычисления значения потери.
После каждой итерации весовые значения будут обновляться (в результате чего, как можно надеяться, снизится потеря), и алгоритм будет перемещаться вдоль кривой к следующей (более нижней) точке.
В конечном итоге алгоритм достигает цели, то есть приходит в нижнюю точку кривой — в точку с самым низким значением потерь.
Вычисление градиента
Алгоритм обновляет значения весов, основываясь на значениях градиента кривой потерь в данной точке, и на параметре скорости обучения.
Обновление параметра градиентного спуска
Градиент измеряет уклон и рассчитывается как значение изменения в вертикальном направлении (dL), поделённое на значение изменения горизонтальном направлении (dW). Другими словами, для крутых уклонов значение градиента большое, для пологих — маленькое.
Вычисление градиента
Практическое применение алгоритма градиентного спуска
Визуальное представление кривых потерь весьма полезно для понимания сути работы алгоритма градиентного спуска. Однако нужно помнить, что описываемый сценарий скорее идеален, чем реален.
Во-первых, на приведённом выше рисунке кривая потерь идёт идеально плавно. На самом деле горбов и впадин на ней гораздо больше, чем плавных мест.
Во-вторых, мы будем работать не с двумя параметрами, а с гораздо большим их числом. Параметров может быть десятки или даже сотни миллионов, их просто невозможно визуализировать и представить даже мысленно.
На каждой итерации алгоритм градиентного спуска работает, «озираясь по всем направлениям, находя самый перспективный уклон, по которому можно спуститься вниз». Но что произойдёт, если алгоритм выберет лучший, по его мнению, уклон, но это будет не лучшее направление? Что делать, если:
Ландшафт круто уходит вниз в одном направлении, но, чтобы попасть в самую нижнюю точку, нужно двигаться в направлении с меньшими изменениями высоты?
Весь ландшафт представляется алгоритму плоским во всех направлениях?
Алгоритм попадает в глубокую канаву? Как ему оттуда выбраться?
Давайте рассмотрим несколько примеров кривых, перемещение по которым может создавать трудности.
Трудности при оптимизации градиентного спуска
Локальные минимумы
На стандартной кривой потерь, кроме глобального минимума, может встретиться множество локальных минимумов. Главной задачей алгоритма градиентного спуска, как следует из его названия, является спуск всё ниже и ниже. Но, стоит ему спуститься до локального минимума — и подняться оттуда наверх часто становится непосильной задачей. Алгоритм может просто застрять в локальном минимуме, так и не попав на глобальный минимум.
Локальный минимум и глобальный минимум
Седловые точки
Ещё одной важной проблемой является прохождение алгоритмом «седловых точек». Седловой называют точку, в которой в одном направлении, соответствующем одному параметру, кривая находится на локальном минимуме; во втором же направлении, соответствующем другому параметру, кривая находится на локальном максимуме.
Седловая точка
Какую опасность таят в себе седловые точки, которые алгоритм может встретить на своём пути? Опасность в том, что область, непосредственно окружающая седловую точку, как правило, довольно плоская, она напоминает плато. Плоская область означает практически нулевые градиенты. Оптимизатор начинает колебаться (осциллировать) вокруг седловой точки в направлении первого параметра, не “догадываясь” спуститься вниз по уклону в направлении второго параметра.
Алгоритм градиентного спуска при этом ошибочно полагает, что минимум им найден.
Овраги
Ещё одна головная боль алгоритма градиентного спуска — пересечение оврагов. Овраг — это протяжённая узкая долина, имеющая крутой уклон в одном направлении (т.е. по сторонам долины) и плавный уклон в другом (т.е. вдоль долины). Довольно часто овраг приводит к минимуму. Поскольку навигация по такой форме кривой затруднена, такую кривую часто называют патологическим искривлением (Pathological Curvature).
Овраги
Представьте узкую речную долину, плавно спускающуюся с холмов и простирающуюся вниз до озера. Вам нужно быстро спуститься вниз по реке, туда, где пролегает долина. Но алгоритм градиентного спуска вполне может начать движение, отталкиваясь попеременно от сторон долины, при этом движение вниз по направлению реки будет весьма медленным.
Алгоритм ванильного градиентного спуска, несмотря на его недостатки, по-прежнему считается основным, в частности, по той причине, что существуют специальные методы его оптимизации.
Первое улучшение алгоритма градиентного спуска — стохастический градиентный спуск (SGD)
Под обычным градиентным спуском (по умолчанию) обычно понимается «градиентный спуск со сменой коэффициентов после обсчёта всей выборки», то есть потери и градиент рассчитываются с использованием всех элементов набора данных.
Также применяется промежуточный стохастический градиентный спуск с мини-пакетами — вариант, при котором коэффициенты меняются после обсчета N элементов выборки, то есть для каждой тренировочной итерации алгоритм выбирает случайное подмножество набора данных.
Случайность выбора хороша тем, что она помогает глубже исследовать ландшафт потерь.
Ранее мы говорили, что кривая потерь получается за счёт изменения параметров модели с сохранением фиксированного набора входных данных. Однако если начать изменять входные данные, выбирая различные данные в каждом мини-пакете, значения потерь и градиентов также будут меняться. Другими словами, изменяя набор входных данных, для каждого мини-пакета мы получим собственную кривую потерь, которая немного отличается от других.
То есть, даже если алгоритм застрянет на каком-либо месте ландшафта в одном мини-пакете, можно будет запустить другой мини-пакет с другим ландшафтом, и вполне вероятно, что алгоритм сможет двигаться дальше. Данная техника предотвращает застревание алгоритма на определённых участках ландшафта, особенно на ранних этапах тренировки.
Второе усовершенствование алгоритма градиентного спуска — накопление импульса (Momentum)
Динамическая корректировка количества обновлений
Один из интересных аспектов алгоритма градиентного спуска связан с его поведением на крутых уклонах. Так как градиент на крутых уклонах большой, алгоритм в этих местах может делать большие шаги, между тем, именно здесь вы на самом деле хотите перемещаться медленно и осторожно. Это может привести к тому, что алгоритм будет «скакать» назад и вперёд, замедляя тем самым процесс тренировки.
В идеале частоту обновлений хочется изменять динамически, чтобы алгоритм мог подстраиваться под изменения окружающего ландшафта. Если уклон очень крутой, алгоритм должен притормаживать. Если склон довольно пологий, скорость вполне можно повысить, и так далее.
Алгоритм градиентного спуска обновляет веса на каждом шаге, используя в качестве параметров значения градиента и скорости обучения. Таким образом, для изменения количества обновлений можно выполнить два действия:
Скорректировать значение скорости обучения.
SGD с функцией накопления импульса и обычный SGD
Первое действие, то есть корректировка градиента, выполняется с помощью функции накопления импульса.
В SGD учитывается только текущее значение градиента, а значения всех прошлых градиентов игнорируются. Это означает, что, если алгоритм внезапно натолкнётся на аномалию на кривой потерь, он может пойти не по нужной траектории.
С другой стороны, при использовании SGD с накоплением импульса учитываются значения прошлых градиентов, общее направление сохраняется, и траектория остаётся правильной. Это позволяет использовать знания об окружающем ландшафте, полученные алгоритмом до того, как он добрался до данной точки, и смягчить эффект аномалии кривой потерь.
Сразу встаёт вопрос — как далеко можно углубляться в прошлое? Чем глубже мы погрузимся в прошлое, тем меньше вероятность воздействия аномалий на конечный результат.
И второй вопрос — все ли градиенты из прошлого можно расценивать одинаково? Вполне логично рассудить, что вещи из недавнего прошлого должны иметь более высокую значимость, чем вещи из далёкого прошлого. Поэтому, если изменение ландшафта представляет собой не аномалию, а естественное структурное изменение, на такие изменения нужно реагировать соответственно и менять курс постепенно.
Функция накопления импульса использует при работе экспоненциальное скользящее среднее, а не текущее значение градиента.
Переходы через овраги с помощью функции накопления импульса
Функция накопления импульса помогает решить проблему узкого оврага с патологическим искривлением, градиент которого, с одной стороны, очень большой для одного весового параметра, а, с другой стороны, очень маленький для другого параметра.
Функция накопления импульса помогает преодолевать овраги
С помощью функции накопления импульса можно сгладить зигзагообразные скачки алгоритма SGD.
Для первого параметра с крутым уклоном большое значение градиента приведёт к резкому повороту от одной стороны оврага к другой. Однако на следующем шаге такое перемещение будет скомпенсировано резким поворотом в обратном направлении.
Если же взять другой параметр, мелкие обновления на первом шаге будут усиливаться мелкими обновлениями на втором шаге, так как эти обновления имеют то же направление. И именно такое направление вдоль оврага будет направлением, в котором необходимо двигаться алгоритму.
Вот некоторые примеры алгоритмов оптимизации, использующих функцию накопления импульса в разных формулах:
SGD с накоплением импульса
Ускоренный градиент Нестерова
Третье усовершенствование алгоритма градиентного спуска — изменение скорости обучения (на базе значения градиента)
Как уже было сказано выше, вторым способом изменения количества обновлений параметров является изменение скорости обучения.
До сих пор на разных итерациях мы сохраняли скорость обучения постоянной. Кроме того, при обновлении градиентов для всех параметров использовалось одно и то же значение скорости обучения.
Однако, как мы уже убедились, градиенты с различными параметрами могут довольно ощутимо отличаться друг от друга. Один параметр может иметь крутой уклон, а другой — пологий.
Мы можем использовать это обстоятельство, чтобы адаптировать скорость обучения к каждому параметру. Чтобы выбрать скорость обучения для данного параметра, мы можем обратиться к прошлым градиентам (для каждого параметра отдельно).
Данная функциональность реализована в нескольких алгоритмах оптимизации, использующих разные, но похожие методы, например Adagrad, Adadelta, RMS Prop.
Например, метод Adagrad возводит в квадрат значения прошлых градиентов и суммирует их в равных весовых пропорциях. Метод RMSProp также возводит в квадрат значения прошлых градиентов, но приводит их к экспоненциальному скользящему среднему, тем самым придавая более высокую значимость последним по времени градиентам.
После возведения градиентов в квадрат все они становятся положительными, то есть получают одно и то же направление. Таким образом, перестаёт действовать эффект, проявляющийся при применении функции накопления импульса (градиенты имеют противоположные направления).
Это означает, что для параметра с крутым уклоном значения градиентов будут большими, и возведение таких значений в квадрат даст ещё большее и всегда положительное значение, поэтому такие градиенты быстро накапливаются. Для погашения данного эффекта алгоритм рассчитывает скорость обучения посредством деления накопленных квадратов градиентов на коэффициент с большим значением. За счет этого алгоритм “притормаживает” на крутых уклонах.
Рассуждая аналогично, если уклоны небольшие, накопления также будут небольшими, поэтому при вычислении скорости обучения алгоритм разделит накопленные квадраты градиентов на коэффициент с меньшим значением. Таким образом, скорость обучения на пологих склонах будет увеличена.
Некоторые алгоритмы оптимизации используют сразу оба подхода — изменяют скорость обучения, как это описано выше, и меняют градиент с помощью функции накопления импульса. Так поступает, например, алгоритм Adam и его многочисленные варианты, а также алгоритм LAMB.
Четвёртое усовершенствование алгоритма градиентного спуска — изменение скорости обучения (на базе тренировочной выборки)
В предыдущем разделе скорость обучения менялась на основе градиентов параметров. Плюс к этому, мы можем менять скорость обучения в зависимости от того, как продвигается процесс тренировки. Скорость обучения устанавливается в зависимости от эпохи тренировочного процесса и не зависит от параметров модели в данной точке.
На самом деле эту задачу выполняет не оптимизатор, а особый компонент нейронной сети, называемый планировщиком. Я упоминаю здесь об этом только для полноты картины и для того, чтобы показать связь с техниками оптимизации, о которых мы здесь говорили. Саму же эту тему логично осветить в отдельной статье.
Заключение
Мы ознакомились с основными приёмами, используемыми методиками оптимизации на базе алгоритма градиентного спуска, рассмотрели причины их использования и взаимосвязи друг с другом. Представленная информация позволит лучше понять принцип действия многих специфических алгоритмов оптимизации. А если вы хотите получить больше знаний — обратите внимание на наш расширенный курс по машинному и глубокому обучению, в рамках которого вы научитесь строить собственные нейронные нейронные сети и решать различные задачи с их помощью.
Узнайте, как прокачаться и в других специальностях или освоить их с нуля:
Профессия Data Scientist
Профессия Data Analyst
Курс по Data Engineering
Курс «Алгоритмы и структуры данных»
Другие профессии и курсы
ПРОФЕССИИ
Профессия Fullstack-разработчик на Python
Профессия QA-инженер на JAVA
Профессия Этичный хакер
Профессия C++ разработчик
Профессия Разработчик игр на Unity
Профессия iOS-разработчик с нуля
Профессия Android-разработчик с нуля
КУРСЫ
Курс по Machine Learning
Курс «Machine Learning и Deep Learning»
Курс «Математика для Data Science»
Курс «Математика и Machine Learning для Data Science»
Стэнфордский курс: лекция 7. Обучение нейросетей, часть 2
В шестой лекции мы начали рассказывать про обучение нейросетей: выяснили, как выбрать функцию активации, подготавливать данные, настраивать параметры и следить за процессом. Сегодня мы продолжим обсуждение и подробнее рассмотрим оптимизацию, регуляризацию и передачу обучения.
Хотя мы уже рассмотрели наиболее важные аспекты обучения нейросетей, стоит взять на заметку ещё несколько интересных и полезных стратегий. Как упоминалось ранее, для получения хороших и точных результатов важно применять оптимизацию (добиваться минимального значения функции потерь) и регуляризацию (упрощать модель, чтобы избежать переобучения). Выясним, как именно эти процессы выглядят на практике.
Оптимизация: подводные камни
Мы говорили, что функция потерь показывает, насколько хорошо или плохо выбранные веса справляются с поставленной задачей (например, с классификацией изображений). В двумерном пространстве значения функции можно представить в виде цветного «ландшафта», где наиболее тёплый регион (красный) — наименьшая величина потерь, к которой мы стремимся; а холодный (синий) — наибольшая. Рассмотрим простой пример оптимизации для двух значений функции потерь: W_1 и W_2.
Самый тривиальный алгоритм оптимизации — стохастический градиентный спуск, реализуемый всего в три строки кода. Он обновляет параметры, вычисляя отрицательное направление градиента, которое соответствует наибольшему уменьшению функции потерь. Выполняя эту операцию много раз, мы, возможно, достигнем красного региона, получим минимум ошибок и будем счастливы.
Но, к сожалению, на практике метод сопровождается множеством проблем. Представим, что мы поменяли одно из значений, например, W_2. При этом наши потери стали уменьшаться очень быстро. Тогда мы меняем значение W_1, но теперь прогресс идёт гораздо медленнее. Выводя это на график, мы увидим, что функция потерь более чувствительна к изменениям в вертикальном направлении, чем в горизонтальном. Стохастический градиентный спуск будет выглядеть зигзагообразно — и это не очень хорошо, поскольку прогресс оптимизации слишком медленный.
Ещё одна распространённая проблема — локальные минимумы и седловые точки. Если функция имеет форму сложной кривой, как показано на рисунке ниже, градиентный спуск может «застрять» в одном из её углублений — локальном экстремуме.
Трудности возникают также из-за зашумлённых данных, которые могут в большом количестве оказаться в пакете образцов, обычно использующемся для стохастического градиентного спуска.
Улучшенная оптимизация
Глядя на вышеупомянутые затруднения, возникает логичный вопрос: какой же алгоритм оптимизации поможет их избежать?
SGD + Momentum
К счастью, есть очень простая стратегия, решающая большинство проблем. Её идея в том, чтобы сохранить скорость градиентного спуска, добавив для этого некоторый импульс (momentum). Теперь вместо простого итеративного вычисления градиента мы прибавляем к нему подсчёт скорости и дописываем две строки в наш код.
Таким образом, мы будем двигаться в направлении изменения скорости, а не в направлении градиента. Гиперпараметр rho соответствует трению, как при движении по твёрдой поверхности.
Что же теперь произойдёт в локальном минимуме или седловой точке? Здесь можно провести аналогию с шариком, который катится с горки. Как только он дойдёт до точки, где градиент равен нулю, он по-прежнему сохранит некоторую скорость и будет двигаться дальше.
Nesterov Accelerated Gradient
Одна из разновидностей этого алгоритма — ускоренный градиент Нестерова (Nesterov Accelerated Gradient). Посмотрим на рисунок ниже, где градиентный спуск стартует из красной точки. Мы знаем, что импульс собирается приблизить нас к вершине зеленой стрелки. Вместо того, чтобы оценивать градиент в текущей позиции (красная точка), мы оцениваем градиент сразу в новой позиции. Так мы найдём точку в окрестности, где окажемся в следующий момент.
Ниже можно увидеть, как это выглядит в виде формул и кода. На графике показано сравнение обычного градиентного спуска (чёрная линия), градиентного спуска с импульсом (синяя линяя) и ускоренного градиента Нестерова (зелёная линия).
AdaGrad
Другой популярный метод оптимизации называется AdaGrad. На каждом шаге мы вычисляем промежуточную сумму всех квадратов градиентов, которые считаются во время обучения. Полученное значение используется для нормализации — мы просто делим параметры на корень из суммы квадратов.
Вспомним ситуацию, когда наша функция слишком быстро менялась в одном направлении и медленно в другом. Это значит, что у одной из координат было большое значение градиента, а у другой — маленькое. Если мы возьмём корень из суммы квадратов небольшого градиента, то будем делить параметры на маленькое число и тем самым ускорим движение в медленном направлении. С большими значениями будет ровно наоборот, что в теории должно сгладить зигзагообразную кривую. Но здесь же кроется загвоздка — если значения градиента постоянно растут, то мы начнём делить параметры на всё большие числа и шаги будут становиться всё медленнее.
RMSProp
Усовершенствованный алгоритм, называемый RMSProp, отчасти решает эту проблему. Вместо простого суммирования он использует скользящее среднее квадратов градиентов, что фактически уменьшает значение grad_squared и не даёт оптимизации замедляться. На рисунке ниже показана реализация RMSProp.
Adam
Adam объединяет в себе преимущества RMSProp, AdaGrad и SGD+Momentum. Сначала мы выполняем оценку первого импульса и взвешенной суммы градиентов, а затем оцениваем второй импульс и квадрат градиентов. Первый импульс играет роль скорости, а второй служит для оптимизации параметров.
Изначально оба параметра равны нулю, поэтому к ним добавляется корректирующее смещение, чтобы избежать слишком большого шага в самом начале.
На анимациях ниже продемонстрировано, как ведут себя различные алгоритмы оптимизации. Можно увидеть, что обычный SGD работает гораздо медленнее всех упомянутых методов. Справа показано поведение в седловой точке, быстрее всего которую преодолевает AdaDelta — ещё одна вариация AdaGrad, очень похожая на Adam за исключением того, что в ней отсутствует импульс.
Оптимизация второго порядка
Все алгоритмы, описанные выше, относятся к оптимизации первого порядка, поскольку для градиента нам необходимо считать только первые производные (якобиан). Но существует также оптимизация второго порядка, учитывающая как первую, так и вторую производные. В ней используется приближение Тейлора, которое локально аппроксимирует функцию к квадратичной и позволяет быстрее достичь минимума.
Обобщая этот подход в многомерное пространство, мы получим так называемый шаг Ньютона. В нём сначала вычисляется гессиан (вторые производные), а затем берётся обратная матрица для более быстрого перехода к минимуму квадратичной аппроксимации нашей функции.
Однако приведенное выше уравнение нецелесообразно для большинства приложений глубокого обучения, поскольку вычисление (и инвертирование) гессиана — очень дорогостоящий процесс. Поэтому существующие методы пытаются аппроксимировать обратный гессиан. Среди них наиболее популярным является L-BFGS, который использует для этого информацию о нескольких последних значениях градиента.
На практике оптимизация второго порядка редко применяется к масштабным нейронным сетям. Вместо неё используются стандартные алгоритмы первого порядка, основанные на градиентном спуске, потому что они проще вычисляются и легче масштабируются.
Ансамблевые методы
Иногда кажется, будто мы уже нашли подходящий алгоритм оптимизации и достигли хорошей точности, но этого всё ещё недостаточно. Что же можно сделать, чтобы модель работала лучше?
На помощь придёт способ «одна голова — хорошо, две — лучше, а ещё лучше — десять!», который также известен как ансамблевый метод. Вместо того, чтобы использовать одну нейросеть, можно независимо обучить несколько архитектур, а во время тестирования взять их усреднённый результат. Это помогает уменьшить переобучение и немного улучшить эффективность модели (обычно на пару процентов). Также вместо разных алгоритмов можно взять несколько версий одной нейросети.
Выбор скорости обучения
Ни один метод не достигнет хороших результатов без правильной настройки гиперпараметров, самый важный из которых — скорость обучения (learning rate). В процессе оптимизации глубоких сетей скорость обычно уменьшают с течением времени. Это связано с тем, что при высоких значениях наша система будет содержать слишком много кинетической энергии. Вектор параметров не сможет приблизиться к минимуму функции потерь и начнёт хаотично перемещаться вокруг него.
Заранее предугадать, когда следует уменьшить скорость обучения, может быть непросто: если она будет затухать медленно, вы потратите слишком много вычислительных ресурсов и долго не будете наблюдать улучшений. Но чересчур агрессивное затухание приведёт к преждевременной остановке обучения.
Существует три распространенных способа снижения learning rate:
Пошаговое затухание: снижение скорости на некоторую величину каждые несколько эпох. Типичные варианты: уменьшение в два раза каждые 5 эпох или в 0.1 раз каждые 20 эпох. Конкретные цифры сильно зависят от задачи и модели.
Экспоненциальное затухание и затухание 1/t: имеют вид, показанный на рисунке ниже. Здесь α0 и k — гиперпараметры, а t — номер итерации.
Регуляризация Dropout
Регуляризация используется для упрощения модели, чтобы избежать переобучения и заставить её корректно работать на новых данных. Мы уже говорили о методах L1, L2 и Elastic net. Но чаще всего в машинном обучении применяется эффективный и простой алгоритм Dropout.
Каждый раз, выполняя прямой проход по слою, мы устанавливаем некоторые значения активаций нулевыми (нейроны выбираются случайно). В результате мы получим несколько «подсетей» и будем поочерёдно оценивать их эффективность, что делает dropout похожим на ансамбль методов.
Самая простая реализация для трёхслойной нейросети выглядит следующим образом:
«»» Vanilla Dropout: не рекомендуется использовать на практике «»»
p = 0.5 # вероятность оставить нейрон активным. Чем выше, тем меньше dropout
# прямой проход по трёхслойной нейросети
H1 = np.maximum(0, np.dot(W1, X) + b1)
На практике при дополнения датасета также используется смещение пикселей, добавление дисторсии, метод главных компонент, увеличение/уменьшение яркости, контрастности, резкости и другие способы. Здесь можно дать волю своей фантазии.
Передача обучения
При недостаточном количестве исходных образцов также стоит попробовать метод передачи обучения или Transfer Learning. Его суть заключается в том, что сначала мы обучаем нейросеть на большом наборе данных (например, ImageNet), а затем подстраиваем её под свой датасет. Для этого необходимо заново инициализировать матрицу весов последнего слоя и переобучить его, чтобы сеть адаптировалась под наши потребности.
Но это работает, только если данных действительно совсем немного. В других случаях, возможно, потребуется переобучить несколько слоёв или всю нейросеть.
Мы заново обучаем только последние слои, оставляя предыдущие без изменений. Это связано с тем, что в первых слоях нейросети содержатся более общие признаки, в то время как последние отвечают за специфичные особенности.
Практический пример
В качестве учебного примера к лекции предлагаем посмотреть, как один из методов минимизации работает на практике. Предположим, что у нас есть четырёхмерная функция. Требуется найти такую комбинацию входных параметров, при которых её значение минимально. В подобных задачах минимум часто нужно искать не в бесконечном пространстве, а в ограниченной области. Например, масса физически не может быть отрицательной, поэтому её значения надо искать только в положительном диапазоне.
Чтобы провести эксперимент, зададим целевую функцию и ограничения на область поиска.
Задача.
Решите проблему оптимизации, описанную ниже, с помощью любой Python-библиотеки на ваш выбор. Используйте два способа:
Сравните результаты методов 1) и 2). Что вы можете сказать о них?
В пакете SciPy реализован отличный метод минимизации trust-constr, который умеет одновременно использовать якобиан, гессиан и ограничения области поиска. В машинном обучении аналитический вид функции неизвестен, следовательно, неизвестен и вид её производных. Поэтому вторую конфигурацию метода мы зададим «с завязанными глазами», выполняя только численное дифференцирование. Запустив прилагаемый код примера, вы получите схожие результаты в обоих случаях.
Мы выяснили, какие бывают стратегии оптимизации, чем полезна регуляризация и как можно использовать уже обученные нейросети для новых проектов. Делитесь в комментариях, пробовали ли вы применять описанные методы на практике и какие из них оказались наиболее эффективными. Предлагайте идеи для новых статей и задавайте вопросы, если что-то непонятно — мы обязательно ответим.
А на следующей лекции мы расскажем о программном обеспечении, которое используется в машинном обучении.
Следующие лекции (список будет дополняться по мере появления материалов):
С оригинальной лекцией можно ознакомиться на YouTube.