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

Пока все хорошо, но что, если у нас нет линейно разделимых данных? Что, если мы хотим лучших результатов? Вот тут-то и появляются нейронные сети. Давайте посмотрим на архитектуру нейронных сетей:

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

Есть несколько вещей, которые следует учитывать, когда дело доходит до принятия решения о том, какой будет архитектура вашей нейронной сети — как слои будут связаны и размер сети. Давайте сначала посмотрим, как мы соединяем слои.

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

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

y = Wx + b

Итак, что происходит, когда мы помещаем дополнительный слой между входным и выходным векторами. Как это влияет на наше уравнение? Самое простое решение — рассмотреть следующее: если мы получим вектор скрытого слоя как HL = Wx + b, мы можем умножить его на другой набор весов и добавить еще один набор смещений, чтобы получить выходной слой:

Y = W1(Wx+b) + b1

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

Y = W1(Wx+b) + b1

Y = W2x+b + b1

Y = W2x + b2

Что мало чем отличается от нашей линейной модели. Разница лишь в значениях весов, которые в любом случае предполагается набирать в процессе тренировки. По сути, у нас все еще есть линейная модель. То, что мы сделали здесь, похоже на нормальную функцию y=x + 1, умножение ее на 2, добавление 1 и получение нелинейной функции. Очевидно, что это не так.

Итак, как мы моделируем нелинейные функции? Вот тут-то и появляются функции активации.

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

Y = W1(f(Wx + b)) + b1

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

У всего вышеперечисленного есть свои плюсы и минусы, однако в последние годы ReLU стал самым популярным выбором. Нет правильного ответа на вопрос «Какой из них я должен использовать для своей модели?». Чтобы выяснить, какой из них лучше всего подходит для ваших данных, вы можете попробовать поэкспериментировать с различными функциями активации.

Размеры нейронной сети

Чтобы измерить размер нейронной сети, учитываются две вещи: количество нейронов и количество обучаемых параметров. На рис. 1 у нас есть 5 + 2 = 7 нейронов (мы пренебрегаем количеством нейронов из входного вектора), 3 × 5 + 5 × 2 = 25 весов и 5 + 2 = 7 смещений, всего 25+ 7=32 обучаемых параметра. Для сравнения, современные сверточные сети содержат порядка 100 миллионов параметров и около 10–20 слоев (отсюда и глубокое обучение). Естественно возникает вопрос: «Так насколько большой должна быть моя нейронная сеть?». Давайте сначала посмотрим, что означает изменение количества нейронов для нашей модели. Здесь я подготовил 3 примера для моделей машинного обучения с разным количеством нейронов.

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

Несмотря на то, что третья модель, кажется, идеально представляет данные, на самом деле она сильно переобучила их. Это означает, что наша модель стала очень хорошо классифицировать точки, на которых она была обучена, но она, вероятно, не будет работать хорошо вне выборки — то есть на новых точках данных. Это происходит потому, что чем больше у нас нейронов, тем более сложные функции может построить наша модель для разделения классов в наших данных. Проблема возникает из-за того, что наши исходные данные, скорее всего, будут иметь какой-то «шум» (фрагменты информации, которые ничего не приносят в таблицу — выбросы, которые следует игнорировать). Если мы будем рассматривать этот шум как важный фактор, вносящий вклад в наши данные, а не как выбросы, мы рискуем неправильно классифицировать множество других точек данных. В примере на рис. 4 наша модель создала довольно сложную функцию, чтобы правильно классифицировать две дополнительные зеленые точки в нашем наборе обучающих данных. Что происходит, когда мы запускаем тесты на реальных данных после этого? Мы потенциально неправильно классифицируем много точек.

Хорошо, значит ли это, что мы должны стремиться к нейронным сетям с меньшим количеством нейронов? Ну нет. К счастью, есть и другие способы борьбы с переоснащением, такие как регуляризация и отсев. Вам может быть интересно, зачем нам прилагать дополнительные усилия по добавлению регуляризации в смесь, если мы можем просто сделать нашу модель с меньшим количеством нейронов. Однако большая проблема небольших нейронных сетей заключается в том, что их сложнее обучать. Их функции потерь имеют относительно мало локальных минимумов. Большинство из этих локальных минимумов плохи, но к ним легко сходиться. Это означает, что мы с большей вероятностью окажемся на локальном минимуме с высокой частотой ошибок. С другой стороны, более крупные нейронные сети имеют гораздо больше локальных минимумов, и большинство из них имеют относительно низкие ошибки. Подводя итог, выбрав более крупную нейронную сеть, мы перестанем так сильно зависеть от случайного шанса инициализации начального веса, чтобы получить хороший конечный результат. Итак, в конце концов, чтобы решить, насколько глубоко мы хотим использовать нашу нейронную сеть, мы должны ответить, насколько глубоко мы готовы залезть в наши карманы, поскольку более крупные нейронные сети требуют большой вычислительной мощности.

Я упомянул регуляризацию как способ уменьшить переоснащение. Что такое регуляризация и как она работает?

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

Вы могли заметить, что для Zs, близких к 0, функция довольно линейна (рис. 6).

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

Если мы возьмем, например, регуляризацию L2, стоимость там вычисляется следующим образом:

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

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

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

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

Источники:

[1] Переоснащение и недооснащение https://satishgunjal.com/underfitting_overfitting/

[2] Регуляризация https://developers.google.com/machine-learning/crash-course/regularization-for-simplicity/l2-regularization

[3] https://towardsdatascience.com/regularization-in-machine-learning-76441ddcf99a

София Пенева — разработчик программного обеспечения в Lab08, работающая над платформой UserTribe. Ранее она работала в SAP, а прошлым летом стала одним из стажеров Google, работая над проектом алгоритма Youtube. Имеет опыт работы с C++, PHP, Python, Java, MySQL, MongoDB и другими. Что касается глубокого обучения, у Софи теперь есть опыт работы с Tensorflow. Софи интересовалась ML в течение достаточно долгого времени, проявляя большую страсть и участие в том, чтобы запачкать руки. Надеемся, вам понравился результат ее энтузиазма!

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