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

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

Фон ML

Передача нейронного стиля берет абстрактный стиль одного изображения и применяет его к содержимому второго изображения. Эта техника была впервые опубликована в статье Нейронный алгоритм художественного стиля авторов Леона А. Гатиса, Александра С. Экера, Маттиаса Бетге, но вы, вероятно, знаете ее лучше всего из примера Ван Гога или из использования приложение Prisma.

Для алгоритма требуются три изображения: Изображение содержимого, Изображение стиля и Входное изображение (обычно копия Изображение содержимого ). Мы обновим Входное изображение, чтобы оно сохранило основные функции Content Image и получило абстрактный стиль Style Image. Чтобы изменить темп, мы воспользуемся этой техникой, чтобы попытаться превратить популярного персонажа Игры престолов Джона Сноу в гуманоидное ледяное существо, известное как Белый Ходок! Используя стандартное изображение Джона Сноу в качестве изображения содержимого и рисунок Белого ходока в качестве изображения стиля, мы можем исследовать, как работает этот алгоритм, и генерировать новые сюжетные идеи для Джорджа. Р. Р. Мартин в то же время.

* Вы можете перейти к концу, если хотите просто увидеть результат

Я включил фрагменты кода полностью, но вы можете проверить полную базу кода на Github.

Обзор - 6 шагов к зомбификации

  1. Функция потерь. Создайте общую функцию потерь, чтобы узнать, чем одно изображение отличается от другого.
  2. Оптимизатор. Создайте оптимизатор, который может обновлять одно изображение, чтобы оно было немного больше похоже на другое изображение.
  3. Предварительно обученный классификатор. Загрузите предварительно обученную нейронную сеть (VGG19) и зарегистрируйте хуки на определенных уровнях.
  4. Потеря содержимого. Создайте потерю содержимого, пропустив входные изображения и изображения содержимого через сеть VGG, а затем передав выходные данные в функцию потери.
  5. Потеря стиля. Создайте потерю стиля, пропустив входные изображения и изображения стиля через сеть VGG. Преобразование каждого вывода в матрицу Грама (я объясню позже), а затем передача их в функцию потерь.
  6. Зажигай! - объедините оба убытка, сделайте обратную оценку общей суммы, измените оптимизатор и повторите.

Шаг 1 - Общая функция потерь (Чем отличаются эти две вещи?)

* Примечание: мы будем использовать Python 3.6 и PyTorch. Код следует запустить в вашем Jupyter Notebook, чтобы просмотреть некоторые анимированные результаты. После того, как у вас есть эти настройки, установите модули в файле requirements.txt, чтобы все заработало.

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

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

Чтобы создать функцию потерь, нам нужно сравнить Входное изображение с целью (либо Content, либо Style Image). Мы можем получить среднеквадратичную ошибку между ними, используя встроенную в PyTorch функцию потерь MSE.

Теперь у нас есть общая функция потерь, которую можно использовать повторно. Если вы передадите ему два тензора, будет возвращена ошибка (насколько они разные?).

Шаг 2. Оптимизатор (сделайте эти две вещи более похожими)

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

* Примечание: оба изображения должны быть одинакового размера (я сделал все свои изображения 150x150).

  • Загрузите Входное изображение (Джон Сноу) и целевое изображение (Король ночи).
  • Пропустите оба изображения через помощник image_loader (), чтобы изменить размер изображений, превратить их в тензоры факела и добавить другие размеры для размера пакета (требуется PyTorch).
  • Определите, сколько эпох мы хотим, чтобы оптимизатор работал, и какова будет наша функция оптимизатора. Мы будем использовать встроенный оптимизатор PyTorch под названием LBFGS, чтобы сделать Входное изображение немного больше похожим на целевое изображение с каждой эпохой. Функция closure () существует, потому что этого требует LBFGS. По сути, он выполняет 1,25 оптимизации за эпоху, но нам не нужно особо об этом беспокоиться.
  • Для каждой эпохи мы передаем Входные и целевые изображения в функцию потерь.
  • Узнайте, насколько они разные (calc_loss).
  • Возврат убытка (убыток. Назад).
  • Немного обновите Входное изображение и повторите (optimizer.step).

Мы можем увидеть, что происходит после каждого шага оптимизатора, передав тензор в animate_output (), и он отобразит изображение на выходе ячейки. С каждой эпохой оптимизатор делает Джона Сноу немного больше похожим на Короля ночи, пока после 140 эпох изображение не становится почти точным изображением Короля ночи.

Получается довольно крутой .gif!

Использование оптимизатора для постепенного уменьшения потерь является ключевым компонентом многих замечательных приложений, таких как беспилотные автомобили и машинный перевод. Мы также можем использовать эту технику для решения не менее важных задач, таких как победа над своим 6-летним ребенком в игре «Найди отличия».

Если мы пропустим оба изображения через оптимизатор, мы увидим, что все различия постепенно проявляются.

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

Шаг 3 - Загрузите предварительно обученный классификатор изображений

Проблема. Наш оптимизатор заставит наше входное изображение выглядеть точно так же, как целевое изображение. Идеальное совпадение пикселей. Когда мы передаем ему наш Стиль и изображения содержимого, оно также сделает Входное изображение идеальным для них пикселем. Мы этого не хотим. Мы хотим сохранить большую часть пространственной информации из Content Image (он должен по-прежнему выглядеть как Джон Сноу) и выбросить пространственную информацию только из Style Image. сохранение текстуры, цвета, формы линий и т. д. Итак, какой инструмент мы можем использовать, чтобы попытаться извлечь эту информацию из наших изображений?

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

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

Будет загружена предварительно обученная сеть VGG, и если вы распечатаете (vgg19), вы увидите все разные слои.

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

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

Шаг 4 - потеря контента

Для потери контента нам сначала нужно пропустить Content Image через сеть VGG и сохранить вывод определенного слоя. Затем мы проведем Входное изображение по сети, получим результат на том же уровне и сравним два изображения, чтобы определить потерю содержимого.

И если мы изменим наш предыдущий оптимизатор, мы сможем визуализировать некоторые функции, которые активируют каждый из слоев.

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

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

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

Шаг 5 - Потеря стиля

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

  1. Используйте более одного слоя для потери стиля, затем сложите все эти потери вместе.
  2. Создайте «Матрицу Грама» для выходных данных каждого слоя, затем передайте их в функцию потерь.

Но что такое матрица граммов и зачем она нам нужна?

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

Шаг 6. Давайте соберем все вместе и создадим белых ходоков.

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

Мы только что создали нашего первого Белого ходока!

А теперь сделаем еще ...

Попробуйте другой стиль изображения…

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

Так что поиграйте с кодом. Посмотри, что получится!

  • Отрегулируйте скорость обучения.
  • Попробуйте разные изображения содержимого и стиля / веса.
  • Используйте разные слои в сети.
  • Может даже попробовать альтернативу VGG19.

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

Бонусный вопрос:

  1. Что произошло во время этой оптимизации?
  2. Что мы можем сделать, чтобы это исправить?