天天看點

pytorch自帶網絡_Pytorch實作風格遷移

這次上手一個比較有意思的項目:圖像的風格遷移(論文的連接配接在底部,以及論文作者的實作)

風格遷移,簡單的說就是提取一個圖檔的風格(該圖檔稱為風格圖像),如下面這張圖檔:

pytorch自帶網絡_Pytorch實作風格遷移

風格圖檔

然後在另外一個圖像(稱為内容圖像)為主要内容的基礎上:

pytorch自帶網絡_Pytorch實作風格遷移

内容圖檔

得到一個基于内容圖像并融合風格圖像的生成圖像:

pytorch自帶網絡_Pytorch實作風格遷移

生成圖檔

一般的神經網絡都基本流程都是:

  1. 正向傳播
  2. 計算損失
  3. 反向傳播
  4. 更新參數

随着訓練的進行,不斷改變的是網絡的參數,輸入資料(即訓練樣本)是不變的;而風格遷移的實作中則相反,網絡是已經訓練好的,在訓練中,

網絡的參數不會改變,改變的是輸入資料

先介紹一下大緻流程:

  • 定好要生成的圖檔的像素規模,比如256x256
  • 找好相同大小的内容圖像和風格圖像
  • 找一個已經訓練好的網絡(論文中使用的是VGG19,我的小筆記本改用VGG16)
  • 随機建立一個256x256x3的數組(RGG三通道),取值範圍在[0-255],作為生成圖像
  • 将生成圖像( G )、内容圖像( C )、風格圖像( S )丢到VGG16中,計算風格損失和内容損失
pytorch自帶網絡_Pytorch實作風格遷移

上圖VGG16中一個色塊表示

一個卷積層

一個ReLU層

,不同色塊之間有一個池化層(論文中提到使用

平均池化層

替代

最大池化層

會得到“更有吸引力”的結果),後面的全連接配接層沒有畫出來,因為也不用它。一般為了産生更好的效果,會計算多個層的風格損失

  • 反向傳播(隻傳播梯度,不更新參數),得到損失關于生成圖像的梯度
pytorch自帶網絡_Pytorch實作風格遷移
  • 使用優化算法(論文裡是LBFGS)更新生成圖像的資料,然後不斷重複最後這三步
pytorch自帶網絡_Pytorch實作風格遷移

如果把輸入當作一個層(Layer G),該層的輸出就是它自身的參數,再将VGG16整個看作是一個損失函數,這就變成了一個最簡單的梯度下降的過程了:

pytorch自帶網絡_Pytorch實作風格遷移

如前面所讨論的,整個損失分成兩部分,内容損失和風格損失:

pytorch自帶網絡_Pytorch實作風格遷移
pytorch自帶網絡_Pytorch實作風格遷移

是兩個超參數,分别是這兩個損失的權重,需要自己設定,下面來分别讨論這兩個損失的具體計算

内容損失(content loss)

關于VGG16的結構就不贅述了,不太記得的可以百度,内容損失的計算方法方式是:

  1. 在VGG16中標明一個層,例如上面的圖中第7個ReLU層
  2. 計算得到該層關于内容圖像輸出
    pytorch自帶網絡_Pytorch實作風格遷移
    和生成圖像的輸出
    pytorch自帶網絡_Pytorch實作風格遷移
  3. 基于上述兩個輸出值計算損失

因為是在卷積網絡中,ReLU的輸出是一個四維張量,是以

pytorch自帶網絡_Pytorch實作風格遷移

,這四個次元分别是樣本批數,特征值的高度、寬度和通道數。因為确定内容圖像和風格圖像之後,一次隻優化一個生成圖檔,是以

pytorch自帶網絡_Pytorch實作風格遷移

為1,在後面的讨論中,都會把這一次元省略掉

然後,論文給出計算兩個圖檔的内容差異的公式為:

pytorch自帶網絡_Pytorch實作風格遷移

這個公式計算梯度也很簡單,就不寫過程了,直接貼出來:

pytorch自帶網絡_Pytorch實作風格遷移

不過論文作者自己的

實作

中使用的是均方誤差(MSE)損失函數計算的内容損失,公式為:

pytorch自帶網絡_Pytorch實作風格遷移

這樣的話就比論文給出的公式數量上低了好幾個量級,不過這都沒什麼關系,因為還有一個

pytorch自帶網絡_Pytorch實作風格遷移

參數可以調節

風格損失(style loss)

風格損失的計算沒有内容損失這麼直接,稍微委婉一點;雖然可能有多個層計算風格損失,但是對于任意一層的處理步驟是一樣的:

  1. 計算得到該層關于風格圖像的輸出
    pytorch自帶網絡_Pytorch實作風格遷移
    和生成圖像的輸出
    pytorch自帶網絡_Pytorch實作風格遷移
  2. 計算兩個輸出值的格拉姆矩陣
  3. 對兩個格拉姆矩陣計算均方誤差損失

其中,格拉姆矩陣計算公式為:

pytorch自帶網絡_Pytorch實作風格遷移

上式是關于生成圖像輸出的格拉姆矩陣的計算,風格圖像也是一樣的(而且可以提前計算),雖然這個式子看起來有點繞,但是用einsum函數可以直接搞定

M_g 
           

這樣,将兩個圖像的風格使用格拉姆矩陣表示出來後,就可以計算它們之間的風格差異了:

pytorch自帶網絡_Pytorch實作風格遷移

可以很容易得到風格損失關于格拉姆矩陣的梯度:

pytorch自帶網絡_Pytorch實作風格遷移

為了得到風格損失關于生成圖像輸出的梯度

pytorch自帶網絡_Pytorch實作風格遷移

,還需要知道格拉姆矩陣中各元素關于輸出的梯度,回顧格拉姆矩陣的計算公式:

pytorch自帶網絡_Pytorch實作風格遷移

可以知道

pytorch自帶網絡_Pytorch實作風格遷移

的任意一個元素

pytorch自帶網絡_Pytorch實作風格遷移

與它的格拉姆矩陣的第

pytorch自帶網絡_Pytorch實作風格遷移

行和第

pytorch自帶網絡_Pytorch實作風格遷移

列上的元素都有關:

pytorch自帶網絡_Pytorch實作風格遷移
pytorch自帶網絡_Pytorch實作風格遷移

上面的兩個式子

pytorch自帶網絡_Pytorch實作風格遷移

代表除

pytorch自帶網絡_Pytorch實作風格遷移

以外的任何值,另外:

pytorch自帶網絡_Pytorch實作風格遷移

是以,根據使用全導數求生成圖像輸出的梯度為:

pytorch自帶網絡_Pytorch實作風格遷移
pytorch自帶網絡_Pytorch實作風格遷移
pytorch自帶網絡_Pytorch實作風格遷移

整理一下,最終得到:

pytorch自帶網絡_Pytorch實作風格遷移

上面這個公式與論文裡的形式不一樣,是因為論文裡的特征值隻有兩個次元,他将特征值的寬和高用一個次元表示了,而且通道是放在了第一個次元,是以其實本質是一回事

以上是論文中給的公式,還有一個需要注意的是在作者的自己的代碼裡,他并沒有按照自己的公式來,其計算格拉姆矩陣的代碼變成公式是下面這樣:

pytorch自帶網絡_Pytorch實作風格遷移

然後使用均方誤差計算風格損失:

pytorch自帶網絡_Pytorch實作風格遷移

如果将

pytorch自帶網絡_Pytorch實作風格遷移

提出來的話,和前面的公式也是一樣的。最後,因為風格損失設計多個層的計算,是以總的風格損失為:

pytorch自帶網絡_Pytorch實作風格遷移

以上,就是損失計算的最主要的兩個部分,其他的什麼基本上也沒什麼了

最後的最後,再把作者的Github貼一下,并說一下代碼與論文不同的一些點:

https://github.com/leongatys/PytorchNeuralStyleTransfer

  1. 代碼雖然使用的是Pytorch的實作,但不是直接下載下傳Pytorch的預訓練模型,而是使用https://bethgelab.org/media/uploads/pytorch_models/vgg_conv.pth這個連結的模型(可以使用迅雷下載下傳)
  2. 在我自己的實驗中,使用Pytorch預訓練的模型需要将裡圖像資料除上255是其調整到(0,1)的範圍,作者自帶的這個模型不用
  3. 作者的代碼裡對圖像做了些預處理,先是将圖像調整到(0,1)然後使用均值為[0.40760392, 0.45795686, 0.48501961]和方差為[1,1,1]進行歸一化,然後又乘上255(此時圖像的資料範圍是[-128~128]),我沒有在論文中找到相關說明
  4. 論文中說明生成圖像初始化的方式并不影響最終結果,無論你是完全随機的初始化,還是直接複制内容圖像作為初始化,最後都看不多出來有什麼差別(論文裡有圖檔),然而我找了張顯示卡使用随機初始化疊代了800次後的圖像是這樣的
pytorch自帶網絡_Pytorch實作風格遷移

随機初始化的結果

如果是複制内容圖像,結果就正常了:

pytorch自帶網絡_Pytorch實作風格遷移

參考文獻:

[1] Image Style Transfer Using Convolutional Neural Networks

繼續閱讀