CNN on TensorFlow

машинное обучение

CNN on TensorFlow

Большая часть этой статьи взята из:

An Intuitive Explanation of Convolutional Neural Networks
Zhihu: «Почему ReLU лучше, чем tanh и сигмовидная функция?»
Deep MNIST for Experts
API TensorFlow Python «tf.nn»

Build a Multilayer Convolutional Network

В официальных руководствах по TensorFlow результаты, которые мы получаем в наборе данных MNIST с использованием модели softmax, верны только на 91%, что очень плохо. Итак, мы будем использовать немного более сложную модель: CNN (Convolutional Neural Network) для улучшения экспериментальных результатов. В CNN есть четыре основных операции:

  1. свертка
  2. Нелинейная обработка (ReLU)
  3. Объединение или подвыборка
  4. Классификация (полностью связанный слой)

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

What's CNN

Convolution

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

Как мы уже говорили выше, каждое изображение можно рассматривать как матрицу значений пикселей. Рассмотрим изображение 5 x 5, значения пикселей которого равны только 0 или 1 (обратите внимание, что для изображений в градациях серого значения пикселей находятся в диапазоне от 0 до 255, зеленая матрица ниже со значениями пикселей 0 и 1 является лишь частным случаем ):

Между тем, рассмотрим другую матрицу 3 x 3, подобную этой:

Затем свертка изображения 5 x 5 и матрицы 3 x 3 может быть рассчитана, как показано на анимации ниже:

Теперь остановитесь и поймите, как выполняется приведенный выше расчет. Мы используем оранжевую матрицу, чтобы скользить по исходному изображению (зеленому), по одному пикселю за раз (также называемому «шагом»), и в каждой позиции мы вычисляем произведение соответствующего элемента (между двумя матрицами) и положить продукт В качестве конечного результата получается значение каждого элемента в выходной матрице (розовый цвет). Обратите внимание, что матрица 3 x 3 видит только часть входного изображения на каждом шаге.

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

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

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

Еще один хороший способ интуитивно понять операцию свертки — посмотреть на анимацию следующего рисунка:

Фильтр (красный прямоугольник) перемещается по входному изображению (операция свертки), создавая карту объектов. Свертывание другого фильтра (зеленая рамка) на том же изображении приводит к другой карте объектов. Обратите внимание, что операция свертки может получить информацию о локальной зависимости от исходного изображения. Также обратите внимание, как эти два разных фильтра генерируют разные карты объектов из одного и того же изображения. Помните изображение выше и два фильтра — это просто числовые матрицы, которые мы обсуждали выше.

На практике CNN узнают значения этих фильтров во время обучения (хотя нам все еще нужно указать такие параметры, как количество фильтров, размер фильтра, сетевую архитектуру и т. д. перед обучением). Чем больше фильтров мы используем, тем больше характеристик изображения мы извлекаем и тем лучше сеть может распознавать шаблоны на неизвестных изображениях.

Размер карты признаков (сверточных признаков) контролируется следующими тремя параметрами, которые нам необходимо определить до свертки:

  • Глубина:Глубина соответствует количеству фильтров, необходимых для операции свертки.. В сети на рисунке ниже мы сворачиваем исходное изображение с тремя разными фильтрами, чтобы можно было создать три разные карты признаков. Вы можете думать об этих трех картах объектов как о сложенных двухмерных матрицах, тогда «глубина» карты объектов равна 3.

  • Шаг:Шаг — это количество пикселей, на которое мы перемещаем матрицу фильтра по входной матрице.. Когда шаг равен 1, мы перемещаем положение фильтра на один пиксель за раз. Когда шаг равен 2, мы пропускаем 2 пикселя при каждом перемещении фильтра. Чем больше размер шага, тем меньше будет карта признаков.

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

ReLU

ReLU расшифровывается как Rectified Linear Unit и представляет собой нелинейную операцию.

  1. Зачем вводить нелинейную функцию возбуждения?

    Если вы не используете функцию возбуждения (на самом деле функция возбуждения $f(x) = x$ ), то в этом случае выход каждого слоя является линейной функцией входа верхнего слоя, т.е. легко проверить, сколько бы слоев ни было у вашей нейронной сети. , выход представляет собой линейную комбинацию входных данных, что эквивалентно эффекту отсутствия скрытого слоя. Такая ситуация — самый примитивный персептрон.

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

  2. Зачем вводить ReLU вместо других нелинейных функций (таких как сигмовидная функция)?

    • Используя такие функции, как сигмоид,Рассчитайте функцию активации (операция индекса), сумма расчета большой, при обратном распространении для нахождения градиента ошибки вывод включает в себя деление, и объем вычислений относительно велик, а использование функции активации Relu экономит много вычислений во всем процессе.
    • Для глубоких сетейПри обратном распространении сигмовидной функции градиент легко исчезает.(Когда сигмоида приближается к области насыщения, преобразование происходит слишком медленно, и производная стремится к 0, что приводит кпотеря информации), так что обучение глубокой сети не может быть завершено.
    • Relu сделает вывод некоторых нейронов равным 0, что приведет к разреженности сети, уменьшит взаимозависимость параметров и облегчит проблему переобучения.(и биологическая интерпретация балабалы некоторыми).

    Конечно, теперь есть некоторые улучшения в relu, такие как prelu, random relu и т. д. Будут некоторые улучшения в скорости обучения или точности на разных наборах данных.Подробности вы можете найти в соответствующих документах.

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

  3. Преимущества и недостатки ReLU?

    преимущество:

    • Исправлена ​​проблема исчезновения градиента (в положительном интервале)
    • Скорость расчета очень высокая, нужно только оценить, больше ли входное значение 0
    • Сходимость намного быстрее, чем сигмоидальная и тангенциальная.

    недостаток:

    • Вывод ReLU не центрирован по нулю
    • Dead ReLU Problem, что означает, что некоторые нейроны никогда не могут быть активированы, в результате чего соответствующие параметры никогда не обновляются. Существует две основные причины, по которым это может произойти: (1) Очень несчастный инициализация параметров, который редко и (2) слишком высокая скорость обучения, которая приводит к слишком большим обновлениям параметра во время обучения, к сожалению, приносят сеть в это состояние. Решение состоит в том, чтобы использовать метод инициализации Xavier и избегать установки скорости обучения или использовать Adagrad для автоматического регулировки скорости обучения алгоритм.

За десятилетия развития машинного обучения мы сформировали концепцию: нелинейные функции активации более продвинуты, чем линейные функции активации.

Особенно в нейронной сети BP, полной сигмовидных функций, и нейронной сети SVM, полной радиальных базисных функций, часто возникает такая иллюзия, что нелинейные функции вносят большой вклад в нелинейную сеть.

Галлюцинации более выражены при SVM. Форма функции ядра не совсем определяет способность SVM обрабатывать нелинейные данные (вспомогательные векторы действуют как скрытый слой).

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

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

Работу ReLU можно понять из диаграммы ниже. Выходную карту объектов здесь также можно рассматривать как «исправленную» карту объектов.

Хоть так называемый воробей и маленький, но все внутренние органы у него есть, и хоть ReLU и маленький, но его тоже можно улучшать.

Типы ReLU

Различие ReLU в основном находится на отрицательном конце, и оно различается по разнице в наклоне отрицательного конца., примерно как показано на рисунке ниже.

Наклон отрицательного конца обычного ReLU равен 0, Leaky ReLU имеет относительно небольшой наклон на отрицательном конце, а PReLU изучает наклон при обратном распространении. Randomized Leaky ReLU использует равномерное распределение для случайного создания наклонов во время обучения и использует средний наклон для расчета во время тестирования.

Эффект

Среди них набор данных NDSB является соревнованием Kaggle, и в этом соревновании появляется RReLU.

Из приведенных выше результатов можно увидеть четыре момента:

  • Для Leaky ReLU, если наклон небольшой, он мало чем отличается от ReLU, а когда наклон больше, эффект намного лучше.
  • На тренировочном наборе PReLU часто может достигать наименьшей частоты ошибок, что указывает на то, что PReLU склонен к переоснащению.
  • Улучшение RReLU в наборе данных NSDB более очевидно, чем в cifar10 и cifar100, в то время как набор данных NSDB меньше, что показывает, что RReLU более эффективен в борьбе с переоснащением.
  • Для RReLU также необходимо изучить, как наклон рандомизации влияет на процесс обучения и тестирования.

использованная литература

[1]. Xu B, Wang N, Chen T, et al. Empirical evaluation of rectified activations in convolutional network[J]. arXiv preprint arXiv:1505.00853, 2015.

Pooling

Пространственное объединение (также называемое субдискретизацией или субдискретизацией)Уменьшите размер каждой карты объектов, но сохраните большую часть важной информации.. Существует несколько способов пространственного объединения: максимизация, усреднение, суммирование и так далее.

Для Max Pooling мы определяем пространственную окрестность (например, окно 2x2) и берем самый большой элемент из выпрямленной карты объектов в пределах окна. В дополнение к наибольшему элементу мы также можем взять среднее значение (Average Pooling) или суммировать элементы в окне.На практике было показано, что максимальное объединение работает лучше.

На рисунке ниже показан пример использования максимального пула на исправленной карте признаков (полученной после операции свертки + ReLU) с использованием окна 2x2.

Мы сдвигаем наше окно 2x2 на 2 элемента (он же «размер шага») и берем максимальное значение в пределах каждой области. Как показано на рисунке выше, эта операция может уменьшить размерность нашей карты объектов.

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

На следующем рисунке показано влияние операции объединения на исправленную карту объектов, которую мы получаем после операции ReLU:

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

  • Уменьшает входное представление (размер объекта), а количество параметров и вычислений в сети сокращается более контролируемо, что позволяет контролировать переоснащение.

  • Сделайте сеть инвариантной к небольшим изменениям, избыточности и преобразованиям во входном изображении (крошечные избыточности во входе не изменят объединенный вывод, потому что мы используем максимальное/среднее значение в локальном соседстве).

  • Помогает нам получить максимальную масштабную инвариантность изображения (точное слово «инвариантность»). Это очень мощно, потому что мы можем обнаруживать объекты на изображениях независимо от того, где они расположены.

До сих пор мы видели, как работают свертка, ReLU и объединение. Важно понимать, что эти слои являются основой для построения любой CNN. Как показано на рисунке ниже,У нас есть два набора сверточных слоев, слоев ReLU и объединения — второй набор сверточных слоев использует шесть фильтров для продолжения свертки вывода первого набора слоев объединения, в результате чего получается шесть карт объектов.. Затем примените ReLU ко всем шести картам функций. Затем мы выполняем операции максимального объединения на каждой из шести измененных карт объектов.

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

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

Connect

Полносвязный слой представляет собой традиционный многослойный персептрон с функцией активации softmax в выходном слое (можно использовать и другие классификаторы, такие как SVM, но в этой статье используется только softmax). Термин «полностью связанный» означает, что все нейроны предыдущего слоя связаны со всеми нейронами следующего слоя.

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

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

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

Use Backpropagation to Train whole network

Как обсуждалось выше, роль слоя свертки + объединения заключается в извлечении признаков из входного изображения, а роль полносвязного слоя — в классификаторе.

Обратите внимание, что на рисунке ниже, поскольку входным изображением является лодка, целевая вероятность для этого класса равна 1, а целевая вероятность для трех других классов равна 0, т. е.

  • входное изображение = лодка

  • целевой вектор = [0, 0, 1, 0]

Процесс обучения полной сверточной сети можно резюмировать следующим образом:

  • Шаг 1: Мы инициализируем все фильтры и устанавливаем параметры/веса со случайными значениями
  • Шаг 2: Сеть получает обучающее изображение в качестве входных данных и находит выходную вероятность каждого класса посредством процесса прямого распространения (операции свертки, ReLU и объединения, а также прямое распространение полносвязного слоя).
    • Мы предполагаем, что выходная вероятность этого изображения лодки равна [0,2, 0,4, 0,1, 0,3]
    • Поскольку веса для первой обучающей выборки назначаются случайным образом, вероятность вывода также является случайной.
  • Шаг 3: Рассчитайте общую ошибку на выходном слое (рассчитайте сумму 4 классов)
    • Общая ошибка = ∑ ½ (целевая вероятность – выходная вероятность) ²
  • Шаг 4: Используйте алгоритм обратного распространения для расчета градиента ошибки в соответствии с весом сети и используйте алгоритм градиентного спуска для обновления значений/весов всех фильтров и значений параметров, чтобы минимизировать ошибку вывода.
    • Обновления весов связаны с их долей в общей ошибке
    • Когда то же изображение снова используется в качестве входных данных, выходная вероятность может быть [0,1, 0,1, 0,7, 0,1], что ближе к целевому вектору [0, 0, 1, 0].
    • Это указывает на то, что сеть смогла правильно классифицировать это конкретное изображение, отрегулировав веса/фильтры, чтобы уменьшить ошибку на выходе.
    • Такие параметры, как количество фильтров, размер фильтра, структура сети и т. д., фиксируются перед первым шагом и остаются неизменными во время обучения — обновляются только значения матрицы фильтров и весов соединений.
  • Шаг 5. Повторите шаги 1–4 для всех изображений в обучающих данных.

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

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

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

Заметка 2: В приведенном выше примере мы использовали два набора слоев свертки и объединения. Помните, однако, что эти операции могут повторяться несколько раз в ConvNet. На самом деле, некоторые из самых эффективных ConvNet сегодня имеют до дюжины слоев свертки и объединения! В то же время за каждым сверточным слоем не обязательно должен следовать объединяющий слой. Как показано на рисунке ниже, мы можем последовательно использовать несколько операций свертки + ReLU перед операцией объединения. Также обратите внимание, как слои ConvNet визуализируются на изображении ниже.

Visualization on CNN

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

Адам Харли создал визуализацию сверточной нейронной сети, используя обучающий набор рукописных цифр MNIST. Я настоятельно рекомендую использовать его, чтобы понять, как работают CNN.

На изображении ниже мы видим, как сеть распознает ввод «8». Обратите внимание, что визуализация на рисунке ниже показывает не только операцию ReLU.

Входное изображение содержит 1024 пикселя (размер 32 x 32), а первый сверточный слой (сверточный слой 1) состоит из шести уникальных фильтров 5x5 (шаг 1). Как видно, шесть различных фильтров используются для получения карты признаков глубины шесть.

За сверточным слоем 1 следует слой объединения 1, который выполняет максимальное объединение 2x2 (с шагом 2) на шести картах объектов, полученных сверточным слоем 1. Вы можете навести указатель мыши на любой пиксель в слое объединения и наблюдать за сеткой 4x4, которую вы получаете в предыдущем сверточном слое (показанном выше). Вы обнаружите, что самый большой (самый яркий) пиксель в сетке 4x4 попадет в слой пула.

За уровнем объединения 1 следуют шесть сверточных фильтров 5x5 (шаг 1), которые выполняют операции свертки. Последний является уровнем объединения 2, который выполняет максимальное объединение 2x2 (с шагом 2). Концепция этих двух слоев такая же, как описано ранее.

Далее мы подошли к трем полносвязным слоям. Они есть:

  • Первый полносвязный слой содержит 120 нейронов.
  • Второй полносвязный слой имеет 100 нейронов.
  • Третий полносвязный слой имеет 10 нейронов, соответствующих 10 числам — выходной слой

Обратите внимание на приведенную ниже диаграмму, что каждый из 10 узлов в выходном слое подключен ко всем 100 узлам во втором полносвязном слое (отсюда и название «полностью подключенный»).

Также обратите внимание, как единственный яркий узел в выходном слое соответствует цифре «8» — это говорит о том, что сеть правильно классифицировала нашу рукописную цифру (более яркий узел указывает на большее выходное значение из нее, т. е. 8 — наибольшая вероятность все числа).

Такую же 3D-визуализацию можно найти вздесьВидеть.

Other ConvNet

Сверточные нейронные сети существуют с начала 1990-х годов. Упомянутая выше LeNet — одна из первых сверточных нейронных сетей. Другие архитектуры, которые имеют некоторое влияние, следующие:

  • LeNet (1990-е): Представлено в этой статье.
  • С 1990-х по 2012 год. В конце 1990-х — начале 2010 года сверточные нейронные сети вступили в инкубационный период. По мере того, как количество данных и вычислительная мощность постепенно растут, задачи, с которыми могут справиться сверточные нейронные сети, становятся все более и более интересными.
  • AlexNet (2012) - В 2012 году Алекс Крижевский (среди прочих) выпустилAlexNet, которая является более глубокой и широкой версией, чем LeNet, и была награждена в конкурсе ImageNet Large-Scale Visual Recognition в 2012 году (ImageNet Крупномасштабный конкурс визуального распознавания, ILSVRC) с огромным отрывом. Это огромный прорыв для предыдущих методов, и нынешнее широкомасштабное применение CNN также основано на этой работе.
  • ZF Net (2013 г.) — Победитель ILSVRC 2013 — Convolutional Neural Networks от Мэтью Зейлера и Роба Фергуса. это начинается сZFNet(сокращение от Zeiler & Fergus Net) известный. Это результат настройки гиперпараметров архитектуры AlexNet.
  • GoogLeNet (2014 г.) – победитель ILSVRC 2014 от Google.SzegedyСверточные нейронные сети et al. Его основным вкладом является использование модуля Inception, который позволяет значительно сократить количество параметров сети (4M, у AlexNet 60M параметров).
  • VGGNet (2014) – один из лидеров ILSVRC 2014VGGNetсеть из. Его основной вклад состоит в том, чтобы показать, что глубина (количество слоев) сети оказывает большое влияние на производительность.
  • Реснеты (2015) –остаточная сетьОн был разработан Кайминг Хо (и другими) и выиграл ILSVRC 2015. ResNets в настоящее время являются лучшими моделями в сверточных нейронных сетях и являются выбором по умолчанию для использования ConvNets на практике (по состоянию на май 2016 г.).
  • DenseNet (август 2016 г.) - недавно опубликовано Гао Хуаном (и другими),the Densely Connected Convolutional NetworkКаждый уровень напрямую связан с другими уровнями в прямом направлении. DenseNet достигает значительного улучшения по сравнению с предыдущими современными архитектурами в пяти конкурирующих тестовых задачах распознавания объектов. допустимыйздесьСмотреть Реализация факела.

CNN on TensorFlow

Tensorflow обладает большой гибкостью в свертке и объединении. Как мы относимся к границам? Насколько большим должен быть размер шага? В этом примере мы всегда будем использоватьvanillaВерсия. Наша сверточная сеть выбираетразмер шага 1,размер заполнения равен 0Шаблон гарантирует, что выходные и входные данные имеют одинаковый размер (строгая свертка). Наш пул использует простой и традиционный шаблон размера $2 \times 2$ какmax pooling(Максимальное объединение). Чтобы сделать код более лаконичным, мы абстрагируем эту часть в функцию:

def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME')
    

Convolution Layer on TensorFlow

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

  • conv2d: Произвольное ядро ​​свертки, которое может одновременно выполнять операции свертки на разных каналах.
  • depthwise_conv2d: ядро ​​свертки может выполнять операции свертки на своих собственных каналах независимо друг от друга.
  • separable_conv2d: Свертка в глубинуdepthwise filterЗатем выполните поточечную сверткуseparable filter.

Примечание. Хотя эти операции называются операциями свертки, строго говоря, они простоКорреляция, потому что ядро ​​свертки не выполняет процесс обратной свертки.

Процесс свертки ядра свертки соответствуетstridesпараметры, такие какstrides = [1, 1, 1, 1]Указывает, что ядро ​​свертки выполняет свертку над каждым пикселем, то есть на двумерном экране размер шага по обеим осям равен 1.strides = [1, 2, 2, 1]Указывает, что ядро ​​свертки выполняет свертку для каждого второго пикселя, то есть на двумерном экране размер шага по обеим осям равен 2.

Если мы пока игнорируем фактор канала, пространственный смысл операции свертки определяется следующим образом: если входные данные являются четырехмернымиinput, размерность данных[batch, in_height, in_width, ...], ядро ​​свертки также является четырехмерным ядром свертки, а размерность данных равна[filter_height, filter_width, ...], то для размерности выходных данныхshape(output), который зависит от параметра заполненияpaddingпараметр:

  • padding = 'SAME': Округление в меньшую сторону, только для полноразмерных операций, т. е. размерность входных данных и размерность выходных данных совпадают.

    out_height = ceil(float(in_height) / float(strides[1]))
    out_width  = ceil(float(in_width) / float(strides[2]))
    
  • padding = 'VALID': сводка, применимая к некоторым окнам, то есть измерение входных данных и измерение выходных данных различаются.

    out_height = ceil(float(in_height - filter_height + 1) / float(strides[1]))
    out_width  = ceil(float(in_width - filter_width + 1) / float(strides[2]))
    
output[b, i, j, :] = 
          sum_{di, dj} input[b, strides[1] * i + di, strides[2] * j + dj, ...] * 
                   filter[di, dj, ...]

так как,inputДанные четырехмерные, с вектором над каждым каналомinput[b, i, j, :]. заconv2d, эти векторы будут использоваться ядром сверткиfilter[di, dj, :, :]Умножьте, чтобы создать новый вектор. заdepthwise_conv_2d, каждая скалярная компонентаinput[b, i , j, k]будетkКаждый канал представляет собой независимое сверточное ядро.filter[di, dj, k]Выполните операцию свертки, а затем объедините все полученные векторы в новый вектор.


tf.nn.conv2d(input, filter, strides, padding, use_cudnn_on_gpu=None, data_format=None, name=None)

Эта функция работает с четырехмерными входными данными.inputи четырехмерное ядро ​​сверткиfilterВыполните операцию, а затем выполните операцию двумерной свертки над входными данными и, наконец, получите результат после свертки.

Размерности данного входного тензора равны[batch, in_height, in_width, in_channels], размерность тензора ядра свертки равна[filter_height, filter_width, in_channels, out_channels], конкретная операция свертки выглядит следующим образом:

  • Преобразуйте размерность ядра свертки в двумерную матричную форму[filter_height * filter_width* in_channels, output_channels]
  • Для каждой партии изображений мы преобразуем входной тензор во временное измерение данных.[batch, out_height, out_width, filter_height * filter_width * in_channels]
  • Для каждого пакета изображений мы умножаем ядро ​​свертки вправо, чтобы получить окончательный результат.

Более конкретные детали представления, если используется форма NHWC data_format по умолчанию:

output[b, i, j, k] =
    sum_{di, dj, q} input[b, strides[1] * i + di, strides[2] * j + dj, q] *
                    filter[di, dj, q, k]

Итак, мы замечаем, что должно бытьstrides[0] = strides[3] = 1. В большинстве случаев количество шагов горизонтального и вертикального перемещения ядра свертки одинаково, т.е.strides = [1, stride, stride, 1].

Пример использования:

import numpy as np
import tensorflow as tf

input_data = tf.Variable(np.random.rand(10, 6, 6, 3), dtype = np.float32)
filter_data = tf.Variable(np.random.rand(2, 2, 3, 1), dtype = np.float32)

y = tf.nn.conv2d(input_data, filter_data, strides = [1, 1, 1, 1], padding = 'SAME')

with tf.Session() as sess:
    init = tf.initialize_all_variables()
    sess.run(init)
    print(sess.run(y))
    print(sess.run(tf.shape(y)))

Входные параметры:

  • input: ОдинTensor. тип данных должен бытьfloat32илиfloat64.
  • filter: ОдинTensor. тип данных должен бытьinputтакой же.
  • strides: Одномерный массив целочисленного типа длиной 4, каждое измерение соответствует соответствующим шагам перемещения каждого измерения во входных данных, например,strides[1]соответствоватьinput[1]количество шагов движения.
  • padding: строка, значение которойSAMEилиVALID.
  • use_cudnn_on_gpu: необязательное логическое значение, которое по умолчанию равноTrue.
  • data_format: необязательныйstring,NHWCилиNCHW. По умолчанию используетсяNHWC. В основном он определяет четырехмерную форму входного тензора и выходного тензора. При использованииNHWC, то данные[batch, in_height, in_width, in_channels]хранение; если используетсяNCHW, то данные[batch, in_channels, in_height, in_width]место хранения.
  • name: (необязательно) дать имя этой операции.

Выходные параметры:

  • ОдинTensor, тип данныхinputтакой же.

Pooling Layer on TensorFlow

Операция объединения сканирует входной тензор с окном матрицы и уменьшает количество элементов, принимая максимальное значение, среднее значение или другие методы значений в каждом окне матрицы. Размер матричного окна для каждой операции объединения определяется какksizeзадается, а в соответствии с параметром шагаstridesопределить размер шага движения. Например, еслиstridesВсе значения равны 1, тогда будет использоваться каждое окно матрицы. еслиstridesЗначение in равно 2, тогда используется каждое второе окно матрицы в каждом измерении. И так далее.

Более конкретный вывод:

output[i] = reduce( value[ strides * i: strides * i + ksize ] )

Размерности выходных данных:

shape(output) = (shape(value) - ksize + 1) / strides

где направление округления зависит от параметраpadding:

  • padding = 'SAME': Округление в меньшую сторону, только для полноразмерных операций, т. е. размерность входных данных и размерность выходных данных совпадают.
  • padding = 'VALID: сводка, применимая к некоторым окнам, т. е. измерение входных данных и измерение выходных данных различаются.

tf.nn.avg_pool(value, ksize, strides, padding , data_format='NHWC', name=None)

Что делает эта функция, так это вычисляет среднее значение элементов в объединенной области.

Пример использования:

import numpy as np
import tensorflow as tf

input_data = tf.Variable( np.random.rand(10,6,6,3), dtype = np.float32 )
filter_data = tf.Variable( np.random.rand(2, 2, 3, 10), dtype = np.float32)

y = tf.nn.conv2d(input_data, filter_data, strides = [1, 1, 1, 1], padding = 'SAME')
output = tf.nn.avg_pool(value = y, ksize = [1, 2, 2, 1], strides = [1, 1, 1, 1], padding = 'SAME')

with tf.Session() as sess:
    init = tf.initialize_all_variables()
    sess.run(init)
    print(sess.run(output))
    print(sess.run(tf.shape(output)))

Входные параметры:

  • value: четырехмерныйTensor. Измерение данных[batch, height, width, channels]. тип данныхfloat32,float64,qint8,quint8,qint32.
  • ksize: целочисленный массив длины 4 или более. Значение над каждым битом соответствует окну, соответствующему значению для каждого измерения в тензоре входных данных.
  • strides: целочисленный массив длины 4 или более. Этот параметр задает размер шага скользящего окна по каждому измерению тензора входных данных.
  • padding: строка, значение которойSAMEилиVALID.
  • data_format: необязательныйstring,NHWCилиNCHW. По умолчанию используетсяNHWC. В основном он определяет четырехмерную форму входного тензора и выходного тензора. При использованииNHWC, то данные[batch, in_height, in_width, in_channels]хранение; если используетсяNCHW, то данные[batch, in_channels, in_height, in_width]место хранения.
  • name: (необязательно) дать имя этой операции.

Выходные параметры:

  • Тензор с тем же типом данных, что и значение.

tf.nn.max_pool(value, ksize, strides, padding, data_format='NHWC', name=None)

Целью этой функции является вычисление максимального значения элементов в области объединения.


tf.nn.max_pool_with_argmax(input, ksize, strides, padding, Targmax=None, name=None)

Роль этой функции заключается в вычислении максимального значения элементов в объединенной области и позиции максимального значения.

потому что расчет местоположенияargmax, мы будемinputПроложите расчет, так что еслиinput = [b, y, x, c], то позиция индекса `( ( b * высота + y ) * ширина + x ) * каналы + c

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

Отображение исходного кода:

REGISTER_KERNEL_BUILDER(Name("MaxPoolWithArgmax")
                            .Device(DEVICE_GPU)
                            .TypeConstraint<int64>("Targmax")
                            .TypeConstraint<float>("T"),
                        MaxPoolingWithArgmaxOp<Eigen::GpuDevice, float>);
REGISTER_KERNEL_BUILDER(Name("MaxPoolWithArgmax")
                            .Device(DEVICE_GPU)
                            .TypeConstraint<int64>("Targmax")
                            .TypeConstraint<Eigen::half>("T"),
                        MaxPoolingWithArgmaxOp<Eigen::GpuDevice, Eigen::half>);

Пример использования:

import numpy as np
import tensorflow as tf

input_data = tf.Variable( np.random.rand(10,6,6,3), dtype = tf.float32 )
filter_data = tf.Variable( np.random.rand(2, 2, 3, 10), dtype = np.float32)

y = tf.nn.conv2d(input_data, filter_data, strides = [1, 1, 1, 1], padding = 'SAME')
output, argmax = tf.nn.max_pool_with_argmax(input = y, ksize = [1, 2, 2, 1], strides = [1, 1, 1, 1], padding = 'SAME')

with tf.Session() as sess:
    init = tf.initialize_all_variables()
    sess.run(init)
    print(sess.run(output))
    print(sess.run(tf.shape(output)))

Входные параметры:

  • input: четырехмерныйTensor. Измерение данных[batch, height, width, channels]. тип данныхfloat32.
  • ksize: целочисленный массив длины 4 или более. Значение над каждым битом соответствует окну, соответствующему значению для каждого измерения в тензоре входных данных.
  • strides: целочисленный массив длины 4 или более. Этот параметр задает размер шага скользящего окна по каждому измерению тензора входных данных.
  • padding: строка, значение которойSAMEилиVALID.
  • Targmax: необязательный тип данных:tf.int32илиtf.int64. По умолчанию этоtf.int64.
  • name: (необязательно) дать имя этой операции.

Выходные параметры:

тензор кортежа(output, argmax):

  • output: ОдинTensor, тип данныхfloat32. Представляет максимальное значение области объединения.
  • argmax: ОдинTensor, тип данныхTargmax. Измерение данных является четырехмерным.

Weight Initialization

Итак, чтобы создать эту модель, нам нужно создать много весов и условий смещения, веса в этой модели должны быть инициализированы небольшим количеством шума, чтобы нарушить симметрию и избежать нулевых градиентов. Поскольку мы используемReLUНейроны, поэтому лучше использовать небольшое положительное число для инициализации члена смещения, чтобы избежать проблемы, связанной с тем, что выход узла нейрона всегда равен 0 (мертвые нейроны). Чтобы не повторять операцию инициализации при построении модели, определим две функции для инициализации.

def conv2d(x, W):
    return tf.nn.conv2d(x, W, strides = [1, 1, 1, 1], padding = 'SAME')

def max_pool_2x2(x):
    return tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME')

def weight_variable(shape):
    initial = tf.truncated_normal(shape, stddev = 0.1)
    return tf.Variable(initial)
    
def bias_variable(shape):
    initial = tf.constant(0.1, shape = shape)
    return tf.Variable(initial)

первый уровень

Далее приступаем к реализации первого слоя. Это делается с помощью сверточного слоя, за которым следует слой max_pooling. Convolution вычисляет 32 функции в каждом патче 5x5. Форма тензора веса свертки[5, 5, 1, 32], первые два измерения - это размер патча, за которым следует количество каналов ввода и, наконец, количество каналов вывода. Для каждого выходного канала имеется соответствующее смещение.

W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])

Чтобы использовать этот слой, мы превращаем x в вектор 4d, второе и третье измерения которого соответствуют ширине и высоте изображения, а последняя цифра представляет собой цветовой канал изображения (поскольку это изображение в градациях серого, поэтому количество каналов здесь равно 1 или 3, если это цветовая карта RBG).

x_image = tf.reshape(x, [-1, 28, 28, 1])

После этого мы сворачиваем x_image с вектором весов, добавляем член смещения, затем применяем функцию активации ReLU и, наконец, делаем максимальное объединение.

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)

Второй этаж

Чтобы построить более глубокую сеть, мы сложим несколько одинаковых слоев. На втором уровне каждый патч 5x5 будет иметь 64 функции.

W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)

плотносвязный слой

Теперь, когда размер изображения уменьшен до 7x7, мы добавляем полносвязный слой с 1024 нейронами для обработки всего изображения. Мы преобразуем тензоры, выдаваемые объединяющим слоем, в некоторые векторы, умножаем на матрицу весов, добавляем смещение и используем для этого ReLU.

W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])

h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

Dropout

Чтобы уменьшить переоснащение, мы добавляем отсев перед выходным слоем. Мы используем заполнитель для представления вероятности того, что выход нейрона останется неизменным при выпадении. Таким образом, мы можем включить отсев во время обучения и отключить отсев во время тестирования. В дополнение к маскированию выходных данных нейронов операция tf.nn.dropout TensorFlow также автоматически обрабатывает масштаб выходных значений нейронов. Таким образом, вам не нужно учитывать масштаб при использовании отсева.

keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

выходной слой

Наконец, мы добавляем слой softmax, точно так же, как предыдущая регрессия softmax с одним слоем.

W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])

y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

Обучите и оцените модель

Как работает эта модель?

Для обучения и оценки мы используем почти тот же набор кода, что и предыдущая простая однослойная модель нейронной сети SoftMax, за исключением того, что мы будем использовать более сложный оптимизатор ADAM для максимально крутого градиентного спуска, добавив дополнительный параметр keep_prob в feed_dict для управления шкала отсева. Затем выводите журнал каждые 100 итераций.

cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
sess.run(tf.initialize_all_variables())
for i in range(20000):
  batch = mnist.train.next_batch(50)
  if i%100 == 0:
    train_accuracy = accuracy.eval(feed_dict={
        x:batch[0], y_: batch[1], keep_prob: 1.0})
    print "step %d, training accuracy %g"%(i, train_accuracy)
  train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})

print "test accuracy %g"%accuracy.eval(feed_dict={
    x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})