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

Это выглядит так:

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

Вы можете посмотреть полное видео здесь: https://www.youtube.com/watch?v=FzvTLEB_3KY

Довольно круто, правда?

Это короткий фрагмент из фильма 1950 года бельгийского режиссера Поля Хэзертса «Визит в Пикассо», в котором Пикассо демонстрирует свое мастерство.

Каждый кадр видео был обработан нейронной сетью Artistic Style Transfer. В результате получается гипнотический и сказочный эпизод, в котором мы видим, как Пикассо рисует своего быка, но визуальный стиль постоянно меняется, поскольку программное обеспечение плавно переключается между разными художественными стилями.

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

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

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

Итак, вот как работает процесс переноса стиля.

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

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

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

Полный процесс обучения теперь очень прост:

  1. Покажите нейронной сети изображения контента и стиля и рассчитайте базовые уровни контента и стиля.
  2. Покажите случайное изображение нейронной сети и рассчитайте потерю контента и потерю стиля.
  3. Настройте пиксели в случайном изображении, чтобы уменьшить потерю содержимого и стиля. Повторяйте, пока потеря не станет приемлемой.

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

Процесс выглядит так:

Посмотрим, смогу ли я создать приложение для переноса стилей на C # с библиотекой Microsoft Cognitive Toolkit.

Я начну с создания нового консольного приложения с нуля с помощью NET Core:

$ dotnet new console -o StyleTransferDemo

Теперь я установлю пакет CNTK:

$ dotnet add package CNTK.Gpu

Библиотека CNTK.GPU - это Cognitive Toolkit от Microsoft, который может обучать и запускать глубокие нейронные сети. Он будет обучать и запускать глубокие нейронные сети с помощью вашего графического процессора. Для этого вам понадобится графический процессор NVidia и графические драйверы Cuda.

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

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

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

Теперь из папки проекта консоли я могу создать ссылку на проект следующим образом:

$ dotnet add reference ../CNTKUtil/CNTKUtil.csproj

Теперь я готов начать писать код. Я отредактирую файл Program.cs с помощью кода Visual Studio и добавлю следующий код:

Код вызывает NetUtil.CurrentDevice для отображения вычислительного устройства, которое будет использоваться для обучения нейронной сети.

Затем я использую вспомогательный класс StyleTransfer и дважды вызываю LoadImage, чтобы загрузить изображение содержимого и изображение стиля.

Теперь мне нужно сообщить CNTK, какую форму имеют входные данные, на которых я буду тренировать нейронную сеть:

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

Мой следующий шаг - спроектировать нейронную сеть. Я собираюсь использовать сеть VGG19, но оставлю только сверточные слои для обнаружения контента и потери стиля:

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

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

Вычисление меток на основе модели и изображений содержимого и стиля - сложная операция, но, к счастью, есть удобный метод под названием CalculateLabels, который делает все это автоматически. Результатом является массив float [] [], который содержит желаемые уровни активации в слоях контента и стилей, которые позволят нейронной сети узнать, что передача стиля была достигнута.

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

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

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

Этот код вызывает GetContentAndStyleLayers для доступа к слоям контента и стилей в сети VGG19, перебирает все метки в массиве меток и создает массив переменных CNTK с правильным Shape.

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

Функция потерь для переноса стиля довольно сложна, но, к счастью, я могу настроить ее с помощью одного вызова CreateLossFunction, предоставив модель, слои содержимого и стиля, а также переменную метки CNTK.

Затем мне нужно решить, какой алгоритм использовать для обучения нейронной сети. Есть много возможных алгоритмов, полученных из Gradient Descent, которые я могу использовать здесь.

Я собираюсь использовать AdamLearner. Вы можете узнать больше об алгоритме Адама здесь: https: //machinelearningmastery.com/adam ...

Я почти готов к тренировкам. Мой последний шаг - настроить трейнер для расчета потерь в каждую тренировочную эпоху:

Метод GetTrainer настраивает трейнер, который будет отслеживать потери в процессе переноса стиля.

Теперь я наконец готов приступить к обучению нейронной сети!

Я добавлю следующий код:

Я обучаю сеть для 300 эпох, используя пакет обучения, настроенный методом CreateBatch. Метод TrainMiniBatch обучает нейронную сеть за одну эпоху. И каждые 50 эпох я отображаю убыток, вызывая метод PreviousMinibarchLossAverage.

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

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

Теперь у меня есть значение для каждого пикселя в массиве float [], поэтому я вызываю конструктор Mat, чтобы проецировать эти значения на 8-битное трехканальное цветное изображение и вызвать метод ImShow для визуализации изображения на экране.

Обратите внимание, что Mat и ImShow являются функциями OpenCV. OpenCV - это гибкая библиотека изображений, используемая CNTKUtil для реализации передачи стилей.

Наконец, я вызываю WaitKey, чтобы изображение оставалось на экране после завершения работы приложения, и у меня было время полюбоваться результатами переноса стиля.

Теперь я готов запустить приложение. Сначала я перейду в папку CNTKUtil и скомпилирую проект:

$ dotnet build -o bin/Debug/netcoreapp3.0 -p:Platform=x64

Обратите внимание, как я указываю платформу x64, потому что для библиотеки CNTK требуется 64-разрядная сборка.

Теперь я могу сделать то же самое в папке StyleTransferDemo:

$ dotnet build -o bin/Debug/netcoreapp3.0 -p:Platform=x64

Это создаст мое приложение. Обратите внимание, как я снова указываю платформу x64.

Теперь я могу запустить приложение:

$ dotnet run

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

Посмотрите, вот мой первый запуск:

Изображение содержимого - это изображение моего профиля, которое отображается в правом верхнем углу. Образ стиля - известная кубистическая картина Любови Поповой, которая видна справа внизу. Слева - сгенерированное смешанное изображение.

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

Попробуем еще раз:

Узнаешь стиль имиджа? Это знаменитая картина Винсента Ван Гога "Звездная ночь". Нейронная сеть перекрасила мой аватар, используя его знаменитые триповые синие завитки и желтые блики.

Хорошо, еще одно:

Теперь я использую знаменитую картину Эдварда Мунка «Крик» в качестве стилевого изображения. Что вы думаете о результате?

Итак, это мои результаты. Не стесняйтесь использовать код и создавать собственные изображения стиля с помощью C # и CNTK.

Эта статья основана на домашнем задании из моего курса машинного обучения: Глубокое обучение с C # и C NTK .