Перевод / Ali Tao Merchant Team - Инновации
Данная статья является переводом и дополнением технической статьи «От персептрона к глубоким нейронным сетям». Сообщение от: Ади Крис
Как инженер по машинному обучению, я некоторое время работал над глубоким обучением. Теперь, пройдя все последние курсы Эндрю Н. Г. по глубокому обучению на Coursera, я решил поместить часть того, что знаю об этой области, в сообщение в блоге. Я обнаружил, что запись — это эффективный способ затронуть тему. Кроме того, я надеюсь, что эта статья может быть полезна тем, кто хочет начать глубокое обучение.
Хорошо, давайте поговорим о глубоком обучении. О, подождите, прежде чем я перейду непосредственно к тому, что такое глубокое обучение или глубокие нейронные сети (ГНС), я хочу начать эту статью с простого вопроса, который, я надеюсь, даст нам лучшее представление о том, зачем нам нужна (глубокая) нейронная сеть. Кстати, я также буду работать с этой статьей наGithubОпубликуйте код, который позволит вам обучить модель глубокой нейронной сети для решения приведенной ниже проблемы XOR.
XOR проблема
Задача «исключающее ИЛИ» — это задача, в которой мы должны предсказать вывод логического элемента «исключающее ИЛИ» при наличии двух двоичных входов. Напоминаем, что функция XOR должна возвращать 1, если два входа не равны, и 0 в противном случае. В таблице 1 ниже перечислены все возможные входные и выходные данные функции XOR:
Теперь давайте построим набор данных и посмотрим, какова природа данных.
def plot_data(data, labels):
"""
argument:
data: np.array containing the input value
labels: 1d numpy array containing the expected label
"""
positives = data[labels == 1, :]
negatives = data[labels == 0, :]
plt.scatter(positives[:, 0], positives[:, 1],
color='red', marker='+', s=200)
plt.scatter(negatives[:, 0], negatives[:, 1],
color='blue', marker='_', s=200)
positives = np.array([[1, 0], [0, 1]])
negatives = np.array([[0, 0], [1, 1]])
data = np.concatenate([positives, negatives])
labels = np.array([1, 1,
Возможно, увидев график выше, мы могли бы захотеть пересмотреть, действительно ли эта проблема xor является простой. Как видите, наши данные не являются линейно разделимыми, поэтому некоторые известные линейные модели, такие как логистическая регрессия, могут быть не в состоянии классифицировать наши данные. Чтобы дать вам более четкое представление, ниже я нарисовал некоторые границы решений, построенные с использованием очень простой линейной модели:
Глядя на график выше, становится ясно, что нам нужен классификатор, который хорошо работает с нелинейно разделимыми данными. SVM с трюками ядра может быть хорошим вариантом. Однако в этой статье мы вместо этого создадим нейронную сеть и посмотрим, как эта нейронная сеть может помочь нам решить проблему XOR.
Что такое нейронная сеть?
Нейронная сеть или искусственная нейронная сеть — очень хороший аппроксиматор функции, в общих чертах основанный на том, как люди думают, как работает мозг. На приведенной ниже диаграмме показана аналогия между биологическими нейронами человека и искусственными нейронными сетями.
Не вдаваясь слишком глубоко в биологические нейроны, я дам краткое интуитивное введение в то, как биологические нейроны обрабатывают информацию. Наши нейроны получают сигналы через дендриты. Эти сообщения или сигналы затем передаются соме или телу клетки. Внутри ячейки вся информация будет агрегироваться для создания выходных данных. Когда суммарный результат достигает порога, нейрон срабатывает, и информация передается по аксону, а затем через его синапсы к другим связанным нейронам. Количество сигнала, передаваемого между нейронами, зависит от силы связи.
Весь описанный выше процесс используется искусственной нейронной сетью. Вы можете думать о дендритах как о взвешенных входных данных, основанных на синаптических взаимосвязях в искусственных нейронных сетях. Затем взвешенные входные данные объединяются в «ячейки» искусственной нейронной сети. Если сгенерированный выходной сигнал больше пороговой единицы, нейрон «запустится», и выходные данные этого нейрона будут переданы другим нейронам. Итак, вы можете видеть, что ИНС моделируется с использованием работы основных биологических нейронов.
Итак, как же работает эта нейронная сеть?
Чтобы понять, как работает эта нейронная сеть, давайте сначала рассмотрим очень простую искусственную нейронную сеть под названием Perceptron. Для меня Perceptron — один из самых элегантных алгоритмов, существующих в машинном обучении. Этот простой алгоритм, созданный в 1950-х годах, возможно, является отправной точкой для таких важных разработок в алгоритмах машинного обучения, как логистическая регрессия, машины опорных векторов и даже глубокие нейронные сети. Так как же работает персептрон? Мы будем использовать изображение, показанное ниже, в качестве отправной точки для обсуждения.
На изображении выше показан алгоритм персептрона с тремя входными данными x1, x2 и x3, а также нейронный блок, который может генерировать выходные значения. Чтобы сгенерировать результат, Розенблатт вводит простое правило, вводя понятие весов. Веса — это в основном действительные числа, представляющие важность каждого входа для выхода [1]. Описанный выше нейрон будет генерировать два возможных значения, 0 или 1, в зависимости от того, меньше или больше взвешенная сумма ∑ wjxj каждого входа определенного порога. Поэтому основная идея алгоритма персептрона состоит в том, чтобы узнать значение веса w, а затем умножить вес w на входной признак, чтобы решить, сработает нейрон или нет. Мы можем записать это, используя математическое выражение, подобное следующему:
Теперь мы можем изменить приведенную выше формулу, выполнив две вещи: во-первых, мы можем преобразовать формулу взвешенной суммы в скалярное произведение двух векторов w (веса) и x (входные данные), где w⋅x≡∑wjxj. Затем мы можем переместить порог на другой конец неравенства и заменить его новой переменной, называемой смещением b, где b≡-порог. Теперь, с этими изменениями, наше правило персептрона можно переписать как:
Теперь, когда мы вернем все обратно в архитектуру персептрона, у нас будет полная архитектура для одного персептрона, подобная этой:
Типичный однослойный персептрон использует ступенчатую функцию Хевисайда в качестве функции активации для преобразования результирующего значения в 0 или 1, тем самым классифицируя входное значение как 0 или 1. Как показано на рисунке ниже, ступенчатая функция Хевисайда будет выводить ноль для отрицательного значения. аргументы и положительный аргумент.
Ступенчатая функция Хевисайда особенно полезна в задачах классификации, если входные данные линейно разделимы. Однако напомню, что наша цель — найти классификатор, который хорошо работает с нелинейно разделимыми данными. Так что ни однослойный персептрон, ни ступенчатая функция Хевисайда здесь не имеют смысла. Позже, как вы увидите в следующем разделе, нам понадобится многослойный слой, состоящий из нескольких персептронов и нелинейной функции активации.
В частности, есть две основные причины, по которым мы не можем использовать ступенчатую функцию Хевисайда:
- В настоящее время одним из наиболее эффективных способов обучения многослойных нейронных сетей является использование градиентного спуска в сочетании с обратным распространением (мы вскоре обсудим оба метода). Требованием алгоритма обратного распространения является дифференцируемая функция активации. Однако ступенчатая функция Хевисайда недифференцируема при x = 0 и имеет производную от 0 в других местах. Это означает, что градиентный спуск не сможет добиться прогресса в обновлении веса.
- Напомним, что основная цель нейронной сети — узнать значения весов и смещений, чтобы модель могла выдавать прогнозы, максимально приближенные к реальным значениям. Для этого, как и во многих задачах оптимизации, нам нужны небольшие изменения весов или смещений, которые приводят лишь к небольшим соответствующим изменениям в выходных данных сети. Наличие функции, которая может генерировать только 0 или 1 (или «да» и «нет»), не поможет нам достичь этого.
функция активации
Функция активации является одним из наиболее важных компонентов нейронной сети. В частности, нелинейные функции активации необходимы по крайней мере по трем причинам:
- Это помогает нейронам учиться и понимать действительно сложные вещи.
- Они привносят в нашу сеть нелинейные свойства.
- Мы хотим, чтобы небольшие изменения в весе вызывали только соответствующие небольшие изменения в выходе сети.
Мы уже видели, что ступенчатая функция Хевисайда является примером функции активации, однако в этом конкретном разделе мы рассмотрим несколько нелинейных функций активации, обычно используемых в сообществе глубокого обучения. Кстати, более подробное объяснение функций активации, включая плюсы и минусы каждой нелинейной функции активации, можно найти в этих двух замечательных статьях Авинаша Шармы и Карпати.
Sigmoid Function
Сигмовидная функция, также известная как логистическая функция, представляет собой функцию, которая при заданных входных данных будет генерировать выходные данные в диапазоне (0,1). сигмовидную можно записать как:
На изображении выше показана форма сигмовидной функции. Как видите, это похоже на сглаженную версию ступенчатой функции Хевисайда. Однако сигмовидная функция предпочтительнее из-за многих факторов:
- Он по своей сути нелинейный.
- Теперь вместо функций, выводящих 0 и 1, мы можем дать функции, выводящие значение 0,67. Да, как вы могли догадаться, его можно использовать для представления значений вероятности.
- Все еще связанный с пунктом (2), теперь наши активации ограничены диапазоном, что означает, что он не взорвет эти активации.
Однако сигмовидная функция активации имеет некоторые недостатки: исчезающий градиент. Как видно из приведенного выше рисунка, когда входное значение z функции мало (движется в направлении -inf), выход сигмовидной функции будет близок к нулю. И наоборот, когда z велико (движется к +inf), выход сигмовидной функции будет близок к 1. Итак, что это значит? В этой области градиент будет очень мал или даже исчезнет. Исчезающие градиенты — большая проблема, особенно в глубоком обучении, потому что мы складываем несколько слоев этой нелинейности вместе, потому что даже большое изменение параметров первого слоя не меняет результат. Другими словами, сеть отказывается обучаться, и время, необходимое для обучения модели, обычно становится все медленнее и медленнее, особенно если используются алгоритмы градиентного спуска. Еще одним недостатком сигмовидной функции активации является то, что на практике вычисление показателя степени может быть дорогостоящим. Хотя, возможно, функция активации является лишь небольшой частью вычислений в глубокой сети по сравнению с умножением матриц и/или сверткой, так что это, вероятно, не будет большой проблемой. Тем не менее, я думаю, стоит упомянуть.
Tanh Function
Tanh или гиперболический тангенс — еще одна функция активации, обычно используемая в глубоких нейронных сетях. Свойства этой функции очень похожи на функцию Sigmoid, где она сжимает входные данные в хорошо ограниченный диапазон значений. В частности, при заданном значении tanh будет генерировать выходное значение от -1 до 1.
Как упоминалось ранее, функция активации тангенса имеет свойства, аналогичные сигмовидной функции. Он нелинейный и привязан к некоторому диапазону, в данном случае (-1, 1). Опять же, неудивительно, что Tanh имеет те же недостатки, что и Model S. Он страдает от исчезающих градиентов, и из-за математики нам нужно вычислить показатель степени, что обычно неэффективно в вычислительном отношении.
ReLu (Rectified Linear Unit)
Это ReLu, это функция активации, от которой не ожидается, что она будет лучше, чем Sigmoid и tanh, но на самом деле это так! На самом деле в лекции говорится использовать нелинейность ReLU по умолчанию. ReLu обладает хорошими математическими свойствами и очень эффективен в вычислительном отношении. Учитывая входное значение, ReLu будет генерировать 0, если вход меньше 0, иначе вывод будет таким же, как ввод. Математически это форма функции ReLu.
Теперь вы можете спросить: «Разве это не линейная функция? Почему мы называем ReLu нелинейной функцией?» Во-первых, давайте сначала разберемся, что такое линейная функция. Википедия говорит:
In linear algebra, a linear function is a map f between two vector spaces that preserves vector addition and scalar multiplication:
f(x + y) = f(x) + f(y) f(ax) = af(x)
Учитывая приведенное выше определение, мы видим, что max(0,x) — кусочно-линейная функция. Это кусочно, потому что оно линейно только в том случае, если мы ограничиваем его область определения до (-inf, 0] или [0, +inf). Однако она не является линейной во всей области. Например:
f (−1) + f (1) ≠ f (0)
Итак, теперь мы знаем, что ReLu — это нелинейная функция активации, обладающая хорошими математическими свойствами и более эффективная в вычислительном отношении по сравнению с сигмовидной или Tanh. Кроме того, известно, что ReLu «устраняет» проблему исчезающих градиентов. Однако у ReLu есть большой недостаток — «умирающий ReLu». Dying ReLu — это явление, при котором нейроны в сети умирают безвозвратно из-за их неспособности двигаться вперед.
Точнее, эта проблема возникает, когда нейрон генерирует нулевое значение активации по мере продвижения вперед, в результате чего его веса становятся нулевым градиентом. В результате, когда мы выполняем обратное распространение, веса этого нейрона никогда не будут обновляться, и конкретный нейрон никогда не будет активирован. Я настоятельно рекомендую посмотреть это видео-лекцию, в котором более подробно объясняется эта конкретная проблема и то, как избежать проблемы умирания ReLu. Пожалуйста, проверьте это!
О, еще одна вещь, которую я должен упомянуть о ReLu. Вы можете заметить, что, в отличие от сигмоида и тангенса, ReLu не ограничивает выходное значение. Поскольку обычно это не может быть большой проблемой, это может стать проблемой в другом варианте моделей глубокого обучения, таком как рекуррентные нейронные сети (RNN). В частности, бесконечные значения, генерируемые ReLu, могут привести к потенциальному взрыву вычислений в RNN до бесконечности без разумных весов. В результате обучение может быть очень нестабильным, так как небольшие смещения весов в неправильном направлении при обратном распространении могут разрушить активации при прямом проходе.
Как нейронные сети предсказывают и учатся?
Архитектура, изображенная на рисунке выше, называется многослойным персептроном (MLP). Как следует из названия, в MLP мы просто складываем несколько персептронов в несколько слоев. Описанная выше сеть имеет 3 слоя: входной слой, скрытый слой и выходной слой. Однако в сообществе глубокого обучения или нейронных сетей люди не называют эту сеть трехслойной нейронной сетью. Обычно мы подсчитываем только количество скрытых слоев или количество скрытых слоев и выходной слой, поэтому нейронная сеть двухслойная. Скрытые слои просто представляют входные или выходные слои. Теперь, как вы могли догадаться, термин «глубокое обучение» просто означает, что у нас есть «больше» скрытых слоев :). Так как же нейронные сети генерируют прогнозы?
Нейронная сеть генерирует прогнозы после прохождения всех входных данных через все слои вплоть до выходного слоя. Этот процесс называется прямой связью. Как вы можете видеть на изображении выше, мы «кормим» сеть входным значением x, вычисляем функцию активации и передаем ее слой за слоем, пока не достигнем выходного слоя. В контролируемых задачах настройки (таких как задачи классификации) мы обычно используем сигмовидную функцию активации в выходном слое, потому что мы можем преобразовать ее выходные данные в вероятности. На изображении выше мы видим, что выходной слой выдает значение 0,24, а поскольку это значение меньше 0,5, мы можем сказать, что предсказанный y_hat равен нулю. Затем, как и в типичной задаче классификации, у нас будет функция стоимости, которая измеряет, насколько модель близка к истинным меткам. На самом деле обучение нейронной сети означает просто максимальное снижение затрат. Мы можем определить функцию стоимости следующим образом:
Таким образом, цель состоит в том, чтобы найти такую комбинацию w и b, при которой наша стоимость J будет как можно меньше. Для этого мы будем полагаться на два важных алгоритма: градиентный спуск и обратное распространение ошибки.
Алгоритм градиентного спуска
Те из вас, кто занимался машинным обучением, вероятно, уже знают об алгоритмах градиентного спуска. Обучение нейронной сети мало чем отличается от обучения любой другой модели машинного обучения с использованием градиентного спуска. Единственным существенным отличием является нелинейный эффект в нашей сети, который делает нашу функцию стоимости невыпуклой. Чтобы обеспечить лучшую интуицию, давайте предположим, что наша функция стоимости является выпуклой функцией (большой чашей), как показано на следующем рисунке:
На приведенном выше графике горизонтальная ось представляет пространство наших параметров, весов и смещений, а функция стоимости J(w,b) представляет собой некоторую поверхность над горизонтальной осью. Красный кружок на изображении выше — это исходное значение нашей стоимости за вычетом весов и смещений. Теперь мы знаем, что для минимизации затрат необходимо выбирать самый крутой путь. Но вопрос в том, как мы узнаем, в каком направлении идти? Должны ли мы увеличивать или уменьшать значение параметра? Мы могли бы выполнить случайный поиск, но это заняло бы много времени и, очевидно, потребовало бы значительных вычислительных ресурсов. При настройке обучаемых параметров, весов и смещений есть лучшие способы найти направление, в котором вы должны двигаться. Расчет говорит нам, что направление вектора градиента будет естественным образом указывать на самое крутое направление в данной точке. Поэтому мы будем использовать градиент функции стоимости независимо от весов и смещений. Теперь давайте упростим ситуацию, просто взглянув на стоимость гирь, как показано на диаграмме ниже.
Обозначим значение нашей функции стоимости w.r.t как значение весов. Вы можете думать о черном круге выше как о нашей первоначальной плате. Напомним, что градиент функции или переменной может быть положительным, нулевым или отрицательным. Отрицательный наклон означает, что линия наклонена вниз, и наоборот. Теперь, поскольку наша цель — минимизировать стоимость, нам нужно переместить веса в направлении, противоположном градиенту функции стоимости. Этот процесс обновления можно записать следующим образом:
где α — размер шага или скорость обучения, которую мы умножаем на частную производную стоимости по обучаемому параметру. Итак, какова роль альфы? Что ж, градиент указывает нам направление, в котором функция имеет наибольшую скорость, однако он не говорит нам, как далеко мы должны двигаться в этом направлении. Это альфа, которая нам нужна, которая является гиперпараметром, который в основном контролирует размер шага, например, насколько мы должны двигаться в определенном направлении. Выбор подходящего значения для скорости обучения важен, потому что это сильно повлияет на две вещи: скорость алгоритма обучения и возможность найти локальный оптимум (сходимость). На практике вы можете использовать адаптивный алгоритм скорости обучения, такой как Momentum, RMSProp, Adam и т. д. Парень из AYLIEN написал очень хороший постстатья, охватывающий различные алгоритмы оптимизации и адаптивной скорости обучения.
Backpropagation
В предыдущем разделе мы обсудили алгоритм градиентного спуска, алгоритм оптимизации, который мы используем в качестве алгоритма обучения в глубоких нейронных сетях. Напомним, что использование градиентного спуска означает, что нам нужно найти функцию стоимости w.r.t и градиенты наших обучаемых параметров w и b. Другими словами, нам нужно вычислить частные производные функций стоимости относительно w и b. Однако, если мы наблюдаем функцию стоимости J (как показано на рис. 12 ниже), нет прямой связи между J и ни w, ни b.
Только когда мы проследим от выходного слоя (слоя, который генерирует y_hat) к входному слою, мы увидим, что J косвенно связано как с w, так и с b, как показано на рисунке 13 ниже:
Теперь вы можете видеть, что для того, чтобы найти градиент стоимости w и b, нам нужно найти частную производную со всеми переменными, такими как a (функция активации) и z (стоимость линейного вычисления: wx + b). Это то, что нам нужно. Здесь появляется обратное распространение. Обратное распространение — это, по сути, повторное применение правила цепного исчисления для дифференцирования, которое, я думаю, является, вероятно, наиболее эффективным способом найти градиент стоимости всех изученных параметров в нейронной сети. В этой статье я предложу вам вычислить градиент функции стоимости J по отношению к W2, который представляет собой вес второго слоя нейронной сети. Для простоты мы будем использовать архитектуру, показанную на рисунке 8, где у нас есть скрытый слой с тремя скрытыми нейронами.
Чтобы найти скорость изменения y_hat относительно z2, нам нужно продифференцировать сигмовидную функцию активации по z.
Теперь, получив значение частной производной J по отношению к W2, вы можете обновить значение W2, используя формулу, показанную на рисунке 11 в предыдущем разделе. По сути, мы будем повторять тот же расчет для всех весов и смещений, пока не получим наименьшее возможное значение стоимости.
Нейронная сеть для решения проблемы XOR
Здорово! Я думаю, что мы рассмотрели почти все, что нужно для построения моделей нейронных сетей или даже моделей глубокого обучения, которые помогут нам решить проблему XOR. На момент написания я построил простую модель нейронной сети только с одним скрытым слоем с различным количеством скрытых нейронов. Пример сети, которую я использовал, показан на изображении ниже. Кроме того, я представляю некоторые границы решений, созданные моей моделью с использованием разного количества нейронов. Как вы увидите позже, мы можем сказать, что большее количество нейронов сделает нашу модель более сложной, создавая более сложную границу принятия решений.
Но каков наилучший вариант? Там больше нейронов или глубже, то есть больше слоев? Теоретически основное преимущество очень глубокой сети заключается в том, что она может представлять очень сложные функции. В частности, используя более глубокие архитектуры, мы можем изучать функции на многих различных уровнях абстракции, например, идентифицировать ребра (на более низких уровнях) как очень сложные функции (на более глубоких уровнях).
Однако использование более глубоких сетей не всегда полезно на практике. Самая большая проблема, с которой мы столкнемся при обучении более глубоких сетей, — это проблема исчезающего градиента: очень глубокие сети часто имеют ситуации, когда сигнал градиента быстро стремится к нулю, что делает градиентный спуск невыносимо быстрым.
Более конкретно, во время градиентного спуска, когда мы возвращаемся от последнего слоя обратно к первому слою, мы умножаем весовую матрицу на каждом шаге, поэтому градиент может экспоненциально падать до нуля быстро или, в крайних случаях, можно быстро экспоненциально растут и «взрываются» до очень больших значений.
Итак, чтобы завершить эту длинную статью, вот несколько ключевых моментов, которые можно кратко суммировать:
-
Интуитивные нейронные сети вносят в модель нелинейность и могут использоваться для решения сложных нелинейно разделимых данных.
-
Perceptron — это элегантный алгоритм, на котором основаны многие современные алгоритмы машинного обучения, включая глубокое обучение.
-
Интуитивно глубокое обучение означает использование нейронных сетей с большим количеством скрытых слоев. Конечно, существует множество его вариантов, таких как сверточные нейронные сети, рекуррентные нейронные сети и другие.
-
Функция активации — очень важная часть нейронной сети, и да, вы должны знать об этом.
-
В настоящее время градиентный спуск с обратным распространением — лучшая комбинация, которую мы используем для обучения (глубоких) нейронных сетей.
-
Наличие большего количества скрытых слоев не обязательно улучшает производительность нашей модели. Фактически, он страдает от хорошо известной проблемы, проблемы исчезающего градиента.
Front-end-F-x-Team Amoy открывает WeiboЛа! (Отображается после входа в Weibo)Помимо статей, вас ждет разблокировка еще командного контента ?