Поиск по сайту Поиск

Стэнфордский курс: лекция 6. Обучение нейросетей, часть 1

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

Предыдущие лекции:

Лекция 1. Введение
Лекция 2. Классификация изображений
Лекция 3. Функция потерь и оптимизация
Лекция 4. Введение в нейронные сети
Лекция 5. Свёрточные нейронные сети

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

  1. Однократная настройка
    Сюда входят: выбор функции активации, предварительная обработка данных, инициализация весов, регуляризация, градиентная проверка.
  2. Динамика обучения
    Отслеживание процесса обучения, оптимизация и обновление гиперпараметров.
  3. Оценка
    Использование ансамблевых методов.

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

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

  1. Однократная настройка

Сюда входят: выбор функции активации, предварительная обработка данных, инициализация весов, регуляризация, градиентная проверка.

  1. Динамика обучения

Отслеживание процесса обучения, оптимизация и обновление гиперпараметров.

  1. Оценка

Использование ансамблевых методов.

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

Функция активации

Ранее мы выяснили, что в каждый слой нейросети поступают входные данные. Они умножаются на веса полносвязного или свёрточного слоя, а результат передаётся в функцию активации или нелинейность. Мы также говорили о сигмоиде и ReLU, которые часто используются в качестве таких функций. Но список возможных вариантов не ограничивается только ими. Какой же следует выбирать?

Рассмотрим наиболее популярные функции активации и обсудим их преимущества и недостатки.

Сигмоида

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

Но если внимательнее присмотреться к сигмоиде, можно заметить несколько проблем. 

1. Насыщенные нейроны могут «убить» градиент. Возьмём сигмоидный узел вычислительного графа и передадим в него входные данные X. Когда мы делаем обратный проход, восходящий градиент равен dL/d𝜎, а локальный — dL/d𝜎 * d𝜎/dx

Что же произойдёт, если X будет равен −10? Градиент станет нулевым, поскольку все большие отрицательные значения находятся на прямом участке сигмоидной функции. Таким образом, во все последующие узлы будут передаваться нулевые производные — это и «убивает» градиентный поток.

А если X = 0? В этом случае всё будет в порядке, как и для других близких к нулю значений. А вот при X = 10 градиент снова обнулится. Поэтому сигмоида не работает для слишком высоких положительных или отрицательных данных.

2. Выходные значения сигмоиды не центрированы нулем. Пусть исходные данные полностью положительны — что тогда станет с градиентами во время обратного распространения? Они все будут либо положительными, либо отрицательными (в зависимости от градиента f). Это приведёт к тому, что все веса при обновлении также будут либо увеличены, либо уменьшены, и градиентный поток станет зигзагообразным.

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

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

Тангенс

Тангенс очень похож на сигмоиду, но обладает двумя существенными отличиями: он преобразует данные в диапазон [-1, 1] и имеет нулевое центрирование, что исключает вторую проблему сигмоиды. Значения градиента при обратном распространении по-прежнему могут обнуляться, тем не менее, использование тангенса обычно более предпочтительно.

ReLU

ReLU или Rectified Linear Unit стала довольно популярной в последние годы. Она вычисляет функцию f(x) = max(0,x), то есть просто выдаёт значения «ноль» и «не ноль». Это решает проблему обнуления градиента для положительных чисел. Кроме того, ReLU очень просто вычисляется: примерно в шесть раз быстрее сигмоиды и тангенса. Однако, в ней снова отсутствует нулевое центрирование.

Другой очевидный недостаток — градиент по-прежнему «умирает» при отрицательных входных данных. Это может привести к тому, что половина нейронов будет неактивна и не сможет обновляться. 

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

Leaky ReLU

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

При этом функцию по-прежнему легко вычислить. То есть, она решает практически все перечисленные проблемы. Одной из её разновидностей является PReLU, которая выглядит как f(x) = max(𝛼x, x)

ELU

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

Maxout

Maxout выбирает максимальную сумму из двух наборов весов, умноженных на исходные данные с учётом смещения. Тем самым он обобщает ReLU и leaky ReLU, не обнуляя градиент. Но, как можно догадаться по виду функции, maxout требует удвоения параметров и нейронов.

Подводя итог: используйте ReLU, можете попробовать взять leaky ReLU/Maxout/ELU. На тангенс и сигмоиду лучше не рассчитывать.

Подготовка данных

Существует три наиболее распространённых способа предварительной обработки данных. Будем полагать, что данные X — это матрица размером [NxD]

1. Вычитание среднего. Чтобы избежать смещения данных и сделать их симметричными относительно нуля, из каждого элемента вычитается среднее значение. Это помогает предотвратить ситуации, когда все исходные числа оказываются только положительными или отрицательными.  В NumPy операция выглядит как X -= np.mean(X, axis = 0). В частности, при обработке изображений можно вычитать одно значение из всех пикселей (например, X -= np.mean(X)) или делать это отдельно по каждому из трёх цветовых каналов.

2. Нормализация. Изменение данных таким образом, чтобы они все были приблизительно одного масштаба. Один из вариантов — разделить каждое измерение на его стандартное отклонение: (X /= np.std(X, axis = 0)). Другой вариант — нормализовать каждое значение так, чтобы min и max были равны -1 и 1 соответственно. Нормализацию следует применять только в том случае, если исходные данные имеют разные форматы или единицы измерения. У изображений значения пикселей не выходят за пределы диапазона от 0 до 255, поэтому для них нет необходимости выполнять нормализацию.

Инициализация весов

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

Как не нужно делать: задавать веса нулевыми. Это приведёт к тому, что абсолютно все нейроны будут вести себя одинаково — совсем не то, что мы хотим получить. Нейросеть должна обучаться разным признакам.

Небольшие случайные величины. Более удачный вариант — присвоить весам маленькие значения. Тогда все нейроны будут уникальными и в процессе обучения постепенно интегрируются в различные части сети. Реализация может выглядеть так: W = 0.01* np.random.randn(D,H). Метод randn(n) формирует массив размера n х n, элементами которого являются случайные величины, распределённые по нормальному закону с математическим ожиданием 0 и среднеквадратичным отклонением 1 (распределение Гаусса). Недостаток этого способа в том, что он неплохо работает для небольших архитектур, но гораздо хуже справляется с громоздкими нейросетями.

Калибровка с помощью 1/sqrt(n). Проблема вышеупомянутого метода состоит в том, что дисперсия случайных величин растёт с числом нейронов. Чтобы избежать этого, можно масштабировать веса, поделив их на корень из количества входов: w = np.random.randn(n) / sqrt(n). Это гарантирует, что все нейроны сети изначально будут иметь примерно одинаковое выходное распределение.

Также можно использовать вариант w = np.random.randn(n) * sqrt(2.0/n), который был предложен в одном из исследований. Он приводит к наиболее удачному распределению нейронов, поэтому на практике рекомендуем использовать именно его.

Пакетная нормализация

Метод, известный также как batch normalization, решает множество проблем при инициализации, заставляя все активации (выводы) принимать единичное гауссово распределение в начале обучения. 

Как же это работает? Рассмотрим небольшое число выводов нейронов на каком-либо слое. Пусть в функцию активации поступает вектор размерности d: x = (x(1),…,x(d)). Нормализуем его по каждой из размерностей:

Где E(x) — математическое ожидание, D(x) — дисперсия, которые вычисляются по всей обучающей выборке. Таким образом, вместо инициализации весов можно использовать эту простую дифференцируемую функцию и получить нормальное распределение на каждом слое.

Пакетная нормализация обычно применяется между слоями (полносвязными или свёрточными) и функциями активации.

Это очень полезный алгоритм, который часто применяется в современном машинном обучении. Нейросети, использующие batch normalization, значительно более устойчивы к плохой инициализации.

За нейросетью глаз да глаз

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

Напомним, что эпоха — один «проход» данных через нейросеть, после которого обновляются веса с помощью градиентного спуска. Упрощённо это выглядит следующим образом:

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

Оптимизация гиперпараметров

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

— начальная скорость обучения;

— график затухания скорости обучения (например, постоянная затухания);

— сила регуляризации.

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

Learning rate — одно из самых важных значений. Попробуйте поэкспериментировать с различными вариантами и построить графики потерь. На рисунке ниже слева показаны эффекты, возникающие при изменении скорости обучения, а справа — типичная функция потерь при обучении небольшой нейросети на наборе данных CIFAR-10.

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

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

Итоги

Кратко изложим всё, что мы узнали про обучение нейросетей из сегодняшней лекции:

— используйте функцию активации ReLU;

— выполняйте предварительную обработку данных (для изображений: вычитайте среднее значение);

— масштабируйте веса при инициализации;

— применяйте пакетную нормализацию;

— следите за процессом обучения;

— оптимизируйте гиперпараметры с помощью случайного поиска.

⌘⌘⌘

На следующей лекции мы расскажем ещё о нескольких важных шагах обучения, узнаем про ансамблевые методы и разберёмся, как выполнять передачу обучения (transfer learning) и точную настройку (fine tuning). Пробовали ли вы самостоятельно обучать нейросети? Были ли у вас свои  хитрости, или вы полагались на установки по умолчанию? Делитесь с нами успехами и не забывайте задавать вопросы, если что-то непонятно.

Следующие лекции (список будет дополняться по мере появления материалов):

Лекция 7. Обучение нейросетей, часть 2
Лекция 8. ПО для глубокого обучения
Лекция 9. Архитектуры CNN
Лекция 10. Рекуррентные нейронные сети

С оригинальной лекцией можно ознакомиться на YouTube.

Виды облачных хранилищ

Зачем мне использовать облачные технологии и какое облачное хранилище выбрать? Наверняка подобными вопросами вы задавались хотя бы раз в жизни....
Read More

Гайд по Ubuntu: узнаём версию, устанавливаем приложения

Мы продолжаем серию постов об операционной системе Linux. В этой статье расскажем, как узнать версию Ubuntu, и поговорим об установке...
Read More

Как узнать URL-адрес сайта

У каждого сайта есть свой уникальный адрес, используя который вы можете как посещать страницы, так и делиться ими с друзьями....
Read More

Своими руками за пару часов: как создать сайт для учителя

Сайт для педагога — один из крутых способов продвигать свои услуги. И применений ему море: например, если вы частный специалист,...
Read More

Как придумать доменное имя для сайта

Известно, что правильно подобранный домен не только производит хорошее впечатление на пользователя, но и повышает узнаваемость бренда. В этой статье...
Read More

Linux-шпаргалка: команды терминала для новичков

Терминал и текстовые команды — главный способ управления операционной системой Linux, особенно, если речь идёт о сервере. И хоть слово...
Read More

Что такое процессор (CPU)

В этой статье мы рассмотрим, что такое процессор CPU, какие у него функции и из чего он состоит. (далее…)
Read More

Подборка лучших онлайн-сервисов для проверки сайта на вирусы

Заражение сайта вирусами — риск, который есть практически у каждого ресурса, что может привести к серьёзным последствиям: сбоям в работе...
Read More

Лучшие расширения для работы в Google Docs

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

IT-скороговорки: улучшаем дикцию, поднимаем настроение

Произносить скороговорки не только полезно для развития речевого аппарата, но ещё и очень весело! Мы придумали IT-скороговорки, которые прокачают вашу...
Read More