В этой статье представлено руководство по передаче нейронного стиля с использованием глубокого обучения. Нейронный перенос стиля - это техника совмещения изображений в стиле другого изображения. Neural Style Transfer принимает в качестве входных данных три изображения, а именно изображение, которое вы хотите стилизовать: изображение содержимого, изображение стиля и комбинированное изображение, которое изначально является копией изображения содержимого. Эта техника смешивает комбинированное изображение таким образом, чтобы оно напоминало изображение содержимого, нарисованное в стиле стиля изображения. Например, возьмем следующее изображение Контента: мое изображение и картину Удни Фрэнсиса Пикабиа в качестве изображения стиля.
Комбинированное изображение будет выглядеть примерно так, как на изображении ниже.
Поток статьи будет следующим: -
- Введение в передачу нейронного стиля - подход к оптимизации
- Используемые различные функции потерь
- Выполнение
- Введение в быструю передачу нейронного стиля
- Используемая архитектура модели
- Использование предварительно обученных моделей для стилизации изображений
- Результаты
- Вывод
Полный код находится в этой записной книжке Cainvas.
Введение в передачу нейронного стиля - подход к оптимизации
Леон Гэтис и др. представила технику передачи нейронного стиля в 2015 году в «Нейронном алгоритме художественного стиля». Как указывалось ранее, Neural Style Transfer - это техника компоновки изображений в стиле другого изображения. Более формальное определение, изложенное в Википедии:
Передача нейронного стиля (NST) относится к классу программных алгоритмов, которые манипулируют цифровыми изображениями или видео для адаптации внешнего вида или визуального стиля другого изображения. Алгоритмы NST характеризуются использованием глубоких нейронных сетей для преобразования изображений.
Если вы хотите углубиться в оригинальную технику, можете обратиться к статье по этой ссылке.
Как вы, возможно, знаете, в сверточной нейронной сети (CNN) сверточные слои, за которыми следует активация, слои MaxPooling / AveragePooling выполняют задачу извлечения признаков. Полностью связанные слои следуют за этими слоями выделения признаков для выполнения классификации с использованием функции активации Softmax. Итак, как и в Neural Style Transfer, нас больше интересуют особенности изображений; промежуточные сверточные слои предварительно обученной модели CNN используются для извлечения признаков из изображения содержимого и изображения стиля.
Первоначальная реализация метода использовала сеть VGG19 в качестве предварительно обученной модели CNN. В этой статье я буду использовать сеть VGG16 вместо VGG19. Архитектура модели после удаления полностью связанных слоев представлена ниже:
Model: "model" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, None, None, 3)] 0 _________________________________________________________________ block1_conv1 (Conv2D) (None, None, None, 64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (None, None, None, 64) 36928 _________________________________________________________________ block1_pool (MaxPooling2D) (None, None, None, 64) 0 _________________________________________________________________ block2_conv1 (Conv2D) (None, None, None, 128) 73856 _________________________________________________________________ block2_conv2 (Conv2D) (None, None, None, 128) 147584 _________________________________________________________________ block2_pool (MaxPooling2D) (None, None, None, 128) 0 _________________________________________________________________ block3_conv1 (Conv2D) (None, None, None, 256) 295168 _________________________________________________________________ block3_conv2 (Conv2D) (None, None, None, 256) 590080 _________________________________________________________________ block3_conv3 (Conv2D) (None, None, None, 256) 590080 _________________________________________________________________ block3_pool (MaxPooling2D) (None, None, None, 256) 0 _________________________________________________________________ block4_conv1 (Conv2D) (None, None, None, 512) 1180160 _________________________________________________________________ block4_conv2 (Conv2D) (None, None, None, 512) 2359808 _________________________________________________________________ block4_conv3 (Conv2D) (None, None, None, 512) 2359808 _________________________________________________________________ block4_pool (MaxPooling2D) (None, None, None, 512) 0 _________________________________________________________________ block5_conv1 (Conv2D) (None, None, None, 512) 2359808 _________________________________________________________________ block5_conv2 (Conv2D) (None, None, None, 512) 2359808 _________________________________________________________________ block5_conv3 (Conv2D) (None, None, None, 512) 2359808 _________________________________________________________________ block5_pool (MaxPooling2D) (None, None, None, 512) 0 ================================================================= Total params: 14,714,688 Trainable params: 14,714,688 Non-trainable params: 0 _________________________________________________________________
После извлечения функций из изображения содержимого, изображения стиля и комбинированного изображения для минимизации потерь определяются функции потерь, описанные в следующем разделе.
Используемые различные функции потерь
В методике передачи нейронного стиля в основном определены две функции потерь - функция потери содержимого и функция потери стиля.
Функция потери контента
Функция потери содержимого определяет расстояние между функциями, извлеченными из изображения содержимого и входного изображения. Потеря содержимого - это просто евклидово расстояние между характеристиками изображения содержимого и входного изображения.
Мы используем метод обратного распространения ошибки для минимизации этой функции потерь, чтобы входное изображение имело почти такое же содержимое, что и изображение содержимого в конце алгоритма.
Функция потери стиля
Как вы уже догадались, эта функция сравнивает функции стиля изображения и входного изображения. Процесс сравнения аналогичен функции потери контента. Однако эта функция использует матрицу Грама для сравнения характеристик обоих изображений.
Мы также используем технику обратного распространения, чтобы гарантировать, что окончательное изображение также имеет некоторые функции Style Image, чтобы минимизировать эту функцию потерь.
Полная потеря модели - это сумма потери контента и потери стиля.
Выполнение
В предыдущих разделах я дал общее введение в технику переноса нейронного стиля на основе оптимизации. Теперь перейдем к реализации методики.
Первый шаг - импортировать все необходимые библиотеки.
Загрузка входного изображения и изображения стиля и их отображение с помощью matplotlib
Это отобразит изображение стиля и входное изображение, показанные в начале этой статьи.
Следующим шагом является определение функций для предварительной обработки изображений, которые будут загружены в модель, и деобработки изображений для отображения выходного изображения.
Затем определите функцию потери содержимого и функцию потери стиля вместе с вспомогательными функциями.
Следующим шагом будет загрузка модели VGG16.
Укажите слои модели для извлечения признаков и определите функцию вычисленных потерь.
Здесь я использовал tf.GradientTape, чтобы обеспечить автоматическое создание градиента функции потерь для метода обратного распространения ошибки.
Теперь последний шаг - определение цикла обучения.
Как вы можете видеть в строке 24, я использовал функцию compute_loss_and_grads для вычисления значения потерь и градиентов. А затем в строке 27 применили эти градиенты для минимизации значения потерь в дальнейших итерациях.
Кроме того, код печатает значение потерь после каждых 100 итераций, а на 8000-й итерации сохраняет окончательное комбинированное изображение в каталоге. Стохастический градиентный спуск (SGD) с экспоненциальным затуханием работает как оптимизатор модели, но вы также можете поэкспериментировать с Адамом или любым другим оптимизатором по вашему выбору.
Величина потерь на 100-й итерации составила 3409,53, а на 8000-й итерации снизилась до 1162,70.
График зависимости потерь от количества итераций представлен ниже:
Теперь я покажу вам окончательное комбинированное изображение: -
Теперь вы можете подумать, что этот вывод не соответствует изображению, которое я показал в начале. Это даже не близко! В этот момент вы можете подумать, что зря потратили время на чтение этой статьи и реализацию длинного метода бок о бок, но все же не смогли добиться хороших результатов.
Тем не менее, я предлагаю вам продолжить чтение, вам предстоит долгий путь в этой статье, и вы очень близки к окончательному результату. Я обещаю, что вы сгенерируете изображение, подобное выходному изображению, которое я представил ранее, к концу этой статьи.
Введение в быструю передачу нейронного стиля
В предыдущих разделах я описал технику переноса нейронного стиля на основе оптимизации. Методика имеет некоторые недостатки и ограничения, которые описаны ниже:
- Каждый раз, когда вы меняете изображение содержимого, которое хотите стилизовать, вам необходимо повторно запускать весь процесс для создания окончательного комбинированного изображения, что требует много времени и не может использоваться для развертываемого проекта.
- В конце предыдущего раздела я представил результат этой техники, который оказался не таким хорошим, как вы могли ожидать. Однако нет сомнений в том, что этот метод также может дать удовлетворительные результаты, но вам придется поэкспериментировать с разными значениями веса стиля и веса контента. Вы должны использовать различные методы обработки изображений, чтобы улучшить результаты.
Обращаясь к этим проблемам, Джастин Джонсон, Александр Алахи, Ли Фей-Фей представили на выставке ECCV 2016 доклад под названием «Потери восприятия при передаче стиля в реальном времени и сверхвысоком разрешении». Кроме того, Дмитрий Ульянов усовершенствовал метод, предложенный в этой статье, в статье «Нормализация экземпляра: недостающий ингредиент для быстрой стилизации». Я не буду вдаваться в подробности этих документов, но если вы хотите прочитать эти документы, вы можете щелкнуть по приведенным ниже ссылкам.
- Потери восприятия при передаче стиля в реальном времени и сверхвысоком разрешении
- Нормализация экземпляра: недостающий ингредиент для быстрой стилизации
Исходная реализация использует сеть VGG16 с набором данных Microsoft COCO, содержащим около 80 тыс. Изображений, и использует оптимизатор Adam со скоростью обучения 0,001.
Этот репозиторий GitHub предоставляет технику быстрой нейронной передачи стилей с нормализацией экземпляров с изображениями различных стилей. Я буду использовать предварительно обученные модели, предлагаемые этим репозиторием, для стилизации изображений вместо повторного обучения модели.
Используемая архитектура модели
Ниже приведен обзор используемой сети: -
Model: "vgg16" __________________________________________________________________________________________________ Layer (type) Output Shape Param # Connected to ================================================================================================== input_3 (InputLayer) [(None, 64, 64, 3)] 0 __________________________________________________________________________________________________ input_normalize_1 (InputNormali (None, 64, 64, 3) 0 input_3[0][0] __________________________________________________________________________________________________ reflection_padding2d_4 (Reflect (None, 144, 144, 3) 0 input_normalize_1[0][0] __________________________________________________________________________________________________ conv2d_16 (Conv2D) (None, 144, 144, 32) 7808 reflection_padding2d_4[0][0] __________________________________________________________________________________________________ batch_normalization_16 (BatchNo (None, 144, 144, 32) 128 conv2d_16[0][0] __________________________________________________________________________________________________ activation_11 (Activation) (None, 144, 144, 32) 0 batch_normalization_16[0][0] __________________________________________________________________________________________________ conv2d_17 (Conv2D) (None, 72, 72, 64) 165952 activation_11[0][0] __________________________________________________________________________________________________ batch_normalization_17 (BatchNo (None, 72, 72, 64) 256 conv2d_17[0][0] __________________________________________________________________________________________________ activation_12 (Activation) (None, 72, 72, 64) 0 batch_normalization_17[0][0] __________________________________________________________________________________________________ conv2d_18 (Conv2D) (None, 36, 36, 128) 73856 activation_12[0][0] __________________________________________________________________________________________________ batch_normalization_18 (BatchNo (None, 36, 36, 128) 512 conv2d_18[0][0] __________________________________________________________________________________________________ activation_13 (Activation) (None, 36, 36, 128) 0 batch_normalization_18[0][0] __________________________________________________________________________________________________ conv2d_19 (Conv2D) (None, 34, 34, 128) 147584 activation_13[0][0] __________________________________________________________________________________________________ batch_normalization_19 (BatchNo (None, 34, 34, 128) 512 conv2d_19[0][0] __________________________________________________________________________________________________ activation_14 (Activation) (None, 34, 34, 128) 0 batch_normalization_19[0][0] __________________________________________________________________________________________________ conv2d_20 (Conv2D) (None, 32, 32, 128) 147584 activation_14[0][0] __________________________________________________________________________________________________ cropping2d_5 (Cropping2D) (None, 32, 32, 128) 0 activation_13[0][0] __________________________________________________________________________________________________ batch_normalization_20 (BatchNo (None, 32, 32, 128) 512 conv2d_20[0][0] __________________________________________________________________________________________________ add_5 (Add) (None, 32, 32, 128) 0 cropping2d_5[0][0] batch_normalization_20[0][0] __________________________________________________________________________________________________ conv2d_21 (Conv2D) (None, 30, 30, 128) 147584 add_5[0][0] __________________________________________________________________________________________________ batch_normalization_21 (BatchNo (None, 30, 30, 128) 512 conv2d_21[0][0] __________________________________________________________________________________________________ activation_15 (Activation) (None, 30, 30, 128) 0 batch_normalization_21[0][0] __________________________________________________________________________________________________ conv2d_22 (Conv2D) (None, 28, 28, 128) 147584 activation_15[0][0] __________________________________________________________________________________________________ cropping2d_6 (Cropping2D) (None, 28, 28, 128) 0 add_5[0][0] __________________________________________________________________________________________________ batch_normalization_22 (BatchNo (None, 28, 28, 128) 512 conv2d_22[0][0] __________________________________________________________________________________________________ add_6 (Add) (None, 28, 28, 128) 0 cropping2d_6[0][0] batch_normalization_22[0][0] __________________________________________________________________________________________________ conv2d_23 (Conv2D) (None, 26, 26, 128) 147584 add_6[0][0] __________________________________________________________________________________________________ batch_normalization_23 (BatchNo (None, 26, 26, 128) 512 conv2d_23[0][0] __________________________________________________________________________________________________ activation_16 (Activation) (None, 26, 26, 128) 0 batch_normalization_23[0][0] __________________________________________________________________________________________________ conv2d_24 (Conv2D) (None, 24, 24, 128) 147584 activation_16[0][0] __________________________________________________________________________________________________ cropping2d_7 (Cropping2D) (None, 24, 24, 128) 0 add_6[0][0] __________________________________________________________________________________________________ batch_normalization_24 (BatchNo (None, 24, 24, 128) 512 conv2d_24[0][0] __________________________________________________________________________________________________ add_7 (Add) (None, 24, 24, 128) 0 cropping2d_7[0][0] batch_normalization_24[0][0] __________________________________________________________________________________________________ conv2d_25 (Conv2D) (None, 22, 22, 128) 147584 add_7[0][0] __________________________________________________________________________________________________ batch_normalization_25 (BatchNo (None, 22, 22, 128) 512 conv2d_25[0][0] __________________________________________________________________________________________________ activation_17 (Activation) (None, 22, 22, 128) 0 batch_normalization_25[0][0] __________________________________________________________________________________________________ conv2d_26 (Conv2D) (None, 20, 20, 128) 147584 activation_17[0][0] __________________________________________________________________________________________________ cropping2d_8 (Cropping2D) (None, 20, 20, 128) 0 add_7[0][0] __________________________________________________________________________________________________ batch_normalization_26 (BatchNo (None, 20, 20, 128) 512 conv2d_26[0][0] __________________________________________________________________________________________________ add_8 (Add) (None, 20, 20, 128) 0 cropping2d_8[0][0] batch_normalization_26[0][0] __________________________________________________________________________________________________ conv2d_27 (Conv2D) (None, 18, 18, 128) 147584 add_8[0][0] __________________________________________________________________________________________________ batch_normalization_27 (BatchNo (None, 18, 18, 128) 512 conv2d_27[0][0] __________________________________________________________________________________________________ activation_18 (Activation) (None, 18, 18, 128) 0 batch_normalization_27[0][0] __________________________________________________________________________________________________ conv2d_28 (Conv2D) (None, 16, 16, 128) 147584 activation_18[0][0] __________________________________________________________________________________________________ cropping2d_9 (Cropping2D) (None, 16, 16, 128) 0 add_8[0][0] __________________________________________________________________________________________________ batch_normalization_28 (BatchNo (None, 16, 16, 128) 512 conv2d_28[0][0] __________________________________________________________________________________________________ add_9 (Add) (None, 16, 16, 128) 0 cropping2d_9[0][0] batch_normalization_28[0][0] __________________________________________________________________________________________________ un_pooling2d_3 (UnPooling2D) (None, 32, 32, 128) 0 add_9[0][0] __________________________________________________________________________________________________ reflection_padding2d_5 (Reflect (None, 36, 36, 128) 0 un_pooling2d_3[0][0] __________________________________________________________________________________________________ conv2d_29 (Conv2D) (None, 34, 34, 64) 73792 reflection_padding2d_5[0][0] __________________________________________________________________________________________________ batch_normalization_29 (BatchNo (None, 34, 34, 64) 256 conv2d_29[0][0] __________________________________________________________________________________________________ activation_19 (Activation) (None, 34, 34, 64) 0 batch_normalization_29[0][0] __________________________________________________________________________________________________ un_pooling2d_4 (UnPooling2D) (None, 68, 68, 64) 0 activation_19[0][0] __________________________________________________________________________________________________ reflection_padding2d_6 (Reflect (None, 72, 72, 64) 0 un_pooling2d_4[0][0] __________________________________________________________________________________________________ conv2d_30 (Conv2D) (None, 70, 70, 32) 18464 reflection_padding2d_6[0][0] __________________________________________________________________________________________________ batch_normalization_30 (BatchNo (None, 70, 70, 32) 128 conv2d_30[0][0] __________________________________________________________________________________________________ activation_20 (Activation) (None, 70, 70, 32) 0 batch_normalization_30[0][0] __________________________________________________________________________________________________ un_pooling2d_5 (UnPooling2D) (None, 70, 70, 32) 0 activation_20[0][0] __________________________________________________________________________________________________ reflection_padding2d_7 (Reflect (None, 72, 72, 32) 0 un_pooling2d_5[0][0] __________________________________________________________________________________________________ conv2d_31 (Conv2D) (None, 64, 64, 3) 7779 reflection_padding2d_7[0][0] __________________________________________________________________________________________________ batch_normalization_31 (BatchNo (None, 64, 64, 3) 12 conv2d_31[0][0] __________________________________________________________________________________________________ activation_21 (Activation) (None, 64, 64, 3) 0 batch_normalization_31[0][0] __________________________________________________________________________________________________ transform_output (Denormalize) (None, 64, 64, 3) 0 activation_21[0][0] __________________________________________________________________________________________________ concatenate (Concatenate) (None, 64, 64, 3) 0 transform_output[0][0] input_3[0][0] __________________________________________________________________________________________________ vgg_normalize (VGGNormalize) (None, 64, 64, 3) 0 concatenate[0][0] __________________________________________________________________________________________________ block1_conv1 (Conv2D) (None, 64, 64, 64) 1792 vgg_normalize[0][0] __________________________________________________________________________________________________ block1_conv2 (Conv2D) (None, 64, 64, 64) 36928 block1_conv1[0][0] __________________________________________________________________________________________________ block1_pool (MaxPooling2D) (None, 32, 32, 64) 0 block1_conv2[0][0] __________________________________________________________________________________________________ block2_conv1 (Conv2D) (None, 32, 32, 128) 73856 block1_pool[0][0] __________________________________________________________________________________________________ block2_conv2 (Conv2D) (None, 32, 32, 128) 147584 block2_conv1[0][0] __________________________________________________________________________________________________ block2_pool (MaxPooling2D) (None, 16, 16, 128) 0 block2_conv2[0][0] __________________________________________________________________________________________________ block3_conv1 (Conv2D) (None, 16, 16, 256) 295168 block2_pool[0][0] __________________________________________________________________________________________________ block3_conv2 (Conv2D) (None, 16, 16, 256) 590080 block3_conv1[0][0] __________________________________________________________________________________________________ block3_conv3 (Conv2D) (None, 16, 16, 256) 590080 block3_conv2[0][0] __________________________________________________________________________________________________ block3_pool (MaxPooling2D) (None, 8, 8, 256) 0 block3_conv3[0][0] __________________________________________________________________________________________________ block4_conv1 (Conv2D) (None, 8, 8, 512) 1180160 block3_pool[0][0] __________________________________________________________________________________________________ block4_conv2 (Conv2D) (None, 8, 8, 512) 2359808 block4_conv1[0][0] __________________________________________________________________________________________________ block4_conv3 (Conv2D) (None, 8, 8, 512) 2359808 block4_conv2[0][0] __________________________________________________________________________________________________ block4_pool (MaxPooling2D) (None, 4, 4, 512) 0 block4_conv3[0][0] __________________________________________________________________________________________________ block5_conv1 (Conv2D) (None, 4, 4, 512) 2359808 block4_pool[0][0] __________________________________________________________________________________________________ block5_conv2 (Conv2D) (None, 4, 4, 512) 2359808 block5_conv1[0][0] __________________________________________________________________________________________________ block5_conv3 (Conv2D) (None, 4, 4, 512) 2359808 block5_conv2[0][0] __________________________________________________________________________________________________ block5_pool (MaxPooling2D) (None, 2, 2, 512) 0 block5_conv3[0][0] ================================================================================================== Total params: 16,544,591 Trainable params: 16,541,385 Non-trainable params: 3,206 __________________________________________________________________________________________________
Использование предварительно обученной модели для стилизации изображения
Я буду использовать предварительно обученную модель PyTorch для изображения стиля Udnie из репозитория GitHub, упомянутого выше. Ниже приводится реализация того же самого.
Сначала загрузите обученную модель и входное изображение с помощью OpenCV. Затем передайте изображение в модель, создав большой двоичный объект с помощью функции cv2.dnn.blobFromImage ().
Наконец, обработайте выходное изображение и отобразите входное изображение вместе с окончательным комбинированным изображением.
Результаты
Выходное изображение, сгенерированное моделью и отображаемое последними несколькими строками кода: -
Это то же изображение, что я показал в начале. Как я и обещал ранее, к концу статьи вы получите хорошие результаты. Вот! Теперь вы можете создавать отличные стилизованные изображения. Репозиторий GitHub для Fast Neural Style Transfer предоставляет около десяти предварительно обученных моделей для изображений различных стилей. Вы также можете попробовать их и сравнить результаты. В качестве альтернативы вы можете обучить модель для изображения другого стиля, для которого не существует предварительно обученной модели, используя обучающий код репозитория.
Вывод
В этой статье представлены два разных метода передачи нейронного стиля, а именно подход на основе оптимизации и быстрая передача нейронного стиля. Процесс на основе оптимизации медленный, поскольку модель необходимо обучать каждый раз при изменении входного изображения. С другой стороны, модель передачи Fast Neural Style уже обучена для изображения определенного стиля с несколькими входными изображениями. Таким образом, его можно использовать для обобщения результатов без переобучения модели. Однако вам необходимо переобучить модель, если вы изменили изображение стиля, а предварительно обученная модель недоступна.
Большое спасибо за чтение этой статьи. Если вам понравилась статья, поделитесь ею с друзьями, которым может быть интересна эта тема. И дайте нам знать, если у вас есть предложения.
Ссылка на блокнот: Здесь
Предоставлено: ЮВНИШ МАЛЬХОТРА