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

Стэнфордский курс: лекция 4. Введение в нейронные сети

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

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

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

Вспомним определение классификатора, на котором мы остановились. Функция f принимает данные x и параметры W на вход и выдаёт вектор оценок s для каждой из категорий, которые вы хотите классифицировать. Также у нас есть функция потерь L (например, SVM), определяющая, насколько правильные получились оценки. С её помощью мы можем вычислить потери данных. Узнать «простоту» модели помогает регуляризация. 

Наша цель — найти параметры W, соответствующие наименьшим потерям. Для этого мы используем отрицательное направление градиента функции L, который указывает путь к её минимуму.

Мы выяснили, что есть два основных способа вычислить градиент: аналитический и числовой. Числовой метод достаточно прост, но работает очень медленно и выдаёт приблизительные значения. Аналитический градиент более точный и быстрый, но в при его подсчёте можно легко допустить ошибки. Чтобы избежать этого и легко вычислять градиент даже для сложных функций, лучше всего использовать вычислительные графы.

Введение в теорию графов и backpropagation

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

На рисунке выше изображён пример графа с нашим классификатором. Узел с операцией (*) означает умножение матриц параметров W и данных x, результатом которого является вектор весов s. Следующая вершина зависимых потерь (hinge loss) определяет потери данных L. Узел R вычисляет регуляризацию. И, наконец, в самом конце мы получаем общие потери, суммируя регуляризацию и потери данных.

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

Начнём с простого примера:

У нас есть функция f(x,y,z) = (x+y)z и мы хотим найти её градиенты по отношению к каждой из переменных. На вычислительном графе отражены операции x+y и (x+y)z в виде вершин сложения (+) и умножения (*). Для примера взяты значения x = 2, y = 5, z = 4, поэтому: −2 + 5 = 3 (промежуточный результат после узла сложения), а 3 * (4) = 12 (результат умножения).

Обозначим какими-нибудь буквами наши промежуточные значения. Пусть переменная после (+) называется q (q = x+y), тогда функция f будет равна qz. Выпишем градиенты (частные производные): градиент q зависит от переменных x и y, а градиент f от q и z. Поскольку мы ищем градиент исходной функции f, которая зависит от всех трёх переменных x, y и z, то нам хотелось бы найти df/dx, df/dy и df/dz.

Частные производные dq/dx и dq/dy равны единице — это означает, что при любом изменении x или y q изменится точно так же.

Попробуем рекурсивно воспользоваться правилом дифференцирования сложной функции. Начнём с конца вычислительного графа и будем двигаться к началу, по пути считая все градиенты. Самое последнее полученное нами значение — функция f и её промежуточный результат −12, частная производная по которому равна единице. Далее идёт переменная z, и мы знаем, что df/dz = q = 3. Переходим к вершине (+) и переменной q: производная df/dq равна z, то есть −4

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

Теперь мы приблизились к производной df/dy. Правило дифференцирования сложной функции говорит, что она может быть записана как df/dq * dq/dy. Значения df/dq и dq/dy нам известны: они равны −4 и 1 соответственно, поэтому производная dfdy равна −4. Точно так же это работает и для df/dx:

Здесь мы нашли ещё одну закономерность: в вершине сложения оказались равными все три градиента: и самой вершины, и входящие в неё. Обратите внимание: значение производной для узла, который следует за текущим, называется восходящий градиент, а для предыдущего узла — локальный градиент. Например, для (+) восходящим градиентом является производная df/df, а локальным — производные df/dx и df/dy.

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

Обратное распространение ошибки: продвинутый уровень

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

Здесь сходу посчитать производные уже не так просто, как в предыдущем случае. Нам нужно найти градиенты df/dw0, df/dw1, df/dw2, df/dx0 и df/dx1. Для вычислений возьмём тестовые значения w0 = 2, w1 =3, w2 =3, x0 =1 и x1 =2. Если подставить их в функцию, то на выходе получим f = 0.73.

Снова начнём с конца и посчитаем производную функции df/df, которая, очевидно, равна единице. Движемся дальше — первая вершина содержит сложную функцию 1/x. Можно воспользоваться таблицей производных и вспомнить, что 1/x = x-1. В следующем узле находится константа +1, производная которой, как вы помните, равна нулю. Третья вершина — это экспонента в степени x, а четвёртая — умножение на −1

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

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

Точно так же выполняем и со следующим узлом сложения. 

Продвинемся к первым двум переменным: w0 и x0. Перед ними находится вершина умножения. Ранее мы обнаружили, что в этом случае градиент по отношению к одному из входов просто является значением другого входа, умноженным на восходящий градиент. Поэтому df/dw0 = 2 * 0.2 = 0.4, а df/dw0 = (1) * 0.2 = 0.2. Таким же образом разбираемся со второй вершиной напротив w1 и x1:

Вот и всё! Было непросто, но мы справились.

Прокачайте свой граф

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

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

Переменные степени экспоненты w0, w1, w2, x0 и x1 складываются в вершине сложения, которая следует сразу за w2. Поэтому мы можем взять ту часть графа, которая полностью совпадает с сигмоидой, и объединить несколько узлов в один. Проверим, что локальные градиенты в этом случае окажутся одинаковыми: просто подставим значение функции f в выражение производной сигмоиды:

Видим, что градиенты совпадают. Теперь наш граф выглядит следующим образом:

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

Мы рассмотрели пример со скалярными величинами, но точно так же можно работать с векторами и матрицами: в этом случае на каждом шаге придётся считать матрицу производных, которая также называется матрица Якоби.

Переносим вычислительный граф в код

Все наши предыдущие действия над вычислительными графами делятся на два больших этапа:

  1. Проходим по графу в прямом направлении, подставляя исходные значения переменных и считая все промежуточные результаты;
  2. Идём в обратном направлении, вычисляя градиенты и пользуясь правилом дифференцирования.

В коде их можно описать функциями forward и backward, которые будут выглядеть примерно так:

При этом мы можем установить механику forward и backward для каждой вершины. Вот пример функции, которая обрабатывает узел умножения по установленным правилам:

Рассмотрим популярный фреймворк для глубокого обучения Caffe. Зайдём в его репозиторий и откроем папку Layers:

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

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

Нейронные сети: начинаем с малого

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

Вспомним функцию линейного классификатора, о которой мы так много говорили: f = Wx. Если мы захотим «превратить» её в нейросеть, то нам надо будет разделить параметры W на две части: W1 и W2 и применить одно линейное преобразование поверх другого:

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

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

Также мы говорили о том, что с такими единичными шаблонами возникает проблема: например, в классе “car” машина окрашена в красный цвет, в то время как в реальных данных могут встречаться автомобили и других цветов. Многослойные сети решают этот вопрос: W1 содержит те же единичные шаблоны, но теперь оценки для них хранятся в промежуточной нелинейной переменной h. И следующий слой W2 объединит шаблоны с помощью взвешенной суммы, что позволит дать более точные оценки для машин других цветов и прочих разнообразных объектов.

Кстати, ничто не мешает нам добавить ещё один слой, чтобы повысить точность распознавания:

Именно так появляются глубокие нейросети.

Для наглядности посмотрим на код простой двухслойной сети, которую можно реализовать всего в 20 строк:

Artificial vs Biological

На самом деле искусственные и биологические нейронные сети имеют мало общего. Тем не менее, именно изучение человеческого мышления вдохновило учёных на создание AI. 

В нашем мозге присутствует огромное число нейронов, которые соединены друг с другом специальными «ветвями» — аксонами. Когда происходит какая-либо мыслительная деятельность, по нейронам проходят электрические импульсы, передающиеся от одних клеток к другим. У нейронов есть дендриты — это отростки, к которым приходят импульсы. Тело клетки объединяет в себе все поступающие сигналы и посылает их в следующие нейроны через свой аксон. Место, где происходит контакт двух нейронов, называется синапс.

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

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

Хотя искусственные и настоящие нейроны кажутся очень похожими, всё-таки нужно учесть несколько важных моментов:

— биологические нейроны делятся на множество разновидностей;

— реальные дендриты могут выполнять сложные нелинейные вычисления;

— синапсы — это не просто веса, а сложная нелинейная динамическая система;

— обычной функции активации на практике может быть недостаточно.

Поэтому будьте осторожны с биологическими аналогиями.

⌘⌘⌘

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

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

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

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

Domains weekly: повышение цен на .ORG, кража $1 млн с поддельным доменом и крупнейшие сделки недели

Domains weekly: повышение цен на .ORG, кража $1 млн с поддельным доменом и крупнейшие сделки недели

Привет! На связи редакция блога. Вы наверняка знаете, что все начинания часто откладываются на «после Нового года», «со следующего месяца»,...
Read More
Отличается умом и сообразительностью: неожиданные применения нейросетей

Отличается умом и сообразительностью: неожиданные применения нейросетей

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

Какой хостинг выбрать для чат-бота?

Чат-боты — это не всегда такие же сложные и продвинутые программы, как, например, голосовые помощники на основе нейросетей. Тем не...
Read More
Какие проблемы решил новый Личный кабинет REG.RU

Какие проблемы решил новый Личный кабинет REG.RU

Около года назад мы запустили новый Личный кабинет REG.RU. Без преувеличения наши коллеги проделали огромную работу. Этим текстом мы бы...
Read More
Как зарегистрировать домен и не попасть под суд

Как зарегистрировать домен и не попасть под суд

Итак, вы хотите стать владельцем доменного имени. Скорее всего, на этом этапе вас волнуют вопросы, где и как его регистрировать...
Read More
Что такое ЭДО и почему вам нужно подключить его прямо сейчас  

Что такое ЭДО и почему вам нужно подключить его прямо сейчас 

Ещё каких-то 15 лет назад принтеры, факсы и подобное оборудование были неотъемлемой частью любого крупного или небольшого офиса, но постепенно...
Read More
6 трендовых доменных зон для онлайн-бизнеса

6 трендовых доменных зон для онлайн-бизнеса

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

Обучаем виртуального дракона фигурам высшего пилотажа

В наши дни компьютерная графика присутствует во всех популярных видах визуального контента: от видеороликов YouTube-блогеров до полнометражных фильмов. Но проработка...
Read More
Стэнфордский курс: лекция 9. Архитектуры CNN

Стэнфордский курс: лекция 9. Архитектуры CNN

На прошлом уроке мы узнали о наиболее популярных библиотеках и фреймворках для глубокого обучения, рассмотрели их особенности и области применения....
Read More
GPT-2: нейросеть, которая закончит за вас предложение

GPT-2: нейросеть, которая закончит за вас предложение

Встречали ли вы когда-нибудь собеседника, который после нескольких сказанных вами слов заканчивал за вас предложение? GPT-2 умеет и не такое:...
Read More