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

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

  • Классификация изображений (сверточные нейронные сети).
  • Генерация изображений, аудио и текста (GAN, RNN).
  • Прогнозирование временных рядов (RNNs, LSTM).
  • Системы рекомендаций.
  • Огромный и так далее (например, регресс).

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

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

В многослойных персептронах (MLP), ванильных нейронных сетях, нейроны каждого слоя соединяются со всеми нейронами следующего слоя. Мы называем этот тип слоев полностью связанными.

Сверточная нейронная сеть отличается: у них есть сверточные слои.

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

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

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

Что такое свертка?

Операция свертки, заданная входной матрицей A (обычно значениями предыдущего слоя) и (обычно намного меньшей) матрицей весов, называемой ядром или фильтром K, будет выведена новая матрица B.

Если K является матрицей CxC, первый элемент в B будет результатом:

  • Взяв первую подматрицу CxC матрицы A.
  • Умножение каждого из его элементов на соответствующий вес в K.
  • Добавление всех товаров.

Эти два последних шага эквивалентны сглаживанию подматрицы A и K и вычислению скалярного произведения результирующих векторов.

Затем мы сдвигаем K вправо, чтобы перейти к следующему элементу, и так далее, повторяя этот процесс для каждой из строк A ’.

В зависимости от того, что мы хотим, мы могли бы начать только с нашим ядром, центрированным в строке и столбце Cth, чтобы избежать «выхода за границы», или предположить, что все элементы «за пределами A» имеют определенное значение по умолчанию. (обычно 0) - определяет, будет ли размер B меньше, чем A, или тот же самый.

Как видите, если A был матрицей NxM, теперь значение каждого нейрона в B не будет зависеть от N * M веса, но только на C * C (намного меньше) из них. Это делает сверточный слой намного легче, чем полностью связанный, что помогает сверточным моделям учиться намного быстрее.

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

Почему это работает?

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

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

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

Соседи пикселя обычно имеют аналогичные значения RGB. Если нет, то это, вероятно, означает, что мы находимся на краю фигуры или объекта.

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

В качестве примера рассмотрим следующие ядра V и H:

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

Свертки на примере

Вот нефильтрованное изображение помета котят:

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

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

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

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

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

Как обучить сверточную нейронную сеть в TensorFlow?

TensorFlow - самый популярный фреймворк Python для глубокого обучения. Я тоже слышал много хорошего о PyTorch, хотя у меня никогда не было возможности попробовать его.

Я уже написал одно руководство на тему Как обучить нейронную сеть с помощью TensorFlow’s Eager API с упором на автоэнкодеры.

Сегодня все будет по-другому: мы попробуем три разные архитектуры и посмотрим, какая из них лучше. Как обычно, весь код доступен на GitHub, так что вы можете попробовать все самостоятельно или продолжить. Конечно, я также буду показывать вам фрагменты Python.

Набор данных

Мы будем обучать нейронную сеть предсказывать, есть ли на изображении собака или кошка. Для этого мы воспользуемся набором данных Kaggle Кошки и собаки. Он содержит 12500 картинок кошек и 12500 собак с разным разрешением.

Загрузка и предварительная обработка данных изображения с помощью NumPy

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

К счастью для нас, библиотека изображений Python предоставляет нам простой способ загрузить изображение в виде массива NumPy. Матрица HeightxWidth значений RGB.
Мы уже делали это в этой статье, поэтому я просто повторно воспользуюсь этим кодом.

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

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

Я сделал выборку из первых 1000 изображений для этого, хотя результат не изменился, когда я посмотрел на 5000. Самая распространенная форма была 375 × 500, хотя я решил разделить это на 4 для входных данных нашей сети.

Вот как теперь выглядит наш код загрузки изображений.

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

Не стесняйтесь увеличивать эти числа до максимума (например, 10K для обучения и 2500 для проверки), если вы попробуете это дома!

Обучение наших нейронных сетей

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

Итак, вот один скрытый слой, полностью подключенная нейронная сеть.

Все тренинги для этой статьи проводились с помощью AdamOptimizer, так как он самый быстрый. Я настраивал только скорость обучения для каждой модели (здесь она была 1e-5).

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

Я использовал MSE как функцию потерь, поскольку обычно она более интуитивно понятна. Если ваш MSE равен 0,5 в двоичной классификации, вы так же хорошо, как всегда прогнозируете 0. Однако MLP с большим количеством слоев или другими функциями потерь не работают лучше.

Обучение сверточной нейронной сети

Насколько хорош один сверточный слой? Давайте добавим один к нашей модели и посмотрим.

Для этой сети я решил добавить один сверточный слой (с 24 ядрами), за которым следуют 2 полностью связанных слоя.

Все, что делает Max Pooling, сводит каждые четыре нейрона к одному с наивысшим значением между четырьмя.

Спустя всего 5 эпох она уже работает намного лучше, чем предыдущие сети. При проверке MSE 0,36 это было намного лучше, чем уже случайное предположение. Однако обратите внимание, что мне пришлось использовать гораздо меньшую скорость обучения. Кроме того, несмотря на то, что он учился за меньшее количество эпох, каждая эпоха занимала гораздо больше времени. Модель также значительно тяжелее (200+ МБ).

Я решил также начать измерять корреляцию Пирсона между прогнозами и метками проверки. Эта модель набрала 15,2%.

Нейронная сеть с двумя сверточными слоями

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

Результаты были превосходными. Коэффициент корреляции Пирсона между предсказаниями и метками достиг 0,21, а MSE проверки - всего 0,33.

Давайте измерим точность сети. Поскольку 1 - кошка, а 0 - собака, я мог бы сказать: «Если модель предсказывает значение выше некоторого порогового значения t, то предсказывайте cat. Иначе предсказать dog. " После проверки 10 простых пороговых значений эта сеть показала максимальную точность 61%.

Еще большая сверточная нейронная сеть

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

Эта модель наконец достигла корреляции 30%! Его наилучшая точность составила 67%, что означает, что она была правильной в двух третях случаев. Я полагаю, что даже более крупная модель могла бы еще лучше соответствовать данным. Однако эта тренировка уже занимала 7 минут на каждую эпоху, и я не хотел оставлять следующую тренировку на все утро.

Обычно требуется компромисс между размером модели и временными ограничениями. Размер ограничивает то, насколько хорошо сеть может соответствовать данным (небольшая модель не подходит), однако я не буду ждать 3 часа, пока моя модель изучит.

Те же проблемы могут возникнуть, если у вас есть рабочий срок.

Выводы

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

Мы узнали о компромиссе между размером модели (который предотвращает недостаточное соответствие) и ее скоростью сходимости.

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

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

Вам помогла данная статья? Вы бы предпочли узнать больше о чем-нибудь другом? Ничего не достаточно ясно? Дай мне знать в комментариях!

Найдите меня в Twitter, Medium или Dev.to, если у вас есть какие-либо вопросы или вы хотите связаться со мной по любому поводу. Если вы хотите начать карьеру в области машинного обучения, вот мой список рекомендованной литературы.

Первоначально опубликовано на http://www.datastuff.tech 12 июня 2019 г.