Простое руководство о том, что такое CNN, как они работают и как создать их с нуля на Python.

В последние несколько лет было много шума о сверточных нейронных сетях (CNN), особенно из-за того, как они произвели революцию в области компьютерного зрения. В этом посте мы будем опираться на базовые знания о нейронных сетях и изучим, что такое CNN, поймем, как они работают, и создадим настоящую с нуля (используя только numpy) на Python. .

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

Готовый? Давайте начнем.

Форматирование в этой статье лучше всего выглядит в оригинальном посте на victorzhou.com.

1. Мотивация

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

Хороший вопрос.

Причина 1. Изображения большие

Изображения, используемые в настоящее время для задач компьютерного зрения, часто имеют размер 224x224 или больше. Представьте себе создание нейронной сети для обработки цветных изображений 224x224: включая 3 цветовых канала (RGB) в изображении, которое выходит на входные функции 224 x 224 x 3 = 150 528! Типичный скрытый слой в такой сети может иметь 1024 узла, поэтому нам придется обучить 150 528 x 1024 = 150+ миллионов весов только для первого слоя. Наша сеть была бы огромной, и ее практически невозможно было бы обучить.

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

Причина 2: позиции могут меняться

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

Скоро мы увидим, как CNN может помочь нам решить эти проблемы.

2. Набор данных

В этом посте мы поговорим о Hello, World! компьютерного зрения: проблема классификации рукописных цифр MNIST. Это просто: учитывая изображение, классифицируйте его как цифру.

Каждое изображение в наборе данных MNIST имеет размер 28x28 и содержит центрированную цифру в градациях серого.

По правде говоря, обычная нейронная сеть отлично справится с этой задачей. Вы можете рассматривать каждое изображение как вектор размером 28 x 28 = 784, подавать его на входной слой с размером 784 тусклых, складывать несколько скрытых слоев и завершать выходным слоем из 10 узлов, по 1 для каждой цифры.

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

Хватит раскачки. Давайте займемся CNN!

3. Свертки

Что такое сверточные нейронные сети?

По сути, это просто нейронные сети, использующие сверточные слои, также известные как Conv-слои, которые основаны на математической операции свертки. Слои Conv состоят из набора фильтров, которые можно представить себе как простую двумерную матрицу чисел. Вот пример фильтра 3x3:

Мы можем использовать входное изображение и фильтр для создания выходного изображения, сворачивая фильтр с входным изображением. Это состоит из

  1. Наложение фильтра поверх изображения в каком-то месте.
  2. Выполнение поэлементного умножения между значениями в фильтре и соответствующими им значениями на изображении.
  3. Подводя итог всем поэлементным произведениям. Эта сумма является выходным значением для целевого пикселя в выходном изображении.
  4. Повторение для всех локаций.

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

Это четырехэтапное описание было немного абстрактным, поэтому давайте рассмотрим пример. Рассмотрим это крошечное изображение в оттенках серого 4x4 и этот фильтр 3x3:

Цифры на изображении представляют интенсивность пикселей, где 0 - черный, а 255 - белый. Мы свяжем входное изображение и фильтр, чтобы получить выходное изображение 2x2:

Для начала давайте наложим наш фильтр в левый верхний угол изображения:

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

Далее подводим все итоги. Это достаточно просто: 62–33 = 29.

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

Мы делаем то же самое, чтобы сгенерировать остальную часть выходного изображения:

3.1 Чем это полезно?

Давайте на секунду уменьшим масштаб и посмотрим на это на более высоком уровне. Что делает свертка изображения с помощью фильтра? Мы можем начать с использования примера фильтра 3x3, который мы использовали, который широко известен как вертикальный фильтр Собеля:

Вот пример того, что делает вертикальный фильтр Собеля:

Точно так же есть и горизонтальный фильтр Собеля:

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

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

3.2 Заполнение

Помните, как раньше свертывали входное изображение 4x4 с фильтром 3x3 для создания выходного изображения 2x2? Часто мы предпочитаем, чтобы выходное изображение было того же размера, что и входное. Для этого мы добавляем нули вокруг изображения, чтобы можно было наложить фильтр в большем количестве мест. Фильтр 3x3 требует заполнения 1 пикселя:

Это называется «одинаковым» заполнением, поскольку входные и выходные данные имеют одинаковые размеры. Отсутствие заполнения, что мы делали и будем делать для этого сообщения, иногда называют «допустимым» заполнением.

3.3 Слои Conv

Теперь, когда мы знаем, как работает свертка изображений и почему она полезна, давайте посмотрим, как она на самом деле используется в CNN. Как упоминалось ранее, CNN включают сверточные слои, которые используют набор фильтров для преобразования входных изображений в выходные изображения. Первичный параметр сверточного слоя - это количество фильтров, которые у него есть.

Для нашей MNIST CNN мы будем использовать небольшой сверточный слой с 8 фильтрами в качестве начального слоя в нашей сети. Это означает, что входное изображение 28 x 28 превратится в объем выходного изображения 26 x 26 x 8:

Напоминание: размер выходных данных составляет 26 x 26 x 8, а не 28 x 28 x 8, поскольку мы используем допустимые отступы, которые уменьшают ширину и высоту ввода на 2.

Каждый из 8 фильтров в сверточном слое дает результат 26x26, поэтому, сложенные вместе, они составляют объем 26x26x8. Все это происходит из-за того, что 3 x 3 (размер фильтра) x 8 (количество фильтров) = всего 72 веса!

3.4 Реализация свертки

Пора записать то, что мы узнали, в код! Мы реализуем часть прямого канала свёрточного слоя, которая заботится о свёртывании фильтров с входным изображением для получения выходного объема. Для простоты предположим, что фильтры всегда имеют размер 3x3 (что неверно - фильтры 5x5 и 7x7 также очень распространены).

Приступим к реализации класса сверточного слоя:

Класс Conv3x3 принимает только один аргумент: количество фильтров. В конструкторе мы сохраняем количество фильтров и инициализируем массив случайных фильтров, используя метод NumPy randn ().

Примечание. Погружение на 9 во время инициализации более важно, чем вы думаете. Если начальные значения слишком велики или слишком малы, обучение сети будет неэффективным. Чтобы узнать больше, прочтите Инициализация Xavier.

Далее собственно свертка:

iterate_regions() - вспомогательный метод генератора, который генерирует для нас все допустимые области изображения 3x3. Это будет полезно для реализации обратной части этого класса позже.

Строка 26 фактически выполняет свертки. Давайте разберемся:

  • У нас есть im_region, массив 3x3, содержащий соответствующую область изображения.
  • У нас есть self.filters, трехмерный массив.
  • Мы делаем, что использует функцию широковещательной передачи numpy для поэлементного умножения двух массивов. Результатом является трехмерный массив того же размера, что и self.filters.
  • Мы np.sum () результат предыдущего шага, используя, который создает 1d массив длиной num_filters, где каждый элемент содержит результат свертки для соответствующего фильтра.

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

Пока все выглядит хорошо.

Примечание: в нашей реализации Conv3x3 мы предполагаем, что ввод представляет собой массив чисел 2d для простоты, потому что так хранятся наши изображения MNIST. Это работает для нас, потому что мы используем его в качестве первого уровня в нашей сети, но большинство CNN имеют гораздо больше уровней Conv. Если бы мы строили более крупную сеть, в которой нужно было бы использовать Conv3x3 несколько раз, нам пришлось бы сделать входными данными массив 3d numpy.

4. Объединение

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

Объединение слоев решает эту проблему. Все, что они делают, - это уменьшают размер входных данных, которые он дает (как вы уже догадались) объединяя значения во входных данных. Объединение обычно выполняется простой операцией, такой как max, min или average. Вот пример слоя Max Pooling с размером пула 2:

Чтобы выполнить объединение max, мы просматриваем входное изображение блоками 2x2 (поскольку размер пула = 2) и помещаем значение max в выходное изображение в соответствующем пикселе. Вот и все!

При объединении ширина и высота ввода делятся на размер пула. Для нашей MNIST CNN мы разместим слой Max Pooling с размером пула 2 сразу после нашего начального сверточного слоя. Слой объединения преобразует вход 26x26x8 в выход 13x13x8:

4.1 Реализация пула

Мы реализуем класс MaxPool2 с теми же методами, что и наш класс conv из предыдущего раздела:

Этот класс работает аналогично классу Conv3x3, который мы реализовали ранее. Критическая строка - строка 30: чтобы найти максимум из заданной области изображения, мы используем np.amax (), метод массива numpy max. Мы устанавливаем, потому что хотим максимизировать только первые два измерения, высоту и ширину, а не третье, num_filters.

Давай проверим!

Наш канал MNIST CNN начинает объединяться!

5. Софтмакс

Чтобы завершить наш CNN, нам нужно дать ему возможность делать прогнозы. Мы сделаем это, используя стандартный конечный слой для задачи мультиклассовой классификации: слой Softmax, полностью связанный (плотный) слой, который использует функцию Softmax в качестве своей активации.

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

Если вы раньше не слышали о Softmax, прочтите мое быстрое введение в Softmax, прежде чем продолжить.

5.1 Использование

Мы будем использовать слой softmax с 10 узлами, по одному для каждой цифры в качестве последнего слоя в нашей CNN. Каждый узел в слое будет подключен к каждому входу. После применения преобразования softmax цифра, представленная узлом с наибольшей вероятностью, будет выходом CNN!

5.2 Потери перекрестной энтропии

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

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

где c - правильный класс (в нашем случае правильная цифра), pc - прогнозируемая вероятность для класса c, а ln - натуральный журнал. Как всегда, чем меньше потеря, тем лучше. Например, в лучшем случае у нас было бы

В более реалистичном случае мы могли бы иметь

Позже в этом посте мы снова увидим потерю кросс-энтропии, так что имейте это в виду!

5.3 Реализация Softmax

Вы уже знаете, что такое упражнение - давайте реализуем Softmax класс слоя:

Здесь нет ничего слишком сложного. Несколько основных моментов:

  • Мы сглаживаем () ввод, чтобы упростить работу, поскольку нам больше не нужна его форма.
  • Np.dot () поэлементно умножает input и self.weights, а затем суммирует результаты.
  • Np.exp () вычисляет экспоненты, используемые для Softmax.

Мы завершили весь прямой проход нашего CNN! Собираем вместе:

Запуск cnn.py дает примерно такой результат:

MNIST CNN initialized!
[Step 100] Past 100 steps: Average Loss 2.302 | Accuracy: 11%
[Step 200] Past 100 steps: Average Loss 2.302 | Accuracy: 8%
[Step 300] Past 100 steps: Average Loss 2.302 | Accuracy: 3%
[Step 400] Past 100 steps: Average Loss 2.302 | Accuracy: 12%

В этом есть смысл: при инициализации случайного веса можно ожидать, что CNN будет работать так же хорошо, как и наугад. Случайное угадывание дало бы точность 10% (поскольку существует 10 классов) и потерю кросс-энтропии −ln (0,1) = 2,302, что мы и получаем!

Хотите попробовать или поработать с этим кодом самостоятельно? Запустите этот CNN в своем браузере. Он также доступен на Github.

6. Заключение

На этом введение в CNN подошло к концу! В этом посте мы

  • Мотивировано, почему CNN могут быть более полезными для определенных проблем, таких как классификация изображений.
  • Представлен набор рукописных цифр MNIST.
  • Узнал о слоях Conv, которые объединяют фильтры с изображениями для получения более полезных результатов.
  • Говорили о объединении слоев, которое может помочь избавиться от всего, кроме самых полезных функций.
  • Реализован слой Softmax, чтобы можно было использовать кросс-энтропийную потерю.

Есть еще много чего, что мы еще не рассмотрели, например, как на самом деле обучать CNN. В моем следующем посте я подробно расскажу об обучении CNN, включая получение градиентов и реализацию обратного распространения, так что следите за обновлениями!

Если вы хотите увидеть обученный CNN в действии: этот пример Keras CNN, обученный по MNIST, обеспечивает точность 99,25%. CNN сильны!

Первоначально опубликовано на https://victorzhou.com.