AI繪畫第二彈——圖像風格遷移

這篇文章是個人《AI繪畫系列》的第三篇,點擊此處能夠查看整個系列的文章
本篇文章的源碼能夠在微信公衆號「01二進制」後臺回覆「圖像風格遷移」得到python

簡介

所謂圖像風格遷移,是指將一幅內容圖A的內容,和一幅風格圖B的風格融合在一塊兒,從而生成一張具備A圖風格和B圖內容的圖片C的技術。目前這個技術已經獲得了比較普遍的應用,這裏安利一個app——"大畫家",這個軟件能夠將用戶的照片自動變換爲具備藝術家的風格的圖片。算法

準備

其實剛開始寫這篇文章的時候我是準備詳細介紹下原理的,可是後來發現公式實在是太多了,就算寫了估計也沒什麼人看,並且這篇文章原本定位的用戶就是隻須要實現功能的新人。此外,有關風格遷移的原理解析的博客實在是太多了,因此這裏我就把重點放在如何使用 TensorFlow 實現一個快速風格遷移的應用上,原理的解析就一帶而過了。若是隻想實現這個效果的能夠跳到**"運行"**一節。微信

第一步咱們須要提早安裝好 TensorFlow,若是有GPU的小夥伴能夠參考個人這篇文章搭建一個GPU環境:《AI 繪畫第一彈——用GPU爲你的訓練過程加速》,若是打算直接用 CPU 運行的話,執行下面一行話就能夠了網絡

pip install numpy tensorflow scipy 
複製代碼

原理

本篇文章是基於A Neural Algorithm of Artistic Style一文提出的方法實現的,若是嫌看英文論文太麻煩的也能夠查看我對這篇文章的翻譯【譯】一種有關藝術風格遷移的神經網絡算法app

爲了將風格圖的風格和內容圖的內容進行融合,所生成的圖片,在內容上應當儘量接近內容圖,在風格上應當儘量接近風格圖,所以須要定義內容損失函數風格損失函數,通過加權後做爲總的損失函數。函數

預訓練模型

CNN具備抽象和理解圖像的能力,所以能夠考慮將各個卷積層的輸出做爲圖像的內容,這裏咱們採用了利用VGG19訓練好的模型來進行遷移學習,通常認爲,卷積神經網絡的訓練是對數據集特徵的一步步抽取的過程,從簡單的特徵,到複雜的特徵。訓練好的模型學習到的是對圖像特徵的抽取方法,而該模型就是在imagenet數據集上預訓練的模型,因此理論上來講,也能夠直接用於抽取其餘圖像的特徵,雖然效果可能沒有在原有數據集上訓練出的模型好,可是可以節省大量的訓練時間,在特定狀況下很是有用。post

加載預訓練模型

def vggnet(self):
    # 讀取預訓練的vgg模型
    vgg = scipy.io.loadmat(settings.VGG_MODEL_PATH)
    vgg_layers = vgg['layers'][0]
    net = {}
    # 使用預訓練的模型參數構建vgg網絡的卷積層和池化層
    # 全鏈接層不須要
    # 注意,除了input以外,這裏參數都爲constant,即常量
    # 和平時不一樣,咱們並不訓練vgg的參數,它們保持不變
    # 須要進行訓練的是input,它便是咱們最終生成的圖像
    net['input'] = tf.Variable(np.zeros([1, settings.IMAGE_HEIGHT, settings.IMAGE_WIDTH, 3]), dtype=tf.float32)
    # 參數對應的層數能夠參考vgg模型圖
    net['conv1_1'] = self.conv_relu(net['input'], self.get_wb(vgg_layers, 0))
    net['conv1_2'] = self.conv_relu(net['conv1_1'], self.get_wb(vgg_layers, 2))
    net['pool1'] = self.pool(net['conv1_2'])
    net['conv2_1'] = self.conv_relu(net['pool1'], self.get_wb(vgg_layers, 5))
    net['conv2_2'] = self.conv_relu(net['conv2_1'], self.get_wb(vgg_layers, 7))
    net['pool2'] = self.pool(net['conv2_2'])
    net['conv3_1'] = self.conv_relu(net['pool2'], self.get_wb(vgg_layers, 10))
    net['conv3_2'] = self.conv_relu(net['conv3_1'], self.get_wb(vgg_layers, 12))
    net['conv3_3'] = self.conv_relu(net['conv3_2'], self.get_wb(vgg_layers, 14))
    net['conv3_4'] = self.conv_relu(net['conv3_3'], self.get_wb(vgg_layers, 16))
    net['pool3'] = self.pool(net['conv3_4'])
    net['conv4_1'] = self.conv_relu(net['pool3'], self.get_wb(vgg_layers, 19))
    net['conv4_2'] = self.conv_relu(net['conv4_1'], self.get_wb(vgg_layers, 21))
    net['conv4_3'] = self.conv_relu(net['conv4_2'], self.get_wb(vgg_layers, 23))
    net['conv4_4'] = self.conv_relu(net['conv4_3'], self.get_wb(vgg_layers, 25))
    net['pool4'] = self.pool(net['conv4_4'])
    net['conv5_1'] = self.conv_relu(net['pool4'], self.get_wb(vgg_layers, 28))
    net['conv5_2'] = self.conv_relu(net['conv5_1'], self.get_wb(vgg_layers, 30))
    net['conv5_3'] = self.conv_relu(net['conv5_2'], self.get_wb(vgg_layers, 32))
    net['conv5_4'] = self.conv_relu(net['conv5_3'], self.get_wb(vgg_layers, 34))
    net['pool5'] = self.pool(net['conv5_4'])
    return net
複製代碼

訓練思路

咱們使用VGG中的一些層的輸出來表示圖片的內容特徵和風格特徵。好比,我使用[‘conv4_2’,’conv5_2’]表示內容特徵,使用[‘conv1_1’,’conv2_1’,’conv3_1’,’conv4_1’]表示風格特徵。在settings.py中進行配置。學習

# 定義計算內容損失的vgg層名稱及對應權重的列表
CONTENT_LOSS_LAYERS = [('conv4_2', 0.5),('conv5_2',0.5)]
# 定義計算風格損失的vgg層名稱及對應權重的列表
STYLE_LOSS_LAYERS = [('conv1_1', 0.2), ('conv2_1', 0.2), ('conv3_1', 0.2), ('conv4_1', 0.2), ('conv5_1', 0.2)]
複製代碼

內容損失函數

image-20190429151634583

其中,X是噪聲圖片的特徵矩陣,P是內容圖片的特徵矩陣。M是P的長*寬,N是信道數。最終的內容損失爲,每一層的內容損失加權和,再對層數取平均。spa

我知道不少人一看到數學公式就會頭疼,簡單理解就是這個公式可讓模型在訓練過程當中不斷的抽取圖片的內容。.net

風格損失函數

計算風格損失。咱們使用風格圖像在指定層上的特徵矩陣的GRAM矩陣來衡量其風格,風格損失能夠定義爲風格圖像和噪音圖像特徵矩陣的格萊姆矩陣的差值的L2範數。

對於每一層的風格損失函數,咱們有:

其中M是特徵矩陣的長*寬,N是特徵矩陣的信道數。G爲噪音圖像特徵的Gram矩陣,A爲風格圖片特徵的GRAM矩陣。最終的風格損失爲,每一層的風格損失加權和,再對層數取平均。

一樣的,看不懂公式不要緊,你就把它理解爲這個公式能夠在訓練過程當中獲取圖片的風格。

計算總損失函數並訓練模型

最後咱們只須要將內容損失函數和風格損失函數帶入剛開始的公式中便可,要作的就是控制一些風格的權重和內容的權重:

# 內容損失權重
ALPHA = 1
# 風格損失權重
BETA = 500
複製代碼

ALPHA越大,則最後生成的圖片內容信息越大;同理,BETA越大,則最後生成的圖片風格化更嚴重。

當訓練開始時,咱們根據內容圖片和噪聲,生成一張噪聲圖片。並將噪聲圖片餵給網絡,計算loss,再根據loss調整噪聲圖片。將調整後的圖片餵給網絡,從新計算loss,再調整,再計算…直到達到指定迭代次數,此時,噪聲圖片已兼具內容圖片的內容和風格圖片的風格,進行保存便可。

運行

若是對上述原理不感興趣的,想直接運行代碼的能夠去微信公衆號「01二進制」後臺回覆「圖像風格遷移」得到源碼。接下來咱們來講說如何使用這個代碼跑出本身的繪畫。

首先看下項目結構:

images下有兩張圖片,分別是內容圖和風格圖,output下是訓練過程當中產生的文件,.mat文件就是預訓練模型,models.py是咱們實現的用於讀取預訓練模型的文件,settings.py是配置文件,train.py是最終的訓練文件。

想要運行該項目,咱們只須要執行python train.py便可,想更改風格和內容的的話只要在images文件中更換原先的圖片便可。固然你也能夠在settings.py中修改路徑:

# 內容圖片路徑
CONTENT_IMAGE = 'images/content.jpg'
# 風格圖片路徑
STYLE_IMAGE = 'images/style.jpg'
# 輸出圖片路徑
OUTPUT_IMAGE = 'output/output'
# 預訓練的vgg模型路徑
VGG_MODEL_PATH = 'imagenet-vgg-verydeep-19.mat'
複製代碼

咱們來看看訓練後的圖片:

最後

雖然經過上述代碼咱們能夠實現圖像的風格遷移,可是他有一個最大的缺點,就是沒法保存訓練好的模型,每次轉換風格都要從新跑一遍,若是使用CPU跑1000輪的話大約是在30分鐘左右,所以推薦你們使用GPU進行訓練。

可是即便使用上了GPU,訓練時間也沒法知足商業使用的,那有沒有什麼辦法能夠保存訓練好的風格模型,而後直接快速生成目標圖片呢?固然是有的,斯坦福的李飛飛發表過一篇《Perceptual Losses for Real-Time Style Transfer and Super-Resolution》,經過使用perceptual loss來替代per-pixels loss使用pre-trained的vgg model來簡化原先的loss計算,增長一個transform Network,直接生成Content image的style。這裏就再也不多說了,感興趣的能夠參考我下面給出的兩個連接:

  1. 《深度有趣 | 30 快速圖像風格遷移》
  2. 《風格遷移背後原理及tensorflow實現》

以上就是本篇文章的所有內容,我的作下來感受仍是挺有意思的,因我的能力有限,文中若有紕漏錯誤之處,還請各位大佬指正,萬分感謝!

參考

  1. A Neural Algorithm of Artistic Style
  2. 深度學習實戰(一)快速理解實現風格遷移
  3. 深度有趣 | 04 圖像風格遷移
  4. 學習筆記:圖像風格遷移

歡迎關注個人微信公衆號:「01二進制」,關注後便可得到博主認真收集的計算機資料

相關文章
相關標籤/搜索