Учебное пособие по глубокому обучению: от персептронов к глубоким сетям
Опубликовано: 2022-03-11В последние годы наблюдается возрождение в области искусственного интеллекта. Он распространился за пределы академического мира благодаря таким крупным игрокам, как Google, Microsoft и Facebook, которые создают свои собственные исследовательские группы и совершают впечатляющие приобретения.
Отчасти это можно объяснить обилием необработанных данных, генерируемых пользователями социальных сетей, большая часть которых нуждается в анализе, появлением передовых решений для обработки данных, а также дешевой вычислительной мощностью, доступной через GPGPU.
Но помимо этих явлений, это возрождение в немалой степени было вызвано новой тенденцией в ИИ, особенно в машинном обучении, известной как «Глубокое обучение». В этом руководстве я познакомлю вас с ключевыми концепциями и алгоритмами глубокого обучения, начиная с простейшей единицы композиции и заканчивая концепциями машинного обучения в Java.
(Для полного раскрытия: я также являюсь автором библиотеки глубокого обучения Java, доступной здесь, и примеры в этой статье реализованы с использованием вышеуказанной библиотеки. Если вам это нравится, вы можете поддержать ее, поставив звезду на GitHub. , за что я был бы признателен. Инструкции по использованию доступны на главной странице.)
Тридцать второе руководство по машинному обучению
Если вы не знакомы, ознакомьтесь с этим введением в машинное обучение:
Общая процедура выглядит следующим образом:
- У нас есть некоторый алгоритм, который дает несколько помеченных примеров, скажем, 10 изображений собак с меткой 1 («Собака») и 10 изображений других вещей с меткой 0 («Не собака») — обратите внимание, что мы в основном придерживаемся к контролируемой бинарной классификации для этой должности.
- Алгоритм «научится» распознавать изображения собак и, получив новое изображение, надеется выдать правильную метку (1, если это изображение собаки, и 0 в противном случае).
Этот параметр невероятно общий: ваши данные могут быть симптомами, а ваши ярлыки — болезнями; или ваши данные могут быть изображениями рукописных символов, а ваши метки — реальными символами, которые они представляют.
Персептроны: ранние алгоритмы глубокого обучения
Одним из первых алгоритмов обучения с учителем является персептрон, базовый строительный блок нейронной сети.
Скажем, у нас есть n точек на плоскости, помеченных «0» и «1». Нам дали новую точку, и мы хотим угадать ее метку (это похоже на сценарий «Собака» и «Не собака» выше). Как мы делаем это?
Один из подходов может состоять в том, чтобы посмотреть на ближайшего соседа и вернуть метку этой точки. Но чуть более разумным способом было бы выбрать линию, которая лучше всего разделяет размеченные данные, и использовать ее в качестве классификатора.
В этом случае каждый фрагмент входных данных будет представлен в виде вектора x = ( x_1, x_2 ), а наша функция будет выглядеть примерно так: «0», если ниже линии, «1», если выше».
Чтобы представить это математически, пусть наш разделитель определяется вектором весов w и вертикальным смещением (или смещением) b . Затем наша функция объединит входные данные и веса с передаточной функцией взвешенной суммы:
Затем результат этой передаточной функции будет передан в функцию активации для создания маркировки. В приведенном выше примере наша функция активации представляла собой пороговое значение (например, 1, если оно больше некоторого значения):
Обучение персептрона
Обучение персептрона заключается в подаче ему нескольких обучающих выборок и вычислении выходных данных для каждой из них. После каждой выборки веса w корректируются таким образом, чтобы минимизировать ошибку вывода , определяемую как разницу между желаемым (целевым) и фактическим выходами. Существуют и другие функции ошибок, такие как среднеквадратическая ошибка, но основной принцип обучения остается прежним.
Недостатки одиночного персептрона
Подход с одним персептроном к глубокому обучению имеет один существенный недостаток: он может изучать только линейно разделимые функции. Насколько велик этот недостаток? Возьмите XOR, относительно простую функцию, и обратите внимание, что ее нельзя классифицировать с помощью линейного разделителя (обратите внимание на неудачную попытку ниже):
Чтобы решить эту проблему, нам потребуется использовать многослойный персептрон, также известный как нейронная сеть с прямой связью: по сути, мы составим группу этих персептронов вместе, чтобы создать более мощный механизм обучения.
Нейронные сети с прямой связью для глубокого обучения
На самом деле нейронная сеть — это просто набор персептронов, связанных по-разному и работающих с разными функциями активации.
Для начала рассмотрим нейронную сеть с прямой связью, которая обладает следующими свойствами:
- Вход, выход и один или несколько скрытых слоев. На рисунке выше показана сеть с входным слоем из 3 единиц, скрытым слоем из 4 единиц и выходным слоем из 2 единиц (термины единицы и нейроны взаимозаменяемы).
- Каждая единица представляет собой отдельный персептрон, подобный описанному выше.
- Единицы входного слоя служат входными данными для единиц скрытого слоя, а единицы скрытого слоя являются входными данными для выходного слоя.
- Каждое соединение между двумя нейронами имеет вес w (аналогично весам персептрона).
- Каждая единица слоя t обычно связана с каждой единицей предыдущего слоя t - 1 (хотя вы можете разъединить их, установив их вес равным 0).
- Для обработки входных данных вы «прижимаете» входной вектор к входному слою, устанавливая значения вектора в качестве «выходов» для каждой из входных единиц. В этом конкретном случае сеть может обрабатывать трехмерный входной вектор (из-за трех входных единиц). Например, если ваш входной вектор равен [7, 1, 2], то вы должны установить выход верхней единицы ввода на 7, средней единицы на 1 и так далее. Затем эти значения передаются скрытым единицам с использованием передаточной функции взвешенной суммы для каждой скрытой единицы (отсюда термин прямое распространение), которые, в свою очередь, вычисляют свои выходные данные (функция активации).
- Выходной слой вычисляет свои выходные данные так же, как и скрытый слой. Результатом выходного слоя является выход сети.
За пределами линейности
Что, если каждому из наших персептронов разрешено использовать только линейную функцию активации? Тогда окончательный результат нашей сети по- прежнему будет некоторой линейной функцией входных данных, просто скорректированной с помощью множества различных весов, собранных по всей сети. Другими словами, линейная композиция набора линейных функций по-прежнему остается линейной функцией. Если мы ограничены линейными функциями активации, то нейронная сеть с прямой связью будет не более мощной, чем персептрон, независимо от того, сколько слоев она имеет.
Из-за этого в большинстве нейронных сетей используются нелинейные функции активации, такие как логистическая, тангенциальная, бинарная или выпрямительная. Без них сеть может обучаться только функциям, которые являются линейными комбинациями ее входных данных.
Обучение персептронов
Наиболее распространенный алгоритм глубокого обучения для контролируемого обучения многослойных персептронов известен как обратное распространение. Основная процедура:
- Обучающая выборка представляется и распространяется по сети.
Вычисляется выходная ошибка, обычно среднеквадратическая ошибка:
Где t — целевое значение, а y — фактический выход сети. Другие расчеты ошибок также допустимы, но MSE — хороший выбор.
Ошибка сети минимизируется с помощью метода, называемого стохастическим градиентным спуском.
Градиентный спуск универсален, но в случае нейросетей это будет график зависимости ошибки обучения от входных параметров. Оптимальным значением для каждого веса является значение, при котором ошибка достигает глобального минимума . На этапе обучения веса обновляются небольшими шагами (после каждой обучающей выборки или мини-пакета из нескольких выборок) таким образом, чтобы они всегда пытались достичь глобального минимума, но это непростая задача, поскольку вы часто заканчиваются локальными минимумами, как тот, что справа. Например, если вес имеет значение 0,6, его необходимо изменить в сторону 0,4.
На этом рисунке представлен простейший случай, когда ошибка зависит от одного параметра. Однако сетевая ошибка зависит от веса каждой сети, а функция ошибки намного сложнее.
К счастью, обратное распространение предоставляет метод обновления каждого веса между двумя нейронами с учетом выходной ошибки. Сам вывод довольно сложен, но обновление веса для данного узла имеет следующий (простой) вид:
Где E — ошибка вывода, а w_i — вес ввода i нейрона.
По сути, цель состоит в том, чтобы двигаться в направлении градиента относительно веса i . Ключевым термином, конечно же, является производная от ошибки, которую не всегда легко вычислить: как найти эту производную для случайного веса случайного скрытого узла в середине большой сети?
Ответ: через обратное распространение. Ошибки сначала вычисляются в выходных единицах, где формула довольно проста (основана на разнице между целевым и прогнозируемым значениями), а затем умным образом распространяются обратно по сети, что позволяет нам эффективно обновлять наши веса во время обучения и (надеюсь) достичь минимума.
Скрытый слой
Скрытый слой представляет особый интерес. Согласно теореме об универсальной аппроксимации, сеть с одним скрытым слоем с конечным числом нейронов может быть обучена аппроксимации произвольно случайной функции. Другими словами, одного скрытого слоя достаточно, чтобы изучить любую функцию. Тем не менее, на практике мы часто лучше учимся, используя несколько скрытых слоев (т. е. более глубокие сети).
Скрытый слой — это место, где сеть хранит свое внутреннее абстрактное представление обучающих данных, аналогично тому, как человеческий мозг (значительно упрощенная аналогия) имеет внутреннее представление реального мира. В дальнейшем в уроке мы рассмотрим различные способы игры со скрытым слоем.
Пример сети
Здесь вы можете увидеть простую (4-2-3 слоя) нейронную сеть с прямой связью, которая классифицирует набор данных IRIS, реализованный на Java, с помощью метода testMLPSigmoidBP . Набор данных содержит три класса растений ириса с такими характеристиками, как длина чашелистика, длина лепестка и т. д. Сеть предоставляет 50 образцов на класс. Признаки привязаны к входным единицам, а каждая выходная единица соответствует одному классу набора данных: «1/0/0» указывает, что растение относится к классу Setosa, «0/1/0» указывает на Versicolor и « 0/0/1” указывает на Вирджинику). Ошибка классификации составляет 2/150 (т. е. неправильно классифицируются 2 образца из 150).
Проблема с большими сетями
Нейронная сеть может иметь более одного скрытого слоя: в этом случае более высокие слои «строят» новые абстракции поверх предыдущих слоев. И, как мы упоминали ранее, вы часто можете лучше учиться на практике с более крупными сетями.
Однако увеличение количества скрытых слоев приводит к двум известным проблемам:
- Исчезающие градиенты: по мере того, как мы добавляем все больше и больше скрытых слоев, обратное распространение становится все менее и менее полезным при передаче информации на нижние слои. Фактически, когда информация передается обратно, градиенты начинают исчезать и становятся малыми по сравнению с весами сетей.
- Переоснащение: пожалуй, центральная проблема машинного обучения. Вкратце, переобучение описывает феномен слишком точного подбора обучающих данных, возможно, со слишком сложными гипотезами. В таком случае ваш ученик в конечном итоге действительно хорошо подходит к обучающим данным, но будет намного хуже работать на реальных примерах.
Давайте рассмотрим некоторые алгоритмы глубокого обучения для решения этих проблем.
Автоэнкодеры
Большинство вводных классов по машинному обучению, как правило, останавливаются на нейронных сетях с прямой связью. Но пространство возможных сетей гораздо богаче — так что продолжим.
Автоэнкодер обычно представляет собой нейронную сеть с прямой связью, целью которой является изучение сжатого распределенного представления (кодирования) набора данных.
Концептуально сеть обучена «воссоздавать» входные данные, т. е. входные и целевые данные совпадают. Другими словами: вы пытаетесь вывести то же самое, что и вводили, но каким-то образом сжатым. Это сбивающий с толку подход, поэтому давайте рассмотрим пример.
Сжатие ввода: изображения в градациях серого
Предположим, что обучающие данные состоят из изображений в градациях серого 28x28, и значение каждого пикселя привязано к одному нейрону входного слоя (т. е. во входном слое будет 784 нейрона). Тогда выходной слой будет иметь то же количество единиц (784), что и входной слой, и целевое значение для каждой выходной единицы будет значением в градациях серого для одного пикселя изображения.
Интуиция, стоящая за этой архитектурой, заключается в том, что сеть не будет изучать «сопоставление» между обучающими данными и их метками, а вместо этого будет изучать внутреннюю структуру и особенности самих данных. (Из-за этого скрытый слой также называют детектором признаков .) Обычно количество скрытых единиц меньше, чем количество входных/выходных слоев, что заставляет сеть изучать только самые важные признаки и добивается уменьшения размерности.
По сути, мы хотим, чтобы несколько небольших узлов посередине действительно изучали данные на концептуальном уровне, создавая компактное представление, которое каким-то образом фиксирует основные характеристики нашего ввода.
грипп
Чтобы дополнительно продемонстрировать автоэнкодеры, давайте рассмотрим еще одно приложение.
В этом случае мы будем использовать простой набор данных, состоящий из симптомов гриппа (за идею спасибо этому сообщению в блоге). Если вам интересно, код для этого примера можно найти в методе testAEBackpropagation .
Вот как разбивается набор данных:
- Имеется шесть функций бинарного ввода.
- Первые три являются симптомами болезни. Например, 1 0 0 0 0 0 указывает на то, что у данного больного высокая температура, тогда как 0 1 0 0 0 0 указывает на кашель, 1 1 0 0 0 0 указывает на кашель и высокую температуру и т. д.
- Последние три признака — это «встречные» симптомы; когда у пациента есть один из них, маловероятно, что он или она болен. Например, 0 0 0 1 0 0 означает, что у этого пациента есть вакцина против гриппа. Возможны комбинации двух наборов признаков: 0 1 0 1 0 0 указывает на вакцинированного пациента с кашлем и так далее.
Мы будем считать пациента больным, если у него или нее есть по крайней мере два из первых трех признаков, и здоровым, если у него или нее есть по крайней мере два из вторых трех признаков (с разрывом связей в пользу здоровых пациентов), например:
- 111000, 101000, 110000, 011000, 011100 = больной
- 000111, 001110, 000101, 000011, 000110 = здоров
Мы будем обучать автоэнкодер (используя обратное распространение ошибки) с шестью входными и шестью выходными модулями, но только с двумя скрытыми модулями .
После нескольких сотен итераций мы наблюдаем, что когда каждый из «больных» образцов предоставляется сети машинного обучения, одна из двух скрытых единиц (одна и та же единица для каждой «больной» выборки) всегда демонстрирует более высокое значение активации, чем разное. Наоборот, когда представлен «здоровый» образец, другая скрытая единица имеет более высокую активацию.
Возвращаясь к машинному обучению
По сути, два наших скрытых модуля изучили компактное представление набора данных о симптомах гриппа. Чтобы увидеть, как это связано с обучением, вернемся к проблеме переобучения. Обучая нашу сеть изучать компактное представление данных, мы отдаем предпочтение более простому представлению, а не очень сложной гипотезе, которая превосходит обучающие данные.
В некотором смысле, отдавая предпочтение этим более простым представлениям, мы пытаемся изучить данные в более истинном смысле.
Ограниченные машины Больцмана
Следующим логическим шагом будет рассмотрение машин Больцмана с ограниченным доступом (RBM), генеративной стохастической нейронной сети, которая может изучать распределение вероятностей по набору входных данных .

RBM состоят из скрытого, видимого и предвзятого слоев. В отличие от сетей с прямой связью, связи между видимым и скрытым слоями являются ненаправленными (значения могут распространяться как в направлении видимый-скрытый, так и в направлении скрытый-видимый) и полносвязными (каждая единица данного слоя связана с каждый блок в следующем — если бы мы позволили любому блоку в любом слое соединиться с любым другим слоем, то у нас была бы машина Больцмана (а не ограниченная машина Больцмана ).
Стандартный RBM имеет бинарные скрытые и видимые блоки: то есть активация блока равна 0 или 1 при распределении Бернулли, но есть варианты с другими нелинейностями.
Хотя исследователи уже некоторое время знают о RBM, недавнее введение алгоритма обучения без учителя с контрастной дивергенцией возобновило интерес.
Контрастное расхождение
Одношаговый алгоритм контрастивной дивергенции (CD-1) работает следующим образом:
- Положительная фаза :
- Входной образец v прикрепляется к входному слою.
- v распространяется на скрытый уровень аналогично сетям прямого распространения. Результатом активации скрытого слоя является h .
- Отрицательная фаза :
- Распространите h обратно на видимый слой с результатом v' (связи между видимым и скрытым слоями ненаправлены и, таким образом, допускают движение в обоих направлениях).
- Распространите новый v' обратно на скрытый слой с результатом активации h' .
Обновление веса :
Где a — скорость обучения, а v , v' , h , h' и w — векторы.
Интуиция, лежащая в основе алгоритма, заключается в том, что положительная фаза ( h при заданном v ) отражает внутреннее представление сети данных реального мира . Между тем, отрицательная фаза представляет собой попытку воссоздать данные на основе этого внутреннего представления ( v' при заданном h ). Основная цель состоит в том, чтобы сгенерированные данные были максимально приближены к реальному миру , и это отражено в формуле обновления веса.
Другими словами, у сети есть некоторое представление о том, как могут быть представлены входные данные, поэтому она пытается воспроизвести данные на основе этого восприятия. Если его воспроизведение недостаточно близко к реальности, он вносит коррективы и повторяет попытку.
Возвращение к гриппу
Чтобы продемонстрировать контрастивное расхождение, мы будем использовать тот же набор данных о симптомах, что и раньше. Тестовая сеть представляет собой RBM с шестью видимыми и двумя скрытыми элементами. Мы будем обучать сеть, используя контрастную дивергенцию с симптомами v , привязанными к видимому слою. Во время тестирования симптомы снова предъявляются видимому слою; затем данные распространяются на скрытый слой. Скрытые единицы представляют больное/здоровое состояние, архитектура очень похожа на автоэнкодер (передача данных из видимого слоя в скрытый).
После нескольких сотен итераций мы можем наблюдать тот же результат, что и с автоэнкодерами: один из скрытых блоков имеет более высокое значение активации при предъявлении любого из «больных» образцов, а другой всегда более активен для «здоровых» образцов.
Вы можете увидеть этот пример в действии в методе testContrastiveDivergence .
Глубокие сети
Теперь мы продемонстрировали, что скрытые уровни автокодировщиков и RBM действуют как эффективные детекторы признаков; но мы редко можем использовать эти функции напрямую. На самом деле приведенный выше набор данных является скорее исключением, чем правилом. Вместо этого нам нужно найти способ косвенного использования этих обнаруженных функций.
К счастью, было обнаружено, что эти структуры можно накладывать друг на друга, образуя глубокие сети. Эти сети можно обучать жадно, по одному слою за раз, чтобы помочь преодолеть исчезающий градиент и проблемы переобучения , связанные с классическим обратным распространением.
Полученные структуры часто бывают довольно мощными и дают впечатляющие результаты. Возьмем, к примеру, известную статью Google о кошках, в которой они используют специальные глубинные автокодировщики, чтобы «обучиться» распознаванию человеческих и кошачьих лиц на основе неразмеченных данных.
Давайте посмотрим поближе.
Стекированные автоэнкодеры
Как следует из названия, эта сеть состоит из нескольких сложенных автоэнкодеров.
Скрытый слой автоэнкодера t действует как входной слой для автоэнкодера t + 1 . Входной слой первого автоэнкодера является входным слоем для всей сети. Жадная послойная процедура обучения работает следующим образом:
- Обучите первый автоэнкодер ( t=1 или красные соединения на рисунке выше, но с дополнительным выходным слоем) индивидуально, используя метод обратного распространения со всеми доступными обучающими данными.
- Обучите второй автоэнкодер t=2 (зеленые соединения). Поскольку входной слой для t=2 является скрытым слоем для t=1 , нас больше не интересует выходной слой для t=1 , и мы удаляем его из сети. Обучение начинается с фиксации входного образца на входном слое t=1 , который распространяется вперед на выходной слой t=2 . Затем веса (скрытый ввод и скрытый вывод) для t = 2 обновляются с использованием обратного распространения. t=2 использует все обучающие выборки, как и t=1 .
- Повторите предыдущую процедуру для всех слоев (т. е. удалите выходной слой предыдущего автоэнкодера, замените его еще одним автоэнкодером и обучите с обратным распространением).
- Шаги 1-3 называются предварительным обучением и оставляют веса правильно инициализированными. Однако между входными данными и выходными метками нет сопоставления. Например, если сеть обучена распознавать изображения рукописных цифр, по-прежнему невозможно сопоставить единицы из последнего детектора признаков (т. е. скрытого слоя последнего автоэнкодера) с цифровым типом изображения. В этом случае наиболее распространенным решением является добавление одного или нескольких полносвязных слоев к последнему слою (синие соединения). Всю сеть теперь можно рассматривать как многослойный персептрон и обучать с помощью обратного распространения (этот шаг также называется тонкой настройкой ).
Таким образом, стекированные автоматические кодировщики предназначены для обеспечения эффективного метода предварительной подготовки для инициализации весов сети, оставляя вам сложный многослойный персептрон, готовый к обучению (или точной настройке ).
Сети глубокого убеждения
Как и в случае с автоэнкодерами, мы также можем объединить машины Больцмана, чтобы создать класс, известный как сети глубокого убеждения (DBN) .
В этом случае скрытый уровень RBM t действует как видимый уровень для RBM t+1 . Входной слой первого RBM является входным слоем для всей сети, и жадное послойное предварительное обучение работает следующим образом:
- Обучите первый RBM t = 1 , используя контрастное расхождение со всеми обучающими выборками.
- Обучите второй RBM t=2 . Поскольку видимый слой для t=2 является скрытым слоем t=1 , обучение начинается с фиксации входной выборки в видимом слое t=1 , который распространяется вперед к скрытому слою t=1 . Затем эти данные служат для инициирования обучения контрастной дивергенции для t=2 .
- Повторите предыдущую процедуру для всех слоев.
- Подобно автоэнкодерам со стеком, после предварительной подготовки сеть можно расширить, подключив один или несколько полносвязных слоев к конечному скрытому слою RBM. Это формирует многослойный персептрон, который затем можно точно настроить с помощью обратного распространения.
Эта процедура аналогична процедуре многоуровневых автоэнкодеров, но автоэнкодеры заменены RBM, а обратное распространение заменено алгоритмом контрастивной дивергенции.
(Примечание: чтобы узнать больше о построении и обучении многоуровневых автокодировщиков или сетей глубокого убеждения, ознакомьтесь с примером кода здесь.)
Сверточные сети
В качестве окончательной архитектуры глубокого обучения давайте рассмотрим сверточные сети, особенно интересный и особый класс сетей прямой связи, которые очень хорошо подходят для распознавания изображений.
Прежде чем мы рассмотрим фактическую структуру сверточных сетей, мы сначала определим фильтр изображения или квадратную область с соответствующими весами. Фильтр применяется ко всему входному изображению, и вы часто применяете несколько фильтров. Например, вы можете применить четыре фильтра 6x6 к заданному входному изображению. Тогда выходной пиксель с координатами 1,1 представляет собой взвешенную сумму квадрата входных пикселей 6x6 с верхним левым углом 1,1 и весов фильтра (который также является квадратом 6x6). Выходной пиксель 2,1 является результатом входного квадрата с верхним левым углом 2,1 и так далее.
С учетом этого эти сети определяются следующими свойствами:
- Сверточные слои применяют к входным данным ряд фильтров . Например, первый сверточный слой изображения может иметь четыре фильтра 6x6. Результат применения одного фильтра к изображению называется картой объектов (FM), а количество карт объектов равно количеству фильтров. Если предыдущий слой также сверточный, фильтры применяются ко всем его ФМ с разными весами, поэтому каждый входной ФМ связан с каждым выходным ФМ. Интуиция, стоящая за общими весами на изображении, заключается в том, что функции будут обнаружены независимо от их местоположения, а множество фильтров позволяет каждому из них обнаруживать разные наборы функций.
- Слои подвыборки уменьшают размер входных данных. Например, если вход состоит из изображения 32x32, а слой имеет область субдискретизации 2x2, выходным значением будет изображение 16x16, что означает, что 4 пикселя (каждый квадрат 2x2) входного изображения объединяются в один выход. пиксель. Существует несколько способов подвыборки, но наиболее популярными являются максимальный пул, средний пул и стохастический пул.
- Последний слой подвыборки (или сверточный) обычно связан с одним или несколькими полносвязными слоями, последний из которых представляет целевые данные.
- Обучение выполняется с использованием модифицированного обратного распространения, которое учитывает слои подвыборки и обновляет веса сверточных фильтров на основе всех значений, к которым применяется этот фильтр.
Вы можете увидеть несколько примеров сверточных сетей, обученных (с обратным распространением) на наборе данных MNIST (изображения рукописных букв в оттенках серого) здесь, в частности, в методах testLeNet* (я бы рекомендовал testLeNetTiny2 , так как он обеспечивает низкую частоту ошибок около 2% в относительно короткие сроки). Здесь также есть хорошая визуализация подобной сети на JavaScript.
Реализация
Теперь, когда мы рассмотрели наиболее распространенные варианты нейронных сетей, я решил немного написать о проблемах, возникающих при реализации этих структур глубокого обучения.
Вообще говоря, моей целью при создании библиотеки глубокого обучения было (и остается) создание фреймворка на основе нейронной сети, который удовлетворял бы следующим критериям:
- Общая архитектура, способная представлять разнообразные модели (например, все варианты нейронных сетей, которые мы видели выше).
- Возможность использования разнообразных алгоритмов обучения (обратное распространение, контрастная дивергенция и др.).
- Достойная производительность.
Чтобы удовлетворить эти требования, я применил многоуровневый (или модульный) подход к разработке программного обеспечения.
Структура
Начнем с основ:
- NeuralNetworkImpl — это базовый класс для всех моделей нейронных сетей.
- Каждая сеть содержит набор слоев.
- Каждый уровень имеет список соединений, где соединение — это соединение между двумя слоями, так что сеть представляет собой ориентированный ациклический граф.
Эта структура достаточно гибкая, чтобы ее можно было использовать для классических сетей с прямой связью, а также для RBM и более сложных архитектур, таких как ImageNet.
Это также позволяет уровню быть частью более чем одной сети. Например, слои в сети глубокого убеждения также являются слоями в соответствующих им RBM.
Кроме того, эта архитектура позволяет рассматривать DBN как список сложенных RBM на этапе предварительной подготовки и как сеть прямой связи на этапе тонкой настройки, что интуитивно приятно и удобно программно.
Распространение данных
Следующий модуль занимается распространением данных по сети в два этапа:
- Определите порядок слоев. Например, чтобы получить результаты от многослойного персептрона, данные «привязываются» к входному слою (следовательно, это первый рассчитываемый слой) и распространяются до выходного слоя. Чтобы обновить веса во время обратного распространения, выходная ошибка должна распространяться через каждый слой в порядке ширины, начиная с выходного слоя. Это достигается с помощью различных реализаций LayerOrderStrategy , которые используют структуру графа сети, применяя различные методы обхода графа. Некоторые примеры включают стратегию «сначала в ширину» и нацеливание на определенный слой. Порядок фактически определяется соединениями между слоями, поэтому стратегии возвращают упорядоченный список соединений.
- Рассчитайте значение активации. С каждым уровнем связан ConnectionCalculator , который берет список подключений (из предыдущего шага) и входные значения (из других слоев) и вычисляет результирующую активацию. Например, в простой сигмоидальной сети с прямой связью ConnectionCalculator скрытого слоя принимает значения входного слоя и слоя смещения (которые, соответственно, являются входными данными и массивом единиц ) и веса между элементами (в случае полностью связанных слои, веса фактически хранятся в соединении FullyConnected как Matrix ), вычисляет взвешенную сумму и передает результат в сигмовидную функцию. Калькуляторы соединения реализуют множество функций передачи (например, взвешенная сумма, свертка) и активации (например, логистическая и тангенциальная для многослойного персептрона, двоичная для RBM). Большинство из них можно выполнить на графическом процессоре с помощью Aparapi и использовать с мини-пакетным обучением.
Вычисление на GPU с помощью Aparapi
Как я упоминал ранее, одна из причин возрождения нейронных сетей в последние годы заключается в том, что их методы обучения очень благоприятны для параллелизма, что позволяет значительно ускорить обучение с помощью GPGPU. В этом случае я решил работать с библиотекой Aparapi, чтобы добавить поддержку графического процессора.
Aparapi накладывает некоторые важные ограничения на калькуляторы соединений:
- Разрешены только одномерные массивы (и переменные) примитивных типов данных.
- Только методы-члены самого класса ядра Aparapi могут вызываться из исполняемого кода графического процессора.
Таким образом, большая часть данных (веса, входные и выходные массивы) хранится в экземплярах Matrix , которые внутри используют одномерные массивы с плавающей запятой. Все калькуляторы соединений Aparapi используют либо AparapiWeightedSum (для полносвязных слоев и функций ввода взвешенной суммы), AparapiSubsampling2D (для субдискретизирующих слоев) или AparapiConv2D (для сверточных слоев). Некоторые из этих ограничений можно преодолеть с помощью архитектуры гетерогенной системы. Aparapi также позволяет запускать один и тот же код как на CPU, так и на GPU.
Повышение квалификации
The training module implements various training algorithms. It relies on the previous two modules. For example, BackPropagationTrainer (all the trainers are using the Trainer base class) uses feedforward layer calculator for the feedforward phase and a special breadth-first layer calculator for propagating the error and updating the weights.
My latest work is on Java 8 support and some other improvements, will soon be merged into master.
Заключение
The aim of this Java deep learning tutorial was to give you a brief introduction to the field of deep learning algorithms, beginning with the most basic unit of composition (the perceptron) and progressing through various effective and popular architectures, like that of the restricted Boltzmann machine.
The ideas behind neural networks have been around for a long time; but today, you can't step foot in the machine learning community without hearing about deep networks or some other take on deep learning. Hype shouldn't be mistaken for justification, but with the advances of GPGPU computing and the impressive progress made by researchers like Geoffrey Hinton, Yoshua Bengio, Yann LeCun and Andrew Ng, the field certainly shows a lot of promise. There's no better time to get familiar and get involved like the present.
Appendix: Resources
If you're interested in learning more, I found the following resources quite helpful during my work:
- DeepLearning.net: a portal for all things deep learning. It has some nice tutorials, software library and a great reading list.
- An active Google+ community.
- Two very good courses: Machine Learning and Neural Networks for Machine Learning, both offered on Coursera.
- The Stanford neural networks tutorial.