[Перевод] Нейросети и глубокое обучение: онлайн-учебник, глава 6, ч.1: глубокое обучение

Содержание


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

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

Основная часть главы — введение в один из наиболее популярных типов глубоких сетей: глубокие свёрточные сети (ГСС). Мы поработаем с подробным примером использования свёрточной сети, с кодом и прочим, для решения задачи классификации рукописных цифр из набора данных MNIST:

839d0b54370af70f06b3f097897de457.png
Начнём мы наш обзор свёрточных сетей с неглубоких сетей, которые мы использовали для решения этой задачи ранее в книге. В несколько этапов мы будем создавать всё более мощные сети. По пути мы будем знакомиться с многими мощными технологиями: свёртками, пулингом [pooling], использованием GPU для серьёзного увеличения объёма обучения по сравнению с тем, что делали неглубокие сети, алгоритмическим расширением обучающих данных (для уменьшения переобучения), использованием технологии исключения [dropout] (также для уменьшения переобучения), использованием ансамблей сетей, и прочим. В результате мы придём к системе, способности которой находятся почти на человеческом уровне. Из 10 000 проверочных изображений MNIST — которые система не видела во время обучения — она сумеет правильно распознать 9967. И вот некоторые из тех изображений, которые были распознаны неправильно. В правом верхнем углу указаны верные варианты; то, что показала наша программа, указано в правом нижнем углу.

b6e2d769a802b1ae5f249932789f2dff.png

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

Остаток главы посвящён обсуждению глубокого обучения с более широкой и менее подробной точки зрения. Мы кратко рассмотрим другие модели НС, в частности, рекуррентные НС, и единицы долгой краткосрочной памяти, и то, как эти модели можно применять для решения задач по распознаванию речи, обработке естественного языка и других. Мы порассуждаем о будущем НС и ГО, от идей типа пользовательских интерфейсов, базирующихся на намерениях [intention-driven], до роли глубокого обучения в ИИ.

Эта глава базируется на материале предыдущих глав книги, используя и интегрируя такие идеи, как обратное распространение, регуляризация, функция softmax, и так далее. Однако для чтения этой главы не обязательно детально прорабатывать материал всех предыдущих глав. Однако не помешает прочесть главу 1, и узнать об основах НС. Когда я буду использовать концепции из глав со 2 по 5, я буду давать нужные ссылки на материал по необходимости.

Стоит отметить, чего в этой главе нет. Это не обучающий материал по последним и самым крутым библиотекам для работы с НС. Мы не собираемся обучать ГНС с десятками слоёв для решения проблем с передового края исследований. Мы будем пытаться понять некоторые основные принципы, лежащие в основе ГНС, и применять их к простому и лёгкому для понимания контексту задач MNIST. Иначе говоря, эта глава не вынесет вас на передовой рубеж области. Стремление этой и предыдущих глав — сконцентрироваться на основах, и подготовить вас к пониманию широкого спектра современных работ.

Введение в свёрточные нейросети


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

839d0b54370af70f06b3f097897de457.png

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

24873ab052991e684b9ff0650c11a1c4.png

В частности, мы закодировали интенсивность каждого пикселя изображения в виде значения для соответствующего нейрона входного слоя. Для изображений размером 28×28 пикселей это означает, что у сети будет 784 (=28×28) входящих нейрона. Затем мы обучали веса и смещения сети так, чтобы на выходе сеть (была такая надежда) правильно идентифицировала входящее изображение: '0', '1', '2', …, '8', or '9'.

Наши ранние сети работают достаточно неплохо: мы достигли точности классификации выше 98%, используя обучающие и проверочные данные из набора рукописных цифр MNIST. Но если оценить эту ситуацию теперь, то покажется странным использовать сеть с полностью связанными слоями для классификации изображений. Дело в том, что такая сеть не принимает во внимание пространственную структуру изображений. К примеру, она совершенно одинаково относится как к пикселям, расположенным далеко друг от друга, так и к соседним пикселям. Предполагается, что выводы о таких концепциях пространственной структуры должны быть сделаны на основе изучения обучающих данных. Но что, если вместо того, чтобы начинать структуру сети с чистого листа, мы будем использовать архитектуру, пытающуюся воспользоваться пространственной структурой? В данном разделе я опишу свёрточные нейронные сети (СНС). Они используют специальную архитектуру, особенно подходящую для классификации изображений. Благодаря использованию такой архитектуры, СНС обучаются быстрее. А это помогает нам обучать более глубокие и многослойные сети, которые хорошо справляются с классификацией изображений. Сегодня глубокие СНС или некий близкий к ним вариант используются в большинстве случаев распознавания изображений.

Истоки СНС уходят в 1970-е. Но стартовой работой, с которой началось их современное распространение, стал труд 1998 года «Градиентное обучение для распознавания документов». Лекун сделал интересное замечание касательно терминологии, используемой в СНС: «Связь таких моделей, как свёрточные сети, с нейробиологией весьма поверхностна. Поэтому я называю их свёрточными сетями, а не свёрточными нейросетями, и поэтому мы называем их узлы элементами, а не нейронами». Но, несмотря на это, СНС используют множество идей из мира НС, которые мы уже изучили: обратное распространение, градиентный спуск, регуляризация, нелинейные функции активации, и т.д. Поэтому мы будем следовать общепринятому соглашению и считать их разновидностью НС. Я буду называть их как сетями, так и нейросетями, а их узлы — как нейронами, так и элементами.

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

Локальные рецептивные поля


В полносвязных слоях сетей входные слои обозначались вертикальными линиями нейронов. В СНС удобнее представлять входной слой в виде квадрата из нейронов с размерностью 28×28, значения которых соответствуют интенсивностям пикселей изображения 28×28:

da38489d04325743131546e76f99396d.png

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

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

cf971d5dc7106f1c56832c8416d7847a.png

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

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

c415cd64dfc93b81b89395ae360026c1.png

Сдвинем локальное рецептивное поле на один пиксель вправо (на один нейрон), чтобы связать его со вторым скрытым нейроном:

db1285a7a7009e0210e97253061054f3.png

Таким образом, мы построим первый скрытый слой. Отметьте, что если наше входящее изображение имеет размеры 28×28, а размеры локального рецептивного поля составляют 5×5, тогда в скрытом слое будет 24×24 нейрона. Это оттого, что мы можем сдвинуть локальное рецептивное поле только на 23 нейрона вправо (или вниз), а потом столкнёмся с правой (или нижней) стороной входящего изображения.

В данном примере локальные рецептивные поля двигаются по одному пикселю за раз. Но иногда используется другой размер шага. К примеру, мы могли бы сдвигать локальное рецептивное поле на 2 пикселя в сторону, и в этом случае можно говорить о размере шага 2. В данной главе мы в основном будем использовать шаг 1, но стоит знать, что иногда проходят эксперименты с шагами другого размера. С размером шага можно поэкспериментировать, как и с другими гиперпараметрами. Можно также изменять и размер локального рецептивного поля, однако обычно оказывается, что больший размер локального рецептивного поля лучше работает на изображениях, значительно больших, чем 28×28 пикселей.

Общие веса и смещения


Я упомянул, что у каждого скрытого нейрона есть смещение и 5×5 весов, связанных с его локальным рецептивным полем. Но я не упомянул, что мы будем использовать одинаковые веса и смещения для всех 24×24 скрытых нейронов. Иначе говоря, для скрытого нейрона j, k выход будет равен:

$ \sigma\left(b + \sum_{l=0}^4 \sum_{m=0}^4 w_{l,m} a_{j+l, k+m} \right) \tag{125} $

Здесь σ — функция активации, возможно, сигмоида из прошлых глав. b — общее значение смещения. wl, m — массив общих весов 5×5. И, наконец, ax, y обозначает входную активацию в позиции x, y.

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

По этой причине иногда мы называем сопоставление входящего слоя и скрытого слоя картой признаков. Веса, определяющие карту признаков, мы называем общими весами. А смещение, определяющее карту признаков — общим смещением. Часто говорят, что общие веса и смещение определяют ядро [kernel] или фильтр. Но в литературе люди иногда используют эти термины немного по другому поводу, и поэтому я не буду сильно углубляться в терминологию; лучше давайте посмотрим на несколько конкретных примеров.

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

f845dfa572668e27590c5bd1c057f849.png

В примере показано 3 карты признаков. Каждая карта определяется набором из общих весов 5×5 и одним общим смещением. В итоге такая сеть может распознать три разных типа признаков, и каждый признак она сможет найти в любой части изображения.

Три карты признаков я нарисовал для простоты. На практике же СНС могут использовать больше (возможно, гораздо больше) карт признаков. Одна из ранних СНС, LeNet-5, использовала 6 карт признаков, каждая из которых была связана с рецептивным полем 5×5, для распознавания цифр MNIST. Поэтому приведённый выше пример очень похож на LeNet-5. В примерах, которые мы будем самостоятельно разрабатывать далее, мы будем использовать свёрточные слои, содержащие по 20 и 40 карт признаков. Давайте быстренько посмотрим на те признаки, что мы изучим:

fada166b767b58edbff262944ee6488b.png

Эти 20 изображений соответствуют 20 различным картам признаков (фильтрам, или ядрам). Каждая карта представляется изображением 5×5, соответствующим 5×5 весам локального рецептивного поля. Белые пиксели означают малый (обычно, более отрицательный) вес, и на соответствующие им пиксели карта признаков реагирует меньше. Тёмные пиксели означают больший вес, и на соответствующие им пиксели карта признаков реагирует больше. Очень грубо говоря, эти изображения демонстрируют те признаки, на которые реагирует свёрточный слой.

Какие выводы можно сделать на основе этих карт признаков? Пространственные структуры здесь, очевидно, появились не случайным образом — у многих признаков видно явные светлые и тёмные участки. Это говорит о том, что наша сеть действительно обучается чему-то, связанному с пространственными структурами. Однако кроме этого достаточно сложно понять, что это за признаки. Мы явно не изучаем, допустим, фильтры Габора, которые использовались во многих традиционных подходах к распознаванию образов. На самом деле сейчас проводится большая работа, связанная с тем, чтобы лучше понять, какие именно признаки изучают СНС. Если вам это интересно, рекомендую начать с работы 2013 года.

Большое преимущество общих весов и смещений состоит в том, что это кардинально уменьшает количество параметров, имеющихся у СНС. Для каждой карты признаков нам понадобится 5×5=25 общих весов и одно общее смещение. Поэтому для каждой карты признаков требуется 26 параметров. Если у нас имеется 20 карт признаков, то всего у нас будет 20×26=520 параметров, определяющих свёрточный слой. Для сравнения, предположим, что у нас есть полносвязный первый слой с 28×28=784 входящими нейронами и относительно скромные 30 скрытых нейронов — такую схему мы использовали ранее во множестве примеров. Получается 784×30 весов, плюс 30 смещений, итого 23 550 параметров. Иначе говоря, у полносвязного слоя будет более чем 40 раз больше параметров, чем у свёрточного.

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

Кстати, название «свёрточные» происходит от операции в уравнении (125), которую иногда называют свёрткой. Точнее, иногда люди записывают это уравнение, как a1=σ (b+w∗a0), где a1 обозначает набор выходных активаций одной карты признаков, a0 — набор входных активаций, а * называется операцией свёртки. Мы не будем глубоко зарываться в математику свёрток, поэтому вам не нужно особенно беспокоиться по поводу этой связи. Но просто стоит знать, откуда взялось название.

Пулинговые слои


Кроме описанных свёрточных слоёв в СНС есть ещё и пулинговые слои. Они обычно используются сразу после свёрточных. Они занимаются тем, что упрощают информацию с выхода свёрточного слоя.

Здесь я использую фразу «карта признаков» не в значении функции, вычисляемой свёрточным слоем, а для обозначения активации выхода скрытых нейронов слоя. Такое вольное использование терминов часто встречается в исследовательской литературе.

Пулинговый слой принимает выход каждой карты признаков свёрточного слоя и готовит сжатую карту признаков. К примеру, каждый элемент пулингового слоя может просуммировать участок из, допустим, 2×2 нейронов предыдущего слоя. Конкретный пример: одна распространённая процедура пулинга известна, как макс-пулинг. В макс-пулинге элемент пулинга просто выдаёт максимальную активацию из участка 2×2, как показано на диаграмме:

dd0f6b86c374504de4ae58056a0f7008.png

Поскольку выход нейронов свёрточного слоя даёт 24×24 значения, после пулинга мы получим 12×12 нейронов.

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

a9568f8d10dd7dced2f682fe259aed48.png

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

Макс-пулинг — не единственная технология пулинга. Ещё один распространённый подход известен, как L2-пулинг. В нём вместо того, чтобы взять максимальную активацию региона нейронов 2×2, мы берём квадратный корень из суммы квадратов активаций региона 2×2. Детали подходов отличаются, но интуитивно он похож на макс-пулинг: L2-пулинг — это способ сжатия информации со свёрточного слоя. На практике часто используются обе технологии. Иногда люди используют другие типы пулинга. Если вы изо всех сил стараетесь оптимизировать качество работы сети, вы можете использовать подтверждающие данные для сравнения нескольких разных подходов к пулингу, и выбрать наилучший. Но мы не будем беспокоиться насчёт настолько подробной оптимизации.

Суммируя


Теперь мы можем свести всю информацию вместе и получить полноценную СНС. Она похожа на недавно рассмотренную нами архитектуру, однако у неё есть дополнительный слой из 10 выходных нейронов, соответствующих 10 возможным значениям цифр MNIST ('0', '1', '2',…):

b2dba18ee40b3f642fb9f4e9cbda772b.png

Сеть начинает с 28×28 входных нейронов, используемых для кодирования интенсивности пикселей изображения MNIST. После этого идёт свёрточный слой, использующий локальные рецептивные поля 5×5 и 3 карты признаков. В итоге получается слой из 3×24х24 скрытых нейронов признаков. Следующий шаг — слой макс-пулинга, применяемый к участкам 2×2, на каждой из трёх карт признаков. В итоге получается слой из 3×12х12 скрытых нейронов признаков.

Последний слой связей в сети полносвязный. То есть, он связывает каждый нейрон слоя макс-пулинга с каждым из 10 выходных нейронов. Такую полносвязную архитектуру мы использовали ранее. Обратите внимание, что на диаграмме выше я для простоты использовал одну стрелочку, не показывая всех связей. Вы легко можете представить себе их все.

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

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

Задача


  • Обратное распространение в свёрточной сети. Главными уравнениями обратного распространения в сети с полностью связанными слоями будут (BP1)-(BP4). Допустим, наша сеть содержит свёрточный слой, слой макс-пулинга и полностью связанный выходной слой, как в описанной выше сети. Как нужно изменить уравнения обратного распространения?


Свёрточные нейронные сети на практике


Мы обсудили идеи, лежащие в основе СНС. Давайте посмотрим, как они работают на практике, реализовав некоторые СНС, и применив их к задаче классификации цифр MNIST. Мы будем использовать программу network3.py, улучшенную версию программ network.py и network2.py, созданных в предыдущих главах. Программа network3.py использует идеи из документации библиотеки Theano (в частности, реализацию LeNet-5), из реализации исключения от Миша Денила и Криса Олаха. Код программы доступен на GitHub. В следующем разделе мы изучим код программы network3.py, а в данном разделе мы используем её, как библиотеку для создания СНС.

Программы network.py и network2.py были написаны на python с использованием матричной библиотеки Numpy. Они работали на основе первых принципов, и доходили до самых подробных деталей обратного распространения, стохастического градиентного спуска, и т.д. Но теперь, когда мы разбираемся в этих деталях, для network3.py мы будем использовать библиотеку машинного обучения Theano (см. научную работу с её описанием). Также Theano лежит в основе популярных библиотек для НС Pylearn2 и Keras, а также Caffe и Torch.

Использование Theano облегчает реализацию обратного распространения в СНС, поскольку автоматически подсчитывает все карты. Также Theano заметно выигрывает по скорости у нашего предыдущего кода (который был написан для облегчения понимания, а не для скоростной работы), поэтому её разумно использовать для обучения более сложных сетей. В частности, одной из великолепных возможностей Theano является запуск кода как на CPU, так и на GPU, при наличии. Запуск на GPU даёт значительный прирост в скорости, и помогает обучать более сложные сети.

Чтобы работать параллельно с книгой, вам нужно установить Theano на своей системе. Для этого следуйте инструкциям на домашней странице проекта. На момент написания книги и запуска примеров существовала версия Theano 0.7. Некоторые эксперименты я запускал на Mac OS X Yosemite без GPU. Некоторые на Ubuntu 14.04 с NVIDIA GPU. А некоторые — и там, и там. Чтоб запустить network3.py, установите в коде флажок GPU в значение True или False. Кроме этого, для запуска Theano на GPU вам могут помочь следующие инструкции. Также в сети легко найти обучающие материалы. Если у вас нет своего GPU, можете посмотреть в сторону Amazon Web Services EC2 G2. Но даже при наличии GPU наш код будет работать не очень быстро. Многие эксперименты будут идти от нескольких минут до нескольких часов. Самые сложные из них на одном только CPU будут выполняться по нескольку дней. Как и в предыдущих главах, рекомендую запускать эксперимент, и продолжать чтение, периодически проверяя его работу. Без использования GPU рекомендую вам уменьшить количество эпох обучения для самых сложных экспериментов.

Чтобы получить базовые результаты для сравнения, начнём с неглубокой архитектуры с одним скрытым слоем, содержащим 100 скрытых нейронов. Мы будем обучаться 60 эпох, использовать скорость обучения η=0,1, размер мини-пакета 10, и будем учиться без регуляризации.

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

>>> import network3
>>> from network3 import Network
>>> from network3 import ConvPoolLayer, FullyConnectedLayer, SoftmaxLayer
>>> training_data, validation_data, test_data = network3.load_data_shared()
>>> mini_batch_size = 10
>>> net = Network([
        FullyConnectedLayer(n_in=784, n_out=100),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 
            validation_data, test_data)


Наилучшая точность классификации составила 97,80%. Это точность классификации test_data, оценённая по обучающей эпохе, в которой мы получили наилучшую точность классификации данных из validation_data. Использование подтверждающих данных для принятия решения об оценке точности помогает избежать переобучения. Далее мы так и будем поступать. Ваши результаты могут немного отличаться, поскольку веса и смещения сети инициализируются случайным образом.

Точность в 97,80% довольно близка к точности в 98,04%, полученной в главе 3, с использованием сходной архитектуры сети и гиперпараметров обучения. В частности, в обоих примерах используются неглубокие сети с одним скрытым слоем, содержащим 100 скрытых нейронов. Обе сети обучаются 60 эпох с размером мини-пакета 10 и скоростью обучения η=0,1.

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

Можем ли мы улучшить результаты, используя более глубокую архитектуру сети?

Начнём со вставки свёрточного слоя, в самом начале сети. Мы будем использовать локальное рецептивное поле 5×5, шаг длиной 1 и 20 карт признаков. Мы также вставим слой макс-пулинга, комбинирующий признаки при помощи окон пулинга 2×2. Так что общая архитектура сети будет выглядеть похоже на ту, что мы обсуждали в предыдущем разделе, но с дополнительным полносвязным слоем:

7ca1788d2206313b37a6f8896086b582.png

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

Давайте обучим такую сеть, и посмотрим, как она себя поведёт.

>>> net = Network([
        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 
                      filter_shape=(20, 1, 5, 5), 
                      poolsize=(2, 2)),
        FullyConnectedLayer(n_in=20*12*12, n_out=100),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 
            validation_data, test_data)   


Мы получаем точность в 98,78%, что значительно выше любого из предыдущих результатов. Мы уменьшили ошибку больше, чем на треть — великолепный результат.

Описывая структуру сети, я считал свёрточные и пулинговые слои единым слоем. Рассматривать их, как отдельные слои, или как единый слой — вопрос предпочтений. network3.py считает их одним слоем, поскольку так код получается компактнее. Однако легко изменить network3.py так, чтобы слои можно было задавать по отдельности.

Упражнение


  • Какую точность классификации мы получим, если опустим полносвязный слой, и будем использовать только свёрточный/пулинговый слой и слой softmax? Помогает ли включение полносвязного слоя?


Можем ли мы улучшить результат в 98,78%?

Попробуем вставить второй свёрточный/пулинговый слой. Мы вставим его между существующим свёрточным/пулинговым и полносвязным скрытым слоями. Мы снова используем локальное рецептивное поле 5×5 и пул по участкам 2×2. Посмотрим, что случится, когда мы обучим сеть с примерно такими же гиперпараметрами, что и ранее:

>>> net = Network([
        ConvPoolLayer(image_shape=(mini_batch_size, 1, 28, 28), 
                      filter_shape=(20, 1, 5, 5), 
                      poolsize=(2, 2)),
        ConvPoolLayer(image_shape=(mini_batch_size, 20, 12, 12), 
                      filter_shape=(40, 20, 5, 5), 
                      poolsize=(2, 2)),
        FullyConnectedLayer(n_in=40*4*4, n_out=100),
        SoftmaxLayer(n_in=100, n_out=10)], mini_batch_size)
>>> net.SGD(training_data, 60, mini_batch_size, 0.1, 
            validation_data, test_data)      


И вновь у нас улучшение: теперь мы получаем точность в 99,06%!

На текущий момент возникает два естественных вопроса. Первый:, а что вообще означает применение второго свёрточного/пулингового слоя? Вы можете считать, что у второго свёрточного/пулингового слоя на вход приходят «изображения» размером 12×12, чьи «пиксели» представляют наличие (или отсутствие) определенных локализованных признаков в изначальной входящей картинке. То есть, можно считать, что на вход этому слою приходит некий вариант изначальной входящей картинки. Это будет более абстрактная и сжатая версия, но у неё всё равно есть достаточно пространственной структуры, поэтому имеет смысл использовать для её обработки второй свёрточно/пулинговый слой.

Приятная точка зрения, но она порождает второй вопрос. На выходе с предыдущего слоя получается 20 отдельных КП, поэтому на второй свёрточно/пулинговый слой приходит 20×12х12 групп входных данных. Получается, что у нас есть как бы 20 отдельных изображений, входящих на свёрточно/пулинговый слой, а не одно изображение, как это было в случае с первым свёрточно/пулинговым слоем. Как же нейроны из второго свёрточно/пулингового слоя должны реагировать на множество этих входящих изображений? На самом деле мы просто позволим каждому нейрону этого слоя обучаться на основе всех 20×5х5 входящих в его локальное рецептивное поле нейронов. Говоря менее формальным языком, у детекторов признаков во втором свёрточно/пулинговом слое будет доступ ко всем признакам первого слоя, но только в рамках их конкретных локальных рецептивных полей.

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

Задача


  • Использование функции активации в виде гиперболического тангенса. Ранее в этой книге я уже несколько раз упоминал свидетельства в пользу того, что функция tanh, гиперболический тангенс, может лучше подойти на роль функции активации, чем сигмоида. Мы ничего с этим не делали, поскольку у нас и с сигмоидой был хороший прогресс. Но давайте попробуем провести несколько экспериментов с tanh в качестве функции активации. Попробуйте обучить сеть с танг-активацией со свёрточными и полносвязными слоями (вы можете передать activation_fn=tanh как параметр классам ConvPoolLayer и FullyConnectedLayer). Начните с тех же гиперпараметров, что были у сигмоидной сети, но обучайте сеть 20 эпох, а не 60. Как ведёт себя сеть? Что будет, если продолжить до 60-й эпохи? Попробуйте построить график точности подтверждения работы по эпохам для тангенса и сигмоиды, вплоть до 60-й эпохи. Если ваши результаты будут похожими на мои, вы обнаружите, что сеть на основе тангенса обучается чуть быстрее, но итоговая точность обеих сетей одинаковая. Можете объяснить, почему так происходит? Можно ли достичь той же скорости обучения при помощи сигмоиды — например, изменив скорость обучения или через масштабирование (вспомните, что σ (z)=(1+tanh (z/2))/2)? Попробуйте пять-шесть разных гиперпараметров или архитектур сети, поищите, где тангенс может опережать сигмоиду. Отмечу, что эта задача открытая. Лично я не нашёл каких-то серьёзных преимуществ в переходе на тангенс, хотя я не проводил всеобъемлющих экспериментов, и, возможно, вы их найдёте. В любом случае, скоро мы найдём преимущество в переходе на выпрямленную линейную функцию активации, поэтому больше не будем углубляться в вопрос с гиперболическим тангенсом.


Использование выпрямленных линейных элементов


Разработанная нами на текущий момент сеть является одним из вариантов сетей, использованных в плодотворной работе 1998 года, в которой была впервые представлена задача MNIST — сети под названием LeNet-5. Это хорошая основа для дальнейших экспериментов, дл

© Habrahabr.ru