Описание свёрточной нейронной сети

Переходная инвариантность

Доброго всем дня и добро пожаловать на занятия по теме «Свёрточные нейронные сети: глубокое обучение на языке Python, часть 3».

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

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

Предположим, мы пытаемся классифицировать объекты, а входные данные содержат, кроме прочего, рисунок автомобиля. Если рисунок автомобиля переместить немного в сторону, мы это увидим, поскольку изображён всё тот же автомобиль. Однако нейронная сеть со всеми различными видами весовых коэффициентов может дать сбой и выдать неправильный ответ – возможно, потому что она не была обучена видеть автомобиль именно в этом месте и не может понять, что изображено.

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

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

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

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

Архитектура свёрточной нейронной сети

Все сети, которые мы создавали до сих пор, имели одну общую черту – каждый узел одного слоя связан со всеми узлами следующего слоя. Вы увидите, как это меняется в случае свёрточных нейронных сетей. Учтите, что большая часть этого материала почерпнута из работы Лекуна 1998 года, особенно модель LeNet.

Итак, почему же свёртка?

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

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

Ещё одна важная операция, которая понадобится нам перед созданием свёрточной нейронной сети, – это понижение дискретизации (downsampling).

Вспомните наш аудиопример, когда мы создавали эхо. Там частота равнялась 16 килогерц. Почему именно 16 килогерц? Потому что это достаточная величина дискретизации для воспроизведения голосов. Телефон имеет частоту дискретизации 8 килогерц, поэтому голос в телефоне звучит несколько приглушённо.

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

Для этого есть несколько способов.

Один из них называется максимизационным агрегированием (max pooling). Это когда мы берём блок 2×2 или 3×3, или любого другого размера, а в качестве результата выбираем максимальное значение величины из этого блока. Второй способ – усредняющее агрегирование (average pooling), когда мы берём среднее значение величин, входящих в блок. Мы будем использовать максимизационное агрегирование. Theano и TensorFlow имеют для этого специальные функции – в Theano это theano.tensor.signal.downsample.max_pool_2d, а в TensorFlow это tf.nn.max_pool.

Наконец мы достигли момента, когда можем описать схему свёрточной нейронной сети в версии LeNet. По сути это просто объединение уже обсуждавшихся нами операций. В первом слое мы берём изображение с сохранением всех цветов и первоначальной формы – то есть без сглаживания. Затем проводим свёртывание. Далее – максимизационное агрегирование, чтобы уменьшить размер признаков. После этого мы проводим следующую свёртку и следующее агрегирование. И наконец после этих двух свёрток и агрегирований мы преобразуем признаки в вектор и вводим в обычную полносвязную нейронную сеть вроде уже рассматривавшихся.

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

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

Обратите внимание, что порядок следования размерностей в некоторой мере произволен. К примеру, в файлах Matlab количество образцов N представлено в качестве последней размерности, тогда как в Theano оно идёт первым. Далее мы увидим, что в TensorFlow размерность ядер отличается от таковой в Theano.

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

Связь с биологией

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

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

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

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

В статье, написанной в 1968 году, отмечалось, что существует два типа клеток – простые и сложные. В Википедии я нашёл цитату, которая, на мой взгляд, прекрасно иллюстрирует это обстоятельство: «Рецептивные поля клеток каждого уровня зрительной системы формируются синаптическими соединениями с клетками более низкого уровня этой системы. В этом случае небольшие и просто устроенные рецептивные поля могут комбинироваться, формируя обширные и сложные рецептивные поля».

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

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

Градиент свёртки и агрегирования

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

В этом курсе мы вводим два новых типа операций, которые могут встретиться в нейронной сети, – свёртка и агрегирование. Вопрос теперь заключается в следующем: если мы захотим создать свёрточную нейронную сеть с нуля – скажем, с помощью Numpy Python или даже С++ – как это сделать? Нам понадобится градиент для свёрточного и агрегационного слоёв. Как же его найти?

Рассмотрим эту задачу в два этапа, поскольку они совершенно различны. Как нам найти градиент для свёртки?

y(m,n) = \sum _i \sum _j w(i,j) x (m-i, \; n-j),

\frac {\partial y (m,n)} {\partial w (i, j)} = ?

Ключ в том, чтобы начать с первопричины. Давайте выпишем уравнение для свёртки с помощью уже известных нам операций – сложения и умножения. Мы видим, что w представлено в первой степени, а следовательно, его производная равна просто единице, а суммы отбрасываются, поскольку мы рассматриваем лишь конкретное i и конкретное j:

\frac {\partial y (m,n)} {\partial w (i, j)} = x (m - i, \; n-j).

Просто, не так ли? Теперь просто подставляем это выражение в нейронную сеть с использованием цепного правила, как мы это делали в курсе по глубокому обучению, часть 1.

А что с производной агрегирования?

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

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

А что это значит с точки зрения градиента свёртки и агрегирования?

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

Андрей Никитенко
Андрей Никитенко
Задать вопрос эксперту
Понравилась статья? Поделить с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!:

Share via
Copy link