深度學習實踐:使用Tensorflow實現快速風格遷移

一、風格遷移簡介

風格遷移(Style Transfer)是深度學習眾多應用中非常有趣的一種,如圖,我們可以使用這種方法把一張圖片的風格「遷移」到另一張圖片上:

然而,原始的風格遷移(論文地址:arxiv.org/pdf/1508.0657)的速度是非常慢的。在GPU上,生成一張圖片都需要10分鐘左右,而如果只使用CPU而不使用GPU運行程序,甚至需要幾個小時。這個時間還會隨著圖片尺寸的增大而迅速增大。

這其中的原因在於,在原始的風格遷移過程中,把生成圖片的過程當做一個「訓練」的過程。每生成一張圖片,都相當於要訓練一次模型,這中間可能會迭代幾百幾千次。如果你了解過一點機器學習的知識,就會知道,從頭訓練一個模型要比執行一個已經訓練好的模型要費時太多。而這也正是原始的風格遷移速度緩慢的原因。

二、快速風格遷移簡介

那有沒有一種方法,可以不把生成圖片當做一個「訓練」的過程,而當成一個「執行」的過程呢?答案是肯定的。這就這篇快速風格遷移(fast neural style transfer):Perceptual Losses for Real-Time Style Transfer and Super-Resolution

快速風格遷移的網路結構包含兩個部分。一個是「生成網路」(原文中為Transformation Network),一個是「損失網路」(Loss Network)。生成網路接收一個圖片當做輸入,然後輸出也是一張圖片(即風格遷移後的結果)。如下圖,左側是生成網路,右側為損失網路:

訓練階段:首先選定一張風格圖片。訓練的目標是讓生成網路可以有效生成圖片。目標由損失網路定義。

執行階段:給定一張圖片,將其輸入生成網路,輸出這張圖片風格遷移後的結果。

我們可以發現,在模型的「執行」階段我們就可以完成風格圖片的生成。因此生成一張圖片的速度非常塊,在GPU上一般小於1秒,在CPU上運行也只需要幾秒的時間。

三、快速風格遷移的Tensorflow實現

話不多說,直接上我的代碼的Github地址:hzy46/fast-neural-style-tensorflow

還有變換效果如下。

原始圖片:

風格遷移後的圖片:

以上圖片在GPU(Titan Black)下生成約需要0.8s,CPU(i7-6850K)下生成用時約2.9s。

關於快速風格遷移,其實之前在Github上已經有了Tensorflow的兩個實現:

  • junrushao1994/fast-neural-style.tf

  • OlavHN/fast-neural-style

但是第一個項目只提供了幾個訓練好的模型,沒有提供訓練的代碼,也沒有提供具體的網路結構。所以實際用處不大。

而第二個模型做了完整的實現,可以進行模型的訓練,但是訓練出來的效果不是很好,在作者自己的博客中,給出了一個範例,可以看到生成的圖片有很多雜訊點:

我的項目就是在OlavHN/fast-neural-style的基礎上做了很多修改和調整。

四、一些實現細節

1、與Tensorflow Slim結合

在原來的實現中,作者使用了VGG19模型當做損失網路。而在原始的論文中,使用的是VGG16。為了保持一致性,我使用了Tensorflow Slim(地址:tensorflow/models)對損失網路重新進行了包裝。

Slim是Tensorflow的一個擴展庫,提供了很多與圖像分類有關的函數,已經很多已經訓練好的模型(如VGG、Inception系列以及ResNet系列)。

下圖是Slim支持的模型:

使用Slim替換掉原先的網路之後,在損失函數中,我們不僅可以使用VGG16,也可以方便地使用VGG19、ResNet等其他網路結構。具體的實現請參考源碼。

2、改進轉置卷積的兩個Trick

原先我們需要使用網路生成圖像的時候,一般都是採用轉置卷積直接對圖像進行上採樣。

這篇文章指出了轉置卷積的一些問題,認為轉置卷積由於不合理的重合,使得生成的圖片總是有「棋盤狀的雜訊點」,它提出使用先將圖片放大,再做卷積的方式來代替轉置卷積做上採樣,可以提高生成圖片的質量,下圖為兩種方法的對比:

對應的Tensorflow的實現:

def resize_conv2d(x, input_filters, output_filters, kernel, strides, training):n with tf.variable_scope(conv_transpose) as scope:n height = x.get_shape()[1].value if training else tf.shape(x)[1]n width = x.get_shape()[2].value if training else tf.shape(x)[2]nn new_height = height * strides * 2n new_width = width * strides * 2nn x_resized = tf.image.resize_images(x, [new_height, new_width], tf.image.ResizeMethod.NEAREST_NEIGHBOR)nn shape = [kernel, kernel, input_filters, output_filters]n weight = tf.Variable(tf.truncated_normal(shape, stddev=0.1), name=weight)n return conv2d(x_resized, input_filters, output_filters, kernel, strides)n

以上為第一個Trick。

第二個Trick是文章 Instance Normalization: The Missing Ingredient for Fast Stylization 中提到的,用 Instance Normalization來代替通常的Batch Normalization,可以改善風格遷移的質量。

3、注意使用Optimizer和Saver

這是關於Tensorflow實現的一個小細節。

在Tensorflow中,Optimizer和Saver是默認去訓練、保存模型中的所有變數的。但在這個項目中,整個網路分為生成網路和損失網路兩部分。我們的目標是訓練好生成網路,因此只需要去訓練、保存生成網路中的變數。在構造Optimizer和Saver的時候,要注意只傳入生成網路中的變數。

找出需要訓練的變數,傳遞給Optimizer:

variable_to_train = []nfor variable in tf.trainable_variables():n if not(variable.name.startswith(FLAGS.loss_model)):n variable_to_train.append(variable)ntrain_op = tf.train.AdamOptimizer(1e-3).minimize(loss, global_step=global_step, var_list=variable_to_train)n

五、總結

總之是做了一個還算挺有趣的項目。代碼不是特別多,如果只是用訓練好的模型生成圖片的話,使用CPU也可以在幾秒內運行出結果,不需要去搭建GPU環境。建議有興趣的同學可以自己玩一下。(再貼下地址吧:hzy46/fast-neural-style-tensorflow)

關於訓練,其實也有一段比較坎(dan)坷(teng)的調參經歷,下次有時間再分享一下,今天就先寫到這兒。謝謝大家!

推薦閱讀:

2017 ICCV 行人檢索/重識別 接受論文匯總
TensorFlow小試牛刀(2):GAN生成手寫數字
論文推薦:機器閱讀理解,文本摘要,Seq2Seq加速 | 本周值得讀 #34
使用計算機視覺方法做海洋瀕臨物種檢測
SiameseFC:Fully-Convolutional Siamese Networks for Object Tracking

TAG:深度学习DeepLearning | 机器学习 | 计算机视觉 |