在藝術繪畫的創做過程當中,人們經過將一張圖片的內容和風格構成複雜的相互做用來產生獨特的視覺體驗。然而,所謂的藝術風格是一種抽象的難以定義的概念。所以,如何將一個圖像的風格轉換成另外一個圖像的風格更是一個複雜抽象的問題。尤爲是對於機器程序而言,解決一個定義模糊不清的問題是幾乎不可行的。
在神經網絡以前,圖像風格遷移的程序採用的思路是:分析一種風格的圖像,爲這種風格創建一個數學統計模型;再改變要作遷移的圖像使它的風格符合創建的模型。該種方法能夠取得不一樣的效果,可是有一個較大的缺陷:一個模型只可以實現一種圖像風格的遷移。所以,基於傳統方法的風格遷移的模型應用十分有限。
隨着神經網絡的發展,機器在某些視覺感知的關鍵領域,好比物體和人臉識別等有着接近於人類甚至超越人類的的表現。這裏咱們要介紹一種基於深度神經網絡的機器學習模型——卷積神經網絡,它能夠分離並結合任意圖片的風格和內容,生成具備高感知品質的藝術圖片。本文介紹一篇在2015年由 Gatys 等人發表的一篇文章_ A Neural Algorithm of Artistic Style_,該文章介紹了一種利用卷積神經網絡進行圖像風格遷移的算法。相比於傳統的風格遷移的方法,該方法具備更好的普適性。
php
處理圖像任務最有效的一種深度神經網絡就是卷積神經網絡。卷積神經網絡由多個網絡層組成的前饋神經網絡,每一個網絡層包含了許多用於處理視覺信息的計算單元(神經元)。每一層的計算單元能夠被理解爲一個圖片過濾器的集合,每一層均可以提取圖片的不一樣的特定特徵。咱們把給定層的輸出稱爲特徵圖譜(Feature Map)——輸入圖像的不一樣過濾版本。
當卷積神經網絡用於物體識別時,隨着網絡的層次愈來愈深,網絡層產生的物體特徵信息愈來愈清晰。這意味着,沿着網絡的層級結構,每個網絡層的輸出愈來愈關注於輸入圖片的實際內容而不是它具體的像素值。經過重構每一個網絡層的特徵圖譜,咱們我能夠可視化每一層所表達的關於輸入圖片的信息。從中能夠看出,位於更高層的網絡層可以根據物體及其在輸入圖像中的排列來捕獲輸入圖像的高級內容而不包含具體的像素值信息。所以,咱們參考網絡模型的高層結構做爲圖片的內容表示。
html
爲了獲取輸入圖片的風格表示,咱們使用一個被用來獲取紋理特徵的特徵空間。該特徵空間包含了特徵圖譜空間範圍內不一樣濾波器響應之間的相關性。經過多個層的特徵相關性,咱們得到輸入圖像的靜態多尺度表示,它可以捕獲圖片的紋理信息卻不包含全局排列。
前端
本文的一個關鍵點是圖片的內容表示和風格表示在卷積神經網絡中是可分離的。也就是說,咱們能夠獨立地操縱這兩種表示來產生新的有感知意義上的圖片。爲了證實這一觀點,該論文展現了一些由不一樣內容和風格的圖片混合生成的合成圖片,如圖 2 所示。
這些圖片是經過尋找一個同時匹配照片內容和對應的藝術風格的圖片的方法而生成的。這些合成圖片在保留原始照片的全局佈置的同時,繼承了各類藝術圖片的不一樣藝術風格。風格表示是一個多層次的表達,包括了神經網絡結構的多個層次。當風格表示只包含了少許的低層結構,風格的就變得更加局部化,產生不一樣的視覺效果。當風格表示由網絡的高層結構表示時,圖像的結構會在更大的範圍內和這種風格匹配(圖 2 最後一行),產生更加流暢持續的視覺體驗。
python
實際上,圖片的內容和風格是不可以被徹底分離的。當咱們合成圖片時,咱們一般找不出一張可以匹配某個圖片內容和另外一種圖片風格的圖片。在咱們合成圖片的過程當中,咱們須要最小化的損失函數包含內容和風格,但它們是分開的。所以,咱們須要平滑地調整內容和風格的權重比例(圖 3 的行座標)。當損失函數分配在內容和風格的權重不一樣時,合成產生的圖片效果也徹底不同。咱們須要適當地調整內容表示和風格表示的權重比來產生具備視覺感染力的圖片。是否可以找到合適的權重比是可否產生使人滿意的圖片的關鍵因素。
git
該論文中的實驗結果,是以 VGG 網絡爲基礎產生的。該實驗使用的是由 16 個卷積層和 5 個池化層(VGG 19)組成的特徵空間。因爲該實驗不要進行分類,咱們不須要使用全鏈接層。該模型是公開可用的,咱們能夠在 Caffe 框架和 Keras 框架找到。該文做者實驗發現使用平均池化比使用最大池化更容易獲得使人滿意的實驗結果。github
在這裏咱們參考後續的文章,使用 VGG 16 網絡模型進行實驗。該模型可以在不丟失圖片精度的條件下,儘量地加快訓練速度。同時,爲了保留原始圖片的結構細節(且讓合成圖片符合大多數人的審美),在這裏咱們使用 'block2_conv2' 的輸出做爲圖片的內容表示(原文采用的是'block4_conv2' )。而風格表示方面,咱們依舊採用論文中所述的紋理特徵做爲風格表示。
算法
在卷積神經網絡(CNN)中,通常認爲較低層的描述了圖像的具體視覺特徵(即紋理、顏色等),較高層的特徵是較爲抽象的圖像內容描述。當要比較兩幅圖像的內容類似性的時候,咱們比較兩幅圖片在 CNN 網絡中高層特徵的類似性便可。本次實驗中,咱們使用內容圖片和合成圖片對應網絡層的特徵圖譜的歐氏距離來表示內容損失。
內容損失的計算公式**:**網絡
參考代碼:app
def content_loss(content, combination):
return backend.sum(backend.square(combination - content))
layer_features = layers['block2_conv2']
content_image_features = layer_features[0, :, :, :]
combination_features = layer_features[2, :, :, :]
loss += content_weight * content_loss(content_image_features,
combination_features)
複製代碼
要比較兩張圖片的風格類似性,咱們須要比較它們在 CNN 網絡中較低層特徵的類似性。與內容損失不一樣的是,咱們不能僅僅使用歐式距離來定義風格損失。CNN 的底層特徵雖然在必定程度上包含了圖像的風格特色,可是因爲特徵圖譜的空間信息過於明顯,直接計算歐氏距離會產生較大的偏差。所以,咱們須要在保留低層的視覺特徵的同時消除空間信息,Gatys 提出了一個很是神奇的矩陣——Gram 矩陣。
Gram 矩陣:框架
# 定義 Gram 矩陣
def gram_matrix(x):
features = backend.batch_flatten(backend.permute_dimensions(x, (2, 0, 1)))
gram = backend.dot(features, backend.transpose(features))
return gram
# 計算總的風格損失
def style_loss(style, combination):
S = gram_matrix(style)
C = gram_matrix(combination)
channels = 3
size = height * width
return backend.sum(backend.square(S - C)) / (4. * (channels ** 2) * (size ** 2))
feature_layers = ['block1_conv2', 'block2_conv2',
'block3_conv3', 'block4_conv3',
'block5_conv3']
for layer_name in feature_layers:
layer_features = layers[layer_name]
style_features = layer_features[1, :, :, :]
combination_features = layer_features[2, :, :, :]
sl = style_loss(style_features, combination_features)
loss += (style_weight / len(feature_layers)) * sl
複製代碼
爲了獲得使人滿意的合成圖片,咱們須要最小化上面定義的內容損失和風格損失。這裏咱們定義了一個總損失函數,分別用
和 表示提供內容的照片和風格的藝術做品, 表示合成圖片。def total_variation_loss(x):
a = backend.square(x[:, :height-1, :width-1, :] - x[:, 1:, :width-1, :])
b = backend.square(x[:, :height-1, :width-1, :] - x[:, :height-1, 1:, :])
return backend.sum(backend.pow(a + b, 1.25))
loss += total_variation_weight * total_variation_loss(combination_image)
複製代碼
實驗中,咱們使用 'conv2_2' 層來計算內容損失而不是論文中用到的 'conv4_2';當咱們在計算圖片的風格損失時,'conv1_1'、'conv2_1'、'conv3_1'、'conv4_1'、'conv5_1' 層的權重爲
,其他網絡層的權重爲 0。總損失中的權重比 須要咱們本身調整,來合成令咱們滿意的圖片。本實驗採用的內容損失權重和風格損失權重爲 0.025 和 5.0,可以獲得一個較爲使人滿意的結果,如圖 4 所示。在本實驗中,咱們使用 L-BFGS 算法來優化總損失函數。因爲咱們使用的是梯度降低算法,咱們引入一個Evaluator 類——經過兩個獨立的函數 loss 和 grads,來計算損失和梯度。
參考代碼:
# 定義梯度
grads = backend.gradients(loss, combination_image)
# 定義類
outputs = [loss],outputs += grads
f_outputs = backend.function([combination_image], outputs)
def eval_loss_and_grads(x):
x = x.reshape((1, height, width, 3))
outs = f_outputs([x]),loss_value = outs[0]
grad_values = outs[1].flatten().astype('float64')
return loss_value, grad_values
class Evaluator(object):
def __init__(self):
self.loss_value = None,self.grads_values = None
def loss(self, x):
assert self.loss_value is None
loss_value, grad_values = eval_loss_and_grads(x)
self.loss_value = loss_value,self.grad_values = grad_values
return self.loss_value
def grads(self, x):
assert self.loss_value is not None
grad_values = np.copy(self.grad_values)
self.loss_value = None,self.grad_values = None
return grad_values
evaluator = Evaluator()
複製代碼
對於合成圖片,咱們將其初始化爲一個隨機有效的像素的集合。最後經過 L-BFGS 算法來最小化損失函數。從實驗結果能夠看出,當迭代進行至 10 次後,損失就再也不顯著減小。
參考代碼:
x = np.random.uniform(0, 255, (1, height, width, 3)) - 128.
iterations = 10
for i in range(iterations):
print('Start of iteration', i)
start_time = time.time()
x, min_val, info = fmin_l_bfgs_b(evaluator.loss, x.flatten(),
fprime=evaluator.grads, maxfun=20)
print('Current loss value:', min_val)
end_time = time.time()
print('Iteration %d completed in %ds' % (i, end_time - start_time))
複製代碼
最終咱們合成的圖片效果展現以下:
本論文首次提出了運用神經網絡模型來實現風格遷移:使用卷積神經網絡將一張圖片中的內容和風格進行了分離和提取;而且定義瞭如何來計算圖片內容類似性和風格類似性,經過最小化內容損失和風格損失來獲得使人滿意的結果。
相比傳統的風格遷移模型,利用卷積神經網絡來提取圖片的內容和風格具備重大的意義 ,它使得模型具備更加普遍的通用性,而不須要爲每一種風格的圖片創建一個數學模型。可是該方法也有一些不足之處:內容損失使用特徵圖譜的歐氏距離來表示的效果並非十分的理想,好比一個張圖片通過一小段平移以後在視覺效果上與原圖幾乎沒有差異,而此時使用像素點間的差值來計算損失會產生較大的偏差;同時該模型優化的是合成圖片
Mo(網址:momodel.cn)是一個支持 Python 的人工智能在線建模平臺,能幫助你快速開發、訓練並部署模型。
Mo 人工智能俱樂部 是由網站的研發與產品設計團隊發起、致力於下降人工智能開發與使用門檻的俱樂部。團隊具有大數據處理分析、可視化與數據建模經驗,已承擔多領域智能項目,具有從底層到前端的全線設計開發能力。主要研究方向爲大數據管理分析與人工智能技術,並以此來促進數據驅動的科學研究。
目前俱樂部每週六在杭州舉辦以機器學習爲主題的線下技術沙龍活動,不按期進行論文分享與學術交流。但願能匯聚來自各行各業對人工智能感興趣的朋友,不斷交流共同成長,推進人工智能民主化、應用普及化。