Глубокое обучение с нулевым входом (4) - сверточная нейронная сеть
机器学习
深度学习入门
Является ли грядущая эра эрой больших данных или искусственного интеллекта, или эрой традиционных отраслей, использующих искусственный интеллект для обработки больших данных в облаке, как программист с идеалами и стремлениями, он не понимает превосходного процесса глубокого обучения ( Глубокое обучение) Горячая технология, будет ли ощущение, что она выйдет сразу? Теперь наступает спасительная соломинка: серия статей «Введение в глубокое обучение с нулевыми основами» призвана помочь любителям программирования достичь начального уровня с нулевых основ. Нулевой фундамент означает, что вам не нужно слишком много математических знаний, если вы можете писать программы, да, эта статья написана специально для программистов. Хотя в тексте будет много формул, которые вы можете не понять, но в то же время будет больше кодов, понятных вам как программисту (меня окружает группа фанатичных чистильщиков Программист кода, поэтому я не могу писать плохой код).
Список статей
Глубокое обучение с нулевой базой (1) — персептрон
Глубокое обучение с нулевым входом (2) — линейная единица и градиентный спуск
Введение в глубокое обучение с нулевыми основами (3) — нейронные сети и алгоритмы обратного распространения
Глубокое обучение с нулевым входом (4) - сверточная нейронная сеть
Введение в глубокое обучение с нулевыми основами (5) — рекуррентная нейронная сеть
Глубокое обучение с нулевым входом (6) — сеть с долговременной кратковременной памятью (LSTM)
Глубокое обучение с нулевым входом (7) — рекуррентная нейронная сеть
прошлый обзор
В предыдущей статье мы представили полносвязную нейронную сеть, ее обучение и использование. Мы использовали его для распознавания рукописных цифр, однако такая структура сети не очень подходит для задач распознавания изображений. В этой статье будет представлена структура нейронной сети, которая больше подходит для задач распознавания изображений и речи —Сверточная нейронная сеть(Сверточная нейронная сеть, CNN). Не будет преувеличением сказать, что сверточная нейронная сеть является самым важным типом нейронной сети. Она блистала в последние годы. Почти все важные прорывы в области распознавания изображений и речи были достигнуты с помощью сверточных нейронных сетей, таких как Google. GoogleNet, Microsoft ResNet и т. д. AlphaGo, победившая Ли Шиши, также использовала этот вид сети. В этой статье будет подробно рассказаноСверточная нейронная сетьи его алгоритм обучения, а также практическая реализация простогоСверточная нейронная сеть.
Новая функция активации - Relu
В последние годы в сверточной нейронной сети функция активации часто выбирает не сигмовидную или тангенциальную функцию, а функцию relu. Определение функции Relu:
Изображение функции Relu показано на следующем рисунке:
В качестве функции активации функция Relu имеет следующие преимущества:
- высокоскоростнойПо сравнению с сигмовидной функцией, которая должна вычислять показатель степени и обратную величину, функция relu на самом деле является максимальной (0, x), и стоимость вычисления намного ниже.
- Облегчить проблему исчезающего градиентаНапомним формулу расчета градиента. в,является производной сигмовидной функции. При использовании алгоритма обратного распространения для вычисления градиента каждый раз, когда проходит слой сигмовидных нейронов, градиент умножается на. Как видно из рисунка ниже,Максимальное значение функции равно 1/4. Поэтому умножьте на единицуЭто приведет к тому, что градиент будет становиться все меньше и меньше, что является большой проблемой для обучения глубоких сетей. Производная функции relu равна 1, что не приводит к уменьшению градиента. Конечно, функция активации является лишь одним из факторов, вызывающих уменьшение градиента, но в любом случае relu превосходит сигмоид в этом отношении. Использование функции активации relu позволяет обучать более глубокие сети.
- РазреженностьБлагодаря исследованиям головного мозга установлено, что при работе активируются только около 5% нейронов головного мозга, в то время как скорость активации искусственной нейронной сети, использующей сигмовидную функцию активации, составляет около 50%. В некоторых статьях утверждается, что искусственные нейронные сети идеальны при уровне активации 15-30%. Поскольку функция relu полностью неактивна, когда вход меньше 0, можно получить более низкую скорость активации.
Полностью подключенные сети против сверточных сетей
Причина, по которой полносвязная нейронная сеть не подходит для задач распознавания изображений, в основном заключается в следующих проблемах:
- Слишком много параметровРассмотрим входное изображение размером 1000*1000 пикселей (один миллион пикселей, что больше не является большим изображением), а входной слой имеет 1000*1000=1 миллион узлов. Если предположить, что первый скрытый слой имеет 100 узлов (что не так много), то только этот слой имеет (1000*1000+1)*100=100 миллионов параметров, что слишком много! Мы видим, что если изображение только немного увеличить, то количество параметров будет намного больше, поэтому оно плохо масштабируется.
- Не использует информацию о положении между пикселямиДля задач распознавания изображений связь между каждым пикселем и окружающими его пикселями относительно близка, а связь с удаленными пикселями может быть очень небольшой. Если нейрон связан со всеми нейронами предыдущего слоя, это эквивалентно обработке всех пикселей изображения одинаково для одного пикселя, что не соответствует предыдущему предположению. Когда мы закончим изучение весов каждой связи, мы можем в конце концов обнаружить, что существует большое количество весов, и все они имеют очень маленькие значения (то есть связи на самом деле не имеют значения). Попытка выучить много неважных весов обязательно будет очень неэффективной.
- лимит сетевого уровняМы знаем, что чем больше слоев сети, тем сильнее выразительная способность, но глубокую полносвязную нейронную сеть трудно обучить методом градиентного спуска, потому что градиент полносвязной нейронной сети трудно пройти более 3 слоев . Поэтому получить очень глубокую полносвязную нейронную сеть у нас невозможно, что ограничивает ее возможности.
Итак, как сверточные нейронные сети решают эту проблему? Есть три основные идеи:
- местное соединениеЭто проще всего представить, каждый нейрон больше не связан со всеми нейронами предыдущего слоя, а только с небольшим количеством нейронов. Это снижает многие параметры.
- разделение весаВместо каждого соединения, имеющего другой вес, набор соединений может делиться один и тот же вес, который снова уменьшает множество параметров.
- понижение частоты дискретизацииОбъединение можно использовать для уменьшения количества выборок на слой, дальнейшего уменьшения количества параметров и в то же время повышения надежности модели.
Для задач распознавания изображений сверточные нейронные сети достигают лучшего эффекта обучения, сохраняя как можно больше важных параметров и удаляя большое количество неважных параметров.
Далее мы подробно расскажем, что на самом деле представляют собой сверточные нейронные сети.
Что такое сверточная нейронная сеть
Во-первых, давайте получим перцептивное понимание.На следующем рисунке представлена схематическая диаграмма сверточной нейронной сети:
Сетевая архитектура
какРисунок 1Как показано, сверточная нейронная сеть состоит из несколькихсверточный слой,Объединенный слой,полносвязный слойсочинение. Вы можете построить множество различных сверточных нейронных сетей, и их общие архитектурные шаблоны:
INPUT -> [[CONV]*N -> POOL?]*M -> [FC]*K
То есть накладываются N сверточных слоев, затем (необязательно) накладывается слой объединения, эта структура повторяется M раз, и, наконец, накладываются K полносвязных слоев.
заРисунок 1Показана сверточная нейронная сеть:
INPUT -> CONV -> POOL -> CONV -> POOL -> FC -> FC
В соответствии с приведенным выше шаблоном это может быть выражено как:
INPUT -> [[CONV]*1 -> POOL]*2 -> [FC]*2
Это:N=1, M=2, K=2
.
трехмерная многослойная структура
отРисунок 1мы можем узнатьСверточная нейронная сетьструктура слоя иПолностью подключенная нейронная сетьСтруктура слоев очень разнообразна.Полностью подключенная нейронная сетьНейроны в каждом слоеОдномерныйрасположены, то есть выстроены в линию; иСверточная нейронная сетьНейроны в каждом слоетрехмерныйАранжированные, то есть расположенные в прямоугольной форме, естьширина,высокийиглубина.
заРисунок 1Показывая нейронную сеть, мы видим, что ширина и высота входного слоя соответствуют ширине и высоте входного изображения, а его глубина равна 1. Затем первый сверточный слой выполняет операцию свертки над этим изображением (о том, как вычислить свертка, мы поговорим позже) и получаем три карты признаков. «3» здесь может сбить с толку многих новичков. На самом деле, этот слой свертки содержит три фильтра, то есть три набора параметров. Каждый фильтр может свернуть исходное входное изображение для получения карты объектов. Три фильтра могут получить три объекта Карты. Что касается того, сколько фильтров может иметь сверточный слой, его можно установить свободно. То есть количество Фильтров сверточного слоя тоже равно единице.Гиперпараметры. Мы можем поставить Карту можно рассматривать как признак изображения, извлеченный с помощью преобразования свертки.Три фильтра извлекают три разных набора признаков из исходного изображения, то есть получаются три Карты признаков, также называемые тремяканал.
Продолжайте наблюдатьРисунок 1, после первого сверточного слоя слой объединения создает три карты объектов.понижение частоты дискретизации(Мы поговорим о том, как рассчитать даунсэмплинг позже), и получите три меньшие карты признаков. Затем, второйсверточный слой, он имеет 5 фильтров. Каждый Fitler ставит фронтпонижение частоты дискретизацииПосле3**Карты функцийсверткавместе, получите новую карту функций. Таким образом, 5 фильтров получают 5 карт функций. Далее идет второй пул, который продолжает выполняться на 5 картах функций.Даунсэмплинг**, есть 5 меньших функций Карта.
Рисунок 1Последние два слоя показанной сети являются полносвязными слоями. Каждый нейрон в первом полносвязном слое соединен с каждым нейроном в 5 картах объектов предыдущего слоя, а каждый нейрон во втором полносвязном слое (то есть выходном слое) соединен с первым полносвязным Каждый нейрон полносвязного слоя связан, так что получается выход всей сети.
До сих пор у нас естьСверточная нейронная сетьС самыми элементарными перцептивными знаниями. Далее мы представимСверточная нейронная сетьРасчет и обучение различных слоев в
Расчет выходного значения сверточной нейронной сети
Расчет выходного значения сверточного слоя
Мы используем простой пример, чтобы описать, как вычислитьсвертка, то абстрагируемсясверточный слойНекоторые важные понятия и методы расчета .
Предположим, есть изображение 5*5, используйте фильтр 3*3 для свертки и хотите получить карту признаков 3*3, как показано ниже:
для четкого описаниясверткаВ процессе расчета мы сначала нумеруем каждый пиксель изображения, используяпредставляет собой первыйрядЭлемент столбца; пронумеруйте каждый вес фильтра, используйтеозначает первыйрядвеса колонны, сПредставляет фильтрПредвзятый термин;Пронумеруйте каждый элемент Карты характеристик, используйтеПредставляет первую часть карты объектов.рядэлемент столбца; свыражатьфункция активации(Этот пример выбираетрелу функциякак функция активации). Затем свертка рассчитывается по следующей формуле:
Режим
Например, для верхнего левого элемента карты объектовНапример, метод расчета свертки:
Результат расчета показан на следующем рисунке:
Далее элементы карты признаковМетод расчета свертки:
Результат расчета показан на следующем рисунке:
Значения всех элементов в Feature Map можно вычислить по очереди. Анимация ниже показывает процесс расчета всей карты признаков:
В приведенном выше процессе расчета шаг равен 1. Шаг может быть установлен на число больше 1. Например, если шаг равен 2, карта признаков рассчитывается следующим образом:
Мы заметили, что когдаСтрайдЕсли установлено значение 2, карта объектов превращается в 2 * 2. Это показывает, что размер изображения, шаг и размер карты объектов после свертки связаны. Фактически они удовлетворяют следующим соотношениям:
формула
В приведенных выше двух формулахширина карты признаков после свертки;ширина изображения до свертки;ширина фильтра;даZero Paddingколичество,Zero Paddingотносится к дополнению несколькими кругами нулей вокруг исходного изображения, еслиЗначение равно 1, затем добавьте 1 круг 0;дашаг;высота карты признаков после свертки;ширина изображения до свертки.Формула 2иФормула 3По сути то же самое.
В предыдущем примере ширина изображения, ширина фильтра,Zero Padding,шаг,но
Описание Ширина карты объекта равна 2. Точно так же мы можем рассчитать, что высота карты объектов также равна 2.
Мы уже говорили о методе расчета сверточного слоя глубиной 1. Как считать, если глубина больше 1? На самом деле похоже. Если глубина изображения до свертки равна D, то глубина соответствующего фильтра также должна быть равна D. Давайте расширимФормула 1, получается формула расчета свертки с глубиной больше 1:
Режим
существуетФормула 4В, D — глубина, F — размер фильтра (ширина или высота, оба одинаковы);Указывает первый фильтрслойрядвес колонны;представляет собой первыйслойрядПиксели столбца, другие значения символов иФормула 1одинаковы и здесь повторяться не будут.
Мы также упоминали ранее, что каждый сверточный слой может иметь несколько фильтров. После свертки каждого фильтра с исходным изображением можно получить карту признаков. Следовательно, глубина (число) карты объектов после свертки совпадает с количеством фильтров в слое свертки.
Анимация ниже показывает вычисление сверточного слоя, содержащего два фильтра. Мы видим, что вход 7*7*3 после свертки двух фильтров 3*3*3 (с шагом 2) дает результат 3*3*2. Кроме того, мы также увидим следующий рисунокZero paddingравен 1, то есть вокруг входного элемента добавляется круг из 0.Zero paddingЭто очень полезно для выделения признаков краевых частей изображения.
Выше приведен метод расчета слоя свертки. Здесь отраженоместное соединениеиразделение веса: Каждый слой нейронов связан только с некоторыми нейронами в предыдущем слое (правила расчета свертки), а вес фильтра одинаков для всех нейронов в предыдущем слое. Для сверточного слоя, содержащего два фитлера 3*3*3, количество параметров составляет всего (3*3*3+1)*2=56, а количество параметров не зависит от количества нейронов в предыдущем слое. иПолностью подключенная нейронная сетьДля сравнения, количество параметров значительно уменьшается.
Используйте формулу свертки, чтобы выразить вычисление слоя свертки
Читатели, которые не хотят знать слишком много математических деталей, могут пропустить этот раздел, не затрагивая понимания всего текста.
Формула 4Выражение очень громоздкое, лучше всего его упростить. Так же, как матрица может быть использована для упрощения выраженияПолностью подключенная нейронная сетьКак и при расчете, используемФормула сверткиможно упроститьСверточная нейронная сетьвыражение.
Ниже мы представляемФормула двумерной свертки.
установить матрицу,, количество строк и столбцов,,,,ноФормула двумерной сверткиследующее:
и,Чтобы выполнить условия.
Мы можем записать приведенное выше уравнение как
Режим
если мы последуемФормула 5Для вычисления свертки мы можем обнаружить, что матрица A на самом деле является фильтром, а матрица B — входными данными для свертки, и отношение позиций также отличается:
Как видно из рисунка выше, значение в верхнем левом углу AЗначение в правом нижнем углу блока, соответствующее BУмножить, а не с верхней левойУмножить. следовательно,математикаСвертка в иСверточная нейронная сетьРазница в «свертке» в , во избежание путаницы, положимСверточная нейронная сетьОперация «свертка» в называетсявзаимная корреляцияработать.
сверткаиКорреляцияДействия конвертируются. Сначала переворачиваем матрицу А на 180 градусов, а затем меняем местами А и В (то есть ставим В слева, а А справа. Свертка удовлетворяет обменному курсу, и эта операция не приводит к результату менять), тосверткаэто становитсяКорреляция.
Если не учитывать небольшую разницу между ними, мы можемФормула 5Заменить наФормула 4:
Режим
в,— это карта объектов, выводимая сверточным слоем. такой жеФормула 4по сравнению с,Формула 6Гораздо проще. Однако это краткое обозначение подходит только для шага 1.
Расчет выходного значения слоя Pooling
Основная функция слоя объединения состоит в том, чтобыпонижение частоты дискретизации, что еще больше сокращает количество параметров за счет удаления неважных сэмплов на карте функций. Существует множество методов объединения, наиболее часто используемым являетсяMax Pooling.Max PoolingФактически максимальное значение берется из n*n выборок в качестве значения выборки после выборки. На следующем рисунке показано максимальное объединение 2 * 2:
КромеMax PooingКроме того, обычно используетсяMean Pooling- Возьмите среднее значение каждого образца.
Для карты объектов с глубиной D каждый слой выполняет объединение независимо друг от друга, поэтому глубина после объединения по-прежнему равна D.
полносвязный слой
Вычисление выходного значения полносвязного слоя и предыдущей статьиВведение в глубокое обучение с нулевыми основами (3) — нейронные сети и алгоритмы обратного распространениясказалПолностью подключенная нейронная сетьЭто то же самое и не будет повторяться здесь.
Обучение сверточных нейронных сетей
иПолностью подключенная нейронная сетьпо сравнению с,Сверточная нейронная сетьОбучение немного сложнее. Но принцип обучения тот же: используйте цепной вывод для вычисления частной производной (градиента) функции потерь по отношению к каждому весу, а затем обновите веса в соответствии с формулой градиентного спуска. Алгоритм обучения по-прежнему является алгоритмом обратного распространения ошибки.
Давайте сначала вспомним предыдущую статьюВведение в глубокое обучение с нулевыми основами (3) — нейронные сети и алгоритмы обратного распространенияВведен алгоритм обратного распространения, весь алгоритм разделен на три этапа:
- прямое вычисление каждого нейронавыходное значение(представляет собой первыйнейроны, то же ниже);
- Обратный расчет каждого нейронаОшибки,Также называется в некоторой литературечувствительность(чувствительность). На самом деле это функция потерь сетик нейронамвзвешенный вводЧастная производная от , то есть;
- Рассчитайте вес соединения каждого нейронаизградиент(представлен нейрономсвязаны с нейронамивес), формула,в,представляет собой нейронВывод.
Наконец, обновите каждый вес в соответствии с правилом градиентного спуска.
Для сверточных нейронных сетей из-заместное соединение,понижение частоты дискретизациии другие операции, влияющие на второй шагОшибкиКонкретный метод расчета , иразделение весавлияет на третий шагВесаизградиентметод расчета. Далее мы вводим алгоритмы обучения для сверточного слоя и слоя объединения соответственно.
Обучение сверточных слоев
Для сверточного слоя давайте посмотрим на второй шаг выше, то есть какОшибкиПередайте его предыдущему слою, затем посмотрите на третий шаг, то есть как рассчитать каждый вес фильтраизградиент.
Передача члена ошибки свёрточного слоя
Передача ошибок в простейшем случае
Давайте сначала рассмотрим простейший случай, когда размер шага равен 1, глубина ввода равна 1, а количество фильтров равно 1.
Предполагая, что размер ввода равен 3 * 3, размер фильтра - 2 * 2, а шаг - 1 свертка, мы получим 2 * 2.feature map. Как показано ниже:
На рисунке выше мы пронумеровали каждый элемент для простоты описания. использоватьозначает первыйслойрядстолбецОшибки;использоватьСначала указывает фильтррядвеса колонны, сПредставляет фильтрПредвзятый термин;использоватьозначает первыйслойрядряд нейроноввывод;использоватьозначает первыйряд нейроноввзвешенный ввод;использоватьозначает первыйслойрядстолбецОшибки;использоватьозначает первыйслоистыйфункция активации. Отношения между ними следующие:
В приведенной выше формуле,,массивы,ОтмассивПредставляет операцию свертки.
Здесь мы предполагаем, чтокаждый изЗначения были рассчитаны, что нам нужно сделать, это рассчитать первыйслой для каждого нейронаОшибки .
По правилу вывода цепочки:
Попросим первого. Давайте сначала рассмотрим несколько частных случаев, а затем выведем из них общие правила.
Пример 1, расчет,только сРасчет связан с:
следовательно:
Пример 2, расчет,ииВсе расчеты касаются:
следовательно:
Пример 3, расчет,и,,иВсе расчеты касаются:
следовательно:
Из вышеприведенных трех примеров, воспользуемся воображением, нетрудно найти, вычислить, что равносильно тому, чтоВокруг чувствительной карты слоя добавляется кружок 0, а фильтр переворачивается на 180 градусов.cross-correlation, можно получить желаемый результат, как показано на следующем рисунке:
так каксверткаЭквивалентно повороту фильтра на 180 градусовcross-correlation, поэтому расчет на приведенном выше рисунке может быть идеально выражен формулой свертки:
в приведенной выше формулеозначает первыйМассив весов для фильтра слоя. Также можно расширить свертку приведенной выше формулы и записать ее в виде суммирования:
Теперь давайте попросим второй элемент. так как
Итак, этот пункт чрезвычайно прост, просто попросите функцию активацииПроизводная от .
Объединив первое и второе слагаемые, получим окончательную формулу:
Режим
так же может бытьФормула 7Написано в форме свертки:
Режим
Среди них символвыражатьelement-wise product, то есть умножить каждый соответствующий элемент в матрице. УведомлениеФормула 8середина,,обаматрица.
Выше приведен простейший случай, когда размер шага равен 1, входная глубина равна 1, количество фильтров равно 1, и алгоритм передачи члена ошибки сверточного слоя. Теперь давайте выведем случай, когда размер шага равен S.
Распространение ошибки, когда шаг свертки равен S
Давайте сначала посмотрим на разницу между размером шага S и размером шага 1.
Как показано выше, вверху — результат свертки, когда шаг равен 1, а внизу — результат свертки, когда шаг равен 2. Мы видим, что, поскольку шаг равен 2, результирующая карта признаков пропускает соответствующую часть шага 1. Поэтому, когда мы вычисляем в обратном порядкеОшибки, мы можем добавить 0 в соответствующую позицию карты чувствительности с размером шага S, и «восстановить» его на карту чувствительности с размером шага 1, а затем использоватьФормула 8решать.
Распространение ошибки, когда глубина входного слоя равна D
Когда глубина ввода равна D, глубина фильтра также должна быть D,слоистыйканал работает только с фильтромРассчитываются веса каналов. Поэтому обратный расчетОшибки, мы можем использоватьФормула 8, используйте первый фильтрпара веса каналаКарта чувствительности слоя свернута для получения первогоЭтажКарта чувствительности канала. Как показано ниже:
Распространение ошибки при количестве фильтров N
Когда количество фильтров равно N, глубина выходного слоя также равна N.Свертка фильтра создает выходной слойкарты характеристик. Из-заЭтажкаждый взвешенный входоба повлияли наВыходные значения всех карт признаков слоя, следовательно, обратный расчетОшибки, необходимо использовать полную формулу производной. То есть сначала используемпара фильтровслой, соответствующийСверните карты чувствительности, чтобы получить набор NКарта частичной чувствительности слоя. Проделайте эту свертку с каждым фильтром по очереди, чтобы получить группу D карт частичной чувствительности. Наконец, сопоставьте частичную чувствительность N между каждой группой.поэлементное сложение, получить окончательное NКарта чувствительности слоя:
Режим
Выше приведен алгоритм передачи термина ошибки слоя свертки.Если читатель все еще не понимает, вы можете обратиться к следующей реализации кода, чтобы понять.
Вычисление весового градиента фильтра сверточного слоя
мы собираемся получить первыйВ случае карты чувствительности слоя вычисляется градиент веса фильтра, так как сверточный слойразделение веса, поэтому градиент рассчитывается немного по-другому.
Как показано на фиг.первыйвывод слоя,первыйВес слоя фильтра,первыйКарта чувствительности слоя. Наша задача вычислитьградиент.
Чтобы вычислить частные производные, нам нужно изучить весаправильноВлияние. весовой терминпо влияниюзначение, которое влияет. Мы по-прежнему рассмотрим весовой термин на нескольких конкретных примерах.правильновоздействовать, а затем делать из этого выводы.
Пример 1, расчет:
Как видно из приведенной выше формулы, посколькуразделение веса, весвсеиметь влияние.да,,... функция, в то время как,,...Сновафункция, согласнополная производнаяформула, расчетПросто сложите каждую частную производную:
Пример 2, расчет:
просмотревиотношения, мы можем легко получить:
На самом деле каждыйвесовой терминВсе они похожи, мы не будем приводить примеры по одному. Теперь пришло время нам снова использовать наше воображение, мы обнаруживаем, что вычисленияПравило:
То есть используйте карту чувствительности в качестве ядра свертки и выполняйте ее на входе.cross-correlation,Как показано ниже:
Наконец, давайте посмотрим на градиент члена смещения. Глядя на предыдущую формулу, мы можем легко найти:
этоПредвзятый терминизградиентэто карта чувствительности всеОшибкиСумма.
Для сверточного слоя с размером шага S метод обработки такой же, как и при передаче **условия ошибки*. Сначала карта чувствительности «восстанавливается» до карты чувствительности с размером шага 1, а затем вышеуказанный метод используется для расчета.
получил всеградиентПосле этого, согласноАлгоритм градиентного спускадля обновления каждого веса. Об этом неоднократно писалось в предыдущих статьях и здесь повторяться не буду.
Пока что мы решили задачу обучения сверточного слоя, давайте взглянем на обучение слоя объединения.
Обучение слоя пула
Независимо от максимального или среднего объединения, нет параметров для изучения. Таким образом, вСверточная нейронная сетьПри обучении все, что нужно сделать слою объединения, этоОшибкипереходит на предыдущий уровень безградиентрасчет.
Перенос максимального срока ошибки объединения
Как показано на рисунке ниже, если предположить, чтоРазмер слоя 4*4, размер фильтра пула 2*2, размер шага 2. Таким образом, после максимального пула первыйРазмер слоя 2*2. Предположим, первыйслоистыйЗначения были рассчитаны, и наша задача теперь состоит в том, чтобы вычислить первыйслоистыйценность.
мы используемозначает первыйслоистыйвзвешенный ввод;использоватьозначает первыйслоистыйвзвешенный ввод. Сначала рассмотрим конкретный пример, а затем резюмируем общие правила. Для максимального объединения:
То есть только самый большой в блокебудет правильностоимость влияет. Будем считать, что наибольшее значение, приведенная выше формула эквивалентна:
Тогда нам нетрудно получить следующие частные производные:
следовательно:
и:
Теперь мы нашли правило: для максимального пула следующий слойОшибкиЗначение передается нейрону, соответствующему максимальному значению в соответствующем блоке предыдущего слоя, а значения остальных нейроновОшибкивсе 0. Как показано на рисунке ниже (при условии, что,,,максимальное выходное значение в блоке):
Перенос срока ошибки среднего пула
Мы по-прежнему используем проверенную выше процедуру, сначала изучаем частный случай, а затем расширяем ее до общего закона.
Как показано на рисунке выше, сначала рассмотрим расчет. Давайте сначала посмотримКак это влияет.
Согласно приведенной выше формуле, мы можем увидеть с первого взгляда:
Поэтому по цепному правилу вывода нам нетрудно вычислить:
Точно так же мы можем вычислить,,:
Итак, мы нашли правило: для объединения средних значений следующий слойОшибкиЗначение будетпоровнувсем нейронам в соответствующем блоке предыдущего слоя. Как показано ниже:
Приведенный выше алгоритм может быть выражен в виде длинногопроизведение КронекераФорму, заинтересованные читатели могут изучить.
в,размер фильтра объединяющего слоя,,все матрицы.
До сих пор мы поставилисверточный слой,Объединенный слойВнедрение алгоритма обучения завершено, плюс предыдущая статьяполносвязный слойДля обучения алгоритма вы уже должны уметь писатьСверточная нейронная сетьЗнания, необходимые для кода. Чтобы углубить понимание знаний, далее мы покажем, как реализовать простоеСверточная нейронная сеть.
Реализация сверточных нейронных сетей
Пожалуйста, обратитесь к GitHub за полным кодом:GitHub.com/долго кричал/узнал… (python2.7)
Теперь давайте запачкаем руки и реализуем сверточную нейронную сеть, чтобы закрепить то, что мы узнали.
В первую очередь нам предстоит изменить архитектуру кода, и «слой» стал нашим основным компонентом. Это связано с тем, что сверточные нейронные сети имеют разные слои, и алгоритмы для каждого слоя реализованы в соответствующем классе.
На этот раз мы использовали то, что часто используется для написания алгоритмов на питоне.numpyСумка. чтобы использоватьnumpy, нам нужно сначалаnumpyИмпортировать:
import numpy as np
Реализация сверточных слоев
Инициализировать сверточный слой
мы используемConvLayerкласс для реализации сверточного слоя. Следующий код предназначен для инициализации сверточного слоя, вы можете установить сверточный слой в конструктореГиперпараметры.
class ConvLayer(object):
def __init__(self, input_width, input_height,
channel_number, filter_width,
filter_height, filter_number,
zero_padding, stride, activator,
learning_rate):
self.input_width = input_width
self.input_height = input_height
self.channel_number = channel_number
self.filter_width = filter_width
self.filter_height = filter_height
self.filter_number = filter_number
self.zero_padding = zero_padding
self.stride = stride
self.output_width = \
ConvLayer.calculate_output_size(
self.input_width, filter_width, zero_padding,
stride)
self.output_height = \
ConvLayer.calculate_output_size(
self.input_height, filter_height, zero_padding,
stride)
self.output_array = np.zeros((self.filter_number,
self.output_height, self.output_width))
self.filters = []
for i in range(filter_number):
self.filters.append(Filter(filter_width,
filter_height, self.channel_number))
self.activator = activator
self.learning_rate = learning_rate
calculate_output_sizeФункция используется для определения размера вывода сверточного слоя, который реализован следующим образом:
@staticmethod
def calculate_output_size(input_size,
filter_size, zero_padding, stride):
return (input_size - filter_size +
2 * zero_padding) / stride + 1
FilterКласс содержит сверточный слойпараметра такжеградиенти реализуетАлгоритм градиентного спускадля обновления параметров.
class Filter(object):
def __init__(self, width, height, depth):
self.weights = np.random.uniform(-1e-4, 1e-4,
(depth, height, width))
self.bias = 0
self.weights_grad = np.zeros(
self.weights.shape)
self.bias_grad = 0
def __repr__(self):
return 'filter weights:\n%s\nbias:\n%s' % (
repr(self.weights), repr(self.bias))
def get_weights(self):
return self.weights
def get_bias(self):
return self.bias
def update(self, learning_rate):
self.weights -= learning_rate * self.weights_grad
self.bias -= learning_rate * self.bias_grad
Мы используем общую стратегию инициализации параметров, а именно:Весаслучайным образом инициализируется небольшим значением, в то время какПредвзятый терминИнициализирован в 0.
Activatorкласс реализуетфункция активации,в,forwardметод реализует опережающее вычисление, в то время какbackwardметод заключается в вычисленииПроизводная. Например, реализация функции relu выглядит следующим образом:
class ReluActivator(object):
def forward(self, weighted_input):
#return weighted_input
return max(0, weighted_input)
def backward(self, output):
return 1 if output > 0 else 0
Реализация прямого вычисления сверточных слоев
ConvLayerКатегорияforwardМетод реализует прямое вычисление сверточного слоя (то есть вычисление вычисляет выходные данные сверточного слоя в соответствии с входными данными), а следующая реализация кода:
def forward(self, input_array):
'''
计算卷积层的输出
输出结果保存在self.output_array
'''
self.input_array = input_array
self.padded_input_array = padding(input_array,
self.zero_padding)
for f in range(self.filter_number):
filter = self.filters[f]
conv(self.padded_input_array,
filter.get_weights(), self.output_array[f],
self.stride, filter.get_bias())
element_wise_op(self.output_array,
self.activator.forward)
Приведенный выше код содержит несколько служебных функций.element_wise_opфункция реализуетnumpyмассив для выполненияПо элементуоперацию и записать возвращаемое значение обратно в массив, код выглядит следующим образом:
# 对numpy数组进行element wise操作
def element_wise_op(array, op):
for i in np.nditer(array,
op_flags=['readwrite']):
i[...] = op(i)
convФункция реализует 2D и 3D массивысвертка, код показан ниже:
def conv(input_array,
kernel_array,
output_array,
stride, bias):
'''
计算卷积,自动适配输入为2D和3D的情况
'''
channel_number = input_array.ndim
output_width = output_array.shape[1]
output_height = output_array.shape[0]
kernel_width = kernel_array.shape[-1]
kernel_height = kernel_array.shape[-2]
for i in range(output_height):
for j in range(output_width):
output_array[i][j] = (
get_patch(input_array, i, j, kernel_width,
kernel_height, stride) * kernel_array
).sum() + bias
paddingФункция реализует операцию заполнения нулями:
# 为数组增加Zero padding
def padding(input_array, zp):
'''
为数组增加Zero padding,自动适配输入为2D和3D的情况
'''
if zp == 0:
return input_array
else:
if input_array.ndim == 3:
input_width = input_array.shape[2]
input_height = input_array.shape[1]
input_depth = input_array.shape[0]
padded_array = np.zeros((
input_depth,
input_height + 2 * zp,
input_width + 2 * zp))
padded_array[:,
zp : zp + input_height,
zp : zp + input_width] = input_array
return padded_array
elif input_array.ndim == 2:
input_width = input_array.shape[1]
input_height = input_array.shape[0]
padded_array = np.zeros((
input_height + 2 * zp,
input_width + 2 * zp))
padded_array[zp : zp + input_height,
zp : zp + input_width] = input_array
return padded_array
Реализация алгоритма обратного распространения сверточного слоя
Теперь пришло время представить основной алгоритм сверточного слоя. Мы знаем, что алгоритм обратного распространения должен выполнить несколько задач:
- будетОшибкипереходит на предыдущий слой.
- рассчитать каждыйпараметризградиент.
- возобновитьпараметр.
Следующие коды находятся вConvLayerреализованы в классе. Давайте сначала посмотрим наОшибкиРеализация кода передана предыдущему уровню.
def bp_sensitivity_map(self, sensitivity_array,
activator):
'''
计算传递到上一层的sensitivity map
sensitivity_array: 本层的sensitivity map
activator: 上一层的激活函数
'''
# 处理卷积步长,对原始sensitivity map进行扩展
expanded_array = self.expand_sensitivity_map(
sensitivity_array)
# full卷积,对sensitivitiy map进行zero padding
# 虽然原始输入的zero padding单元也会获得残差
# 但这个残差不需要继续向上传递,因此就不计算了
expanded_width = expanded_array.shape[2]
zp = (self.input_width +
self.filter_width - 1 - expanded_width) / 2
padded_array = padding(expanded_array, zp)
# 初始化delta_array,用于保存传递到上一层的
# sensitivity map
self.delta_array = self.create_delta_array()
# 对于具有多个filter的卷积层来说,最终传递到上一层的
# sensitivity map相当于所有的filter的
# sensitivity map之和
for f in range(self.filter_number):
filter = self.filters[f]
# 将filter权重翻转180度
flipped_weights = np.array(map(
lambda i: np.rot90(i, 2),
filter.get_weights()))
# 计算与一个filter对应的delta_array
delta_array = self.create_delta_array()
for d in range(delta_array.shape[0]):
conv(padded_array[f], flipped_weights[d],
delta_array[d], 1, 0)
self.delta_array += delta_array
# 将计算结果与激活函数的偏导数做element-wise乘法操作
derivative_array = np.array(self.input_array)
element_wise_op(derivative_array,
activator.backward)
self.delta_array *= derivative_array
expand_sensitivity_mapМетод заключается в «восстановлении» карты чувствительности с размером шага S в карту чувствительности с размером шага 1. Код выглядит следующим образом:
def expand_sensitivity_map(self, sensitivity_array):
depth = sensitivity_array.shape[0]
# 确定扩展后sensitivity map的大小
# 计算stride为1时sensitivity map的大小
expanded_width = (self.input_width -
self.filter_width + 2 * self.zero_padding + 1)
expanded_height = (self.input_height -
self.filter_height + 2 * self.zero_padding + 1)
# 构建新的sensitivity_map
expand_array = np.zeros((depth, expanded_height,
expanded_width))
# 从原始sensitivity map拷贝误差值
for i in range(self.output_height):
for j in range(self.output_width):
i_pos = i * self.stride
j_pos = j * self.stride
expand_array[:,i_pos,j_pos] = \
sensitivity_array[:,i,j]
return expand_array
create_delta_arrayпредставляет собой массив, созданный для хранения карты чувствительности, переданной предыдущему слою.
def create_delta_array(self):
return np.zeros((self.channel_number,
self.input_height, self.input_width))
Далее идет код для расчета градиента.
def bp_gradient(self, sensitivity_array):
# 处理卷积步长,对原始sensitivity map进行扩展
expanded_array = self.expand_sensitivity_map(
sensitivity_array)
for f in range(self.filter_number):
# 计算每个权重的梯度
filter = self.filters[f]
for d in range(filter.weights.shape[0]):
conv(self.padded_input_array[d],
expanded_array[f],
filter.weights_grad[d], 1, 0)
# 计算偏置项的梯度
filter.bias_grad = expanded_array[f].sum()
Наконец, согласноАлгоритм градиентного спускаКод для обновления параметров, эта часть очень проста.
def update(self):
'''
按照梯度下降,更新权重
'''
for filter in self.filters:
filter.update(self.learning_rate)
Проверка градиента для сверточных слоев
Чтобы проверить правильность нашего вывода формулы и реализации кода, мы должны выполнить проверку градиента на сверточных слоях. Вот реализация кода:
def init_test():
a = np.array(
[[[0,1,1,0,2],
[2,2,2,2,1],
[1,0,0,2,0],
[0,1,1,0,0],
[1,2,0,0,2]],
[[1,0,2,2,0],
[0,0,0,2,0],
[1,2,1,2,1],
[1,0,0,0,0],
[1,2,1,1,1]],
[[2,1,2,0,0],
[1,0,0,1,0],
[0,2,1,0,1],
[0,1,2,2,2],
[2,1,0,0,1]]])
b = np.array(
[[[0,1,1],
[2,2,2],
[1,0,0]],
[[1,0,2],
[0,0,0],
[1,2,1]]])
cl = ConvLayer(5,5,3,3,3,2,1,2,IdentityActivator(),0.001)
cl.filters[0].weights = np.array(
[[[-1,1,0],
[0,1,0],
[0,1,1]],
[[-1,-1,0],
[0,0,0],
[0,-1,0]],
[[0,0,-1],
[0,1,0],
[1,-1,-1]]], dtype=np.float64)
cl.filters[0].bias=1
cl.filters[1].weights = np.array(
[[[1,1,-1],
[-1,-1,1],
[0,-1,1]],
[[0,1,0],
[-1,0,-1],
[-1,1,0]],
[[-1,0,0],
[-1,0,1],
[-1,0,0]]], dtype=np.float64)
return a, b, cl
def gradient_check():
'''
梯度检查
'''
# 设计一个误差函数,取所有节点输出项之和
error_function = lambda o: o.sum()
# 计算forward值
a, b, cl = init_test()
cl.forward(a)
# 求取sensitivity map,是一个全1数组
sensitivity_array = np.ones(cl.output_array.shape,
dtype=np.float64)
# 计算梯度
cl.backward(a, sensitivity_array,
IdentityActivator())
# 检查梯度
epsilon = 10e-4
for d in range(cl.filters[0].weights_grad.shape[0]):
for i in range(cl.filters[0].weights_grad.shape[1]):
for j in range(cl.filters[0].weights_grad.shape[2]):
cl.filters[0].weights[d,i,j] += epsilon
cl.forward(a)
err1 = error_function(cl.output_array)
cl.filters[0].weights[d,i,j] -= 2*epsilon
cl.forward(a)
err2 = error_function(cl.output_array)
expect_grad = (err1 - err2) / (2 * epsilon)
cl.filters[0].weights[d,i,j] += epsilon
print 'weights(%d,%d,%d): expected - actural %f - %f' % (
d, i, j, expect_grad, cl.filters[0].weights_grad[d,i,j])
В приведенном выше коде стоит подумать о том, что карта чувствительности, переданная сверточному слою, представляет собой массив всех единиц, и читателю остается сделать вывод, почему это так (подсказка: функция активации выбирает функцию идентификации :). Если читатели все еще в замешательстве, пишите в комментариях к статье, и я отвечу.
Запустив приведенный выше код проверки градиента, мы получим следующий вывод: ожидаемый градиент согласуется с фактически рассчитанным градиентом, что доказывает, что вывод нашего алгоритма и реализация кода действительно верны.
Выше приведена реализация сверточного слоя.
Реализация слоя максимального пула
Реализация слоя max pooling относительно проста, мы напрямую выкладываем все коды следующим образом:
class MaxPoolingLayer(object):
def __init__(self, input_width, input_height,
channel_number, filter_width,
filter_height, stride):
self.input_width = input_width
self.input_height = input_height
self.channel_number = channel_number
self.filter_width = filter_width
self.filter_height = filter_height
self.stride = stride
self.output_width = (input_width -
filter_width) / self.stride + 1
self.output_height = (input_height -
filter_height) / self.stride + 1
self.output_array = np.zeros((self.channel_number,
self.output_height, self.output_width))
def forward(self, input_array):
for d in range(self.channel_number):
for i in range(self.output_height):
for j in range(self.output_width):
self.output_array[d,i,j] = (
get_patch(input_array[d], i, j,
self.filter_width,
self.filter_height,
self.stride).max())
def backward(self, input_array, sensitivity_array):
self.delta_array = np.zeros(input_array.shape)
for d in range(self.channel_number):
for i in range(self.output_height):
for j in range(self.output_width):
patch_array = get_patch(
input_array[d], i, j,
self.filter_width,
self.filter_height,
self.stride)
k, l = get_max_index(patch_array)
self.delta_array[d,
i * self.stride + k,
j * self.stride + l] = \
sensitivity_array[d,i,j]
Реализация полносвязного слоя аналогична предыдущей статье, поэтому здесь повторяться не будем. До сих пор вы реализовали простойСверточная нейронная сетьНеобходимые основные компоненты. заСверточная нейронная сеть, существует много хороших реализаций с открытым исходным кодом, поэтому нам не нужно реализовывать их самим. Цель публикации этих кодов — дать нам лучшее пониманиеСверточная нейронная сетьосновной принцип.
Приложения сверточных нейронных сетей
Распознавание рукописных цифр MNIST
LeNet-5реализовать распознавание рукописных цифрСверточная нейронная сеть, он достигает коэффициента ошибок 0,8% на тестовом наборе MNIST.LeNet-5Структура выглядит следующим образом:
оLeNet-5В интернете много информации, поэтому повторяться не буду. Заинтересованные читатели могут попробовать построить и обучить наш собственный реализованный код сверточной нейронной сети.LeNet-5(Конечно, код будет сложнее).
подраздел
так какСверточная нейронная сетьСложность серии, мы написали самую длинную статью во всей серии, и я считаю, что читатели устали так же, как и автор.Сверточная нейронная сетьЭто самый важный инструмент для глубокого обучения (я стесняюсь писать «один»), и стоит приложить некоторые усилия, чтобы понять его. Если вы действительно понимаете содержание этой статьи, это эквивалентно преодолению самого важного порога для начала работы с глубоким обучением. В следующей статье мы представим еще один очень важный инструмент глубокого обучения:Рекуррентная нейронная сеть, к тому времени наша серия статей будет закончена более чем наполовину. Каждая статья — это фильтр.
использованная литература
- CS231n Convolutional Neural Networks for Visual Recognition
- Функция активации ReLu (выпрямленные линейные единицы)
- Jake Bouvrie, Notes on Convolutional Neural Networks, 2006
- Ian Goodfellow, Yoshua Bengio, Aaron Courville, Deep Learning, MIT Press, 2016