深度卷積GAN之圖像生成

前言

在我們之前的文章中,我們學習了如何構造一個簡單的GAN來生成MNIST手寫圖片。對於圖像問題,卷積神經網路相比於簡單地全連接的神經網路更具優勢,因此,我們這一節我們將繼續深入GAN,通過融合卷積神經網路來對我們的GAN進行改進,實現一個深度卷積GAN。如果還沒有親手實踐過GAN的小夥伴可以先去學習一下上一篇專欄:生成對抗網路(GAN)之MNIST數據生成。

專欄中的所有代碼都在我的GitHub中,歡迎star與fork。

本次代碼在NELSONZHAO/zhihu/dcgan,裡面包含了兩個文件:

  • dcgan_mnist:基於MNIST手寫數據集構造深度卷積GAN模型
  • dcgan_cifar:基於CIFAR數據集構造深度卷積GAN模型

本文主要以MNIST為例進行介紹,兩者在本質上沒有差別,只在細微的參數上有所調整。由於窮學生資源有限,沒有對模型增加迭代次數,也沒有構造更深的模型。並且也沒有選取像素很高的圖像,高像素非常消耗計算量。本節只是一個拋磚引玉的作用,讓大家了解DCGAN的結構,如果有資源的小夥伴可以自己去嘗試其他更清晰的圖片以及更深的結構,相信會取得很不錯的結果。

工具

  • Python3
  • TensorFlow 1.0
  • Jupyter notebook

正文

整個正文部分將包括以下部分:

- 數據載入

- 模型輸入

- Generator

- Discriminator

- Loss

- Optimizer

- 訓練模型

- 可視化

數據載入

數據載入部分採用TensorFlow中的input_data介面來進行載入。關於載入細節在前面的文章中已經寫了很多次啦,相信看過我文章的小夥伴對MNIST載入也非常熟悉,這裡不再贅述。

模型輸入

在GAN中,我們的輸入包括兩部分,一個是真實圖片,它將直接輸入給discriminator來獲得一個判別結果;另一個是隨機雜訊,隨機雜訊將作為generator來生成圖片的材料,generator再將生成圖片傳遞給discriminator獲得一個判別結果。

上面的函數定義了輸入圖片與雜訊圖片兩個tensor。

Generator

生成器接收一個雜訊信號,基於該信號生成一個圖片輸入給判別器。在上一篇專欄文章生成對抗網路(GAN)之MNIST數據生成中,我們的生成器是一個全連接層的神經網路,而本節我們將生成器改造為包含卷積結構的網路,使其更加適合處理圖片輸入。整個生成器結構如下:

我們採用了transposed convolution將我們的雜訊圖片轉換為了一個與輸入圖片具有相同shape的生成圖像。我們來看一下具體的實現代碼:

上面的代碼是整個生成器的實現細節,裡面包含了一些trick,我們來一步步地看一下。

首先我們通過一個全連接層將輸入的雜訊圖像轉換成了一個1 x 4*4*512的結構,再將其reshape成一個[batch_size, 4, 4, 512]的形狀,至此我們其實完成了第一步的轉換。接下來我們使用了一個對加速收斂及提高卷積神經網路性能中非常有效的方法——加入BN(batch normalization),它的思想是歸一化當前層輸入,使它們的均值為 0 和方差為 1,類似於我們歸一化網路輸入的方法。它的好處在於可以加速收斂,並且加入BN的卷積神經網路受權重初始化影響非常小,具有非常好的穩定性,對於提升卷積性能有很好的效果。關於batch normalization,我會在後面專欄中進行一個詳細的介紹。

完成BN後,我們使用Leaky ReLU作為激活函數,在上一篇專欄中我們已經提過這個函數,這裡不再贅述。最後加入dropout正則化。剩下的transposed convolution結構層與之類似,只不過在最後一層中,我們不採用BN,直接採用tanh激活函數輸出生成的圖片。

在上面的transposed convolution中,很多小夥伴肯定會對每一層size的變化疑惑,在這裡來講一下在TensorFlow中如何來計算每一層feature map的size。首先,在卷積神經網路中,假如我們使用一個k x k的filter對m x m x d的圖片進行卷積操作,strides為s,在TensorFlow中,當我們設置padding="same"時,卷積以後的每一個feature map的height和width為ceil(frac{float(m)}{float(s)});當設置padding="valid"時,每一個feature map的height和width為ceil(frac{float(m-k+1)}{float(s)})。那麼反過來,如果我們想要進行transposed convolution操作,比如將7 x 7 的形狀變為14 x 14,那麼此時,我們可以設置padding="same",strides=2即可,與filter的size沒有關係;而如果將4 x 4變為7 x 7的話,當設置padding="valid"時,即4 = ceil(frac{7-k+1}{s}),此時s=1,k=4即可實現我們的目標。

上面的代碼中我也標註了每一步shape的變化。

Discriminator

Discriminator接收一個圖片,輸出一個判別結果(概率)。其實Discriminator完全可以看做一個包含卷積神經網路的圖片二分類器。結構如下:

實現代碼如下:

上面代碼其實就是一個簡單的卷積神經網路圖像識別問題,最終返回logits(用來計算loss)與outputs。這裡沒有加入池化層的原因在於圖片本身經過多層卷積以後已經非常小了,並且我們加入了batch normalization加速了訓練,並不需要通過max pooling來進行特徵提取加速訓練。

Loss Function

Loss部分分別計算Generator的loss與Discriminator的loss,和之前一樣,我們加入label smoothing防止過擬合,增強泛化能力。

Optimizer

GAN中實際包含了兩個神經網路,因此對於這兩個神經網路要分開進行優化。代碼如下:

這裡的Optimizer和我們之前不同,由於我們使用了TensorFlow中的batch normalization函數,這個函數中有很多trick要注意。首先我們要知道,batch normalization在訓練階段與非訓練階段的計算方式是有差別的,這也是為什麼我們在使用batch normalization過程中需要指定training這個參數。上面使用tf.control_dependencies是為了保證在訓練階段能夠一直更新 moving averages。具體參考A Gentle Guide to Using Batch Normalization in Tensorflow - Rui Shu。

訓練

到此為止,我們就完成了深度卷積GAN的構造,接著我們可以對我們的GAN來進行訓練,並且定義一些輔助函數來可視化迭代的結果。代碼太長就不放上來了,可以直接去我的GitHub下載。

我這裡只設置了5輪epochs,每隔100個batch列印一次結果,每一行代表同一個epoch下的25張圖:

我們可以看出僅僅經過了少部分的迭代就已經生成非常清晰的手寫數字,並且訓練速度是非常快的。

上面的圖是最後幾次迭代的結果。我們可以回顧一下上一篇的一個簡單的全連接層的GAN,收斂速度明顯不如深度卷積GAN。

總結

到此為止,我們學習了一個深度卷積GAN,並且看到相比於之前簡單的GAN來說,深度卷積GAN的性能更加優秀。當然除了MNST數據集以外,小夥伴兒們還可以嘗試很多其他圖片,比如我們之前用到過的CIFAR數據集,我在這裡也實現了一個CIFAR數據集的圖片生成,我只選取了馬的圖片進行訓練:

剛開始訓練時:

訓練50個epochs:

這裡我只設置了50次迭代,可以看到最後已經生成了非常明顯的馬的圖像,可見深度卷積GAN的優勢。

我的GitHub:NELSONZHAO (Nelson Zhao)

上面包含了我的專欄中所有的代碼實現,歡迎star,歡迎fork。

轉載請聯繫作者獲得授權。

往期回顧:

  • 生成對抗網路(GAN)之MNIST數據生成
  • 利用卷積神經網路處理CIFAR圖像分類
  • 利用卷積自編碼器對圖片進行降噪
  • 在語言翻譯中理解Attention Mechanism
  • 從Encoder到Decoder實現Seq2Seq模型
  • 基於TensorFlow實現Skip-Gram模型
  • 理解 Word2Vec 之 Skip-Gram 模型
  • 《安娜卡列尼娜》文本生成——利用TensorFlow構建LSTM模型

推薦閱讀:

Docker--深度學習環境配置一站式解決方案
一個基於 TensorFlow 的「顏值評分」開源項目:FaceRank

TAG:深度学习DeepLearning | TensorFlow | 机器学习 |