В этой статье представлено руководство по передаче нейронного стиля с использованием глубокого обучения. Нейронный перенос стиля - это техника совмещения изображений в стиле другого изображения. 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 уже обучена для изображения определенного стиля с несколькими входными изображениями. Таким образом, его можно использовать для обобщения результатов без переобучения модели. Однако вам необходимо переобучить модель, если вы изменили изображение стиля, а предварительно обученная модель недоступна.

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

Ссылка на блокнот: Здесь

Предоставлено: ЮВНИШ МАЛЬХОТРА