1. 程式人生 > >[GAN學習系列3]採用深度學習和 TensorFlow 實現圖片修復(中)

[GAN學習系列3]採用深度學習和 TensorFlow 實現圖片修復(中)

上一篇文章--[GAN學習系列3]採用深度學習和 TensorFlow 實現圖片修復(上)中,我們先介紹了對於影象修復的背景,需要利用什麼資訊來對缺失的區域進行修復,以及將影象當做概率分佈取樣的樣本來看待,通過這個思路來開始進行影象的修復。

這篇文章將繼續介紹原文的第二部分,利用對抗生成網路來快速生成假圖片。目錄如下:

  • 第二步:快速生成假的圖片
    • 從未知的概率分佈中學習生成新的樣本
    • [ML-Heavy] 建立 GAN 模型
    • 採用 G(z) 生成假的圖片
    • [ML-Heavy] 訓練 DCGAN
    • 目前的 GAN 和 DCGAN 實現
    • [ML-Heavy] TensorFlow 實現 DCGAN
    • 在你的資料集上執行 DCGAN 模型

同樣的,標題帶有 [ML-Heavy] 的會介紹比較多的細節,可以選擇跳過。


第二步:快速生成假的圖片

從未知的概率分佈中學習生成新的樣本

與其考慮如何計算概率密度函式,現在在統計學中更好的方法是採用一個生成模型來學習如何生成新的、隨機的樣本。過去生成模型一直是很難訓練或者非常難以實現,但最近在這個領域已經有了一些讓人驚訝的進展。Yann LeCun在這篇 Quora 上的問題“最近在深度學習有什麼潛在的突破的領域”中給出了一種訓練生成模型(對抗訓練)方法的介紹,並將其描述為過去十年內機器學習最有趣的想法:

Yann LeCun 在回答中簡單介紹了 GAN 的基本原理,也就是兩個網路相互博弈的過程。

實際上,深度學習還有其他方法來訓練生成模型,比如 Variational Autoencoders(VAEs)。但在本文,主要介紹對抗生成網路(GANs)

[ML-Heavy] 建立 GAN 模型

GANs 這個想法是 Ian Goodfellow 在其帶有里程碑意義的論文“Generative Adversarial Nets” (GANs)發表在 2014 年的 Neural Information Processing Systems (NIPS) 會議上後開始火遍整個深度學習領域的。這個想法就是我們首先定義一個簡單並眾所周知的概率分佈,並表示為p_z

,在本文後面,我們用 p_z 表示在[-1,1)(包含-1,但不包含1)範圍的均勻分佈。用z \thicksim p_z表示從這個分佈中取樣,如果p_z是一個五維的,我們可以利用下面一行的 Python 程式碼來進行取樣得到,這裡用到 numpy這個庫:

z = np.random.uniform(-1, 1, 5)
array([ 0.77356483,  0.95258473, -0.18345086,  0.69224724, -0.34718733])
複製程式碼

現在我們有一個簡單的分佈來進行取樣,接下來可以定義一個函式G(z)來從原始的概率分佈中生成樣本,程式碼例子如下所示:

def G(z):
   ...
   return imageSample

z = np.random.uniform(-1, 1, 5)
imageSample = G(z)
複製程式碼

那麼問題來了,怎麼定義這個G(Z)函式,讓它實現輸入一個向量然後返回一張圖片呢?答案就是採用一個深度神經網路。對於深度神經網路基礎,網路上有很多的介紹,本文就不再重複介紹了。這裡推薦的一些參考有斯坦福大學的 CS231n 課程、Ian Goodfellow 等人編著的《深度學習》書籍形象解釋影象的核心以及論文"A guide to convolution arithmetic for deep learning"

通過深度學習可以有多種方法來實現G(z)函式。在原始的 GAN 論文中提出一種訓練方法並給出初步的實驗結果,這個方法得到了極大的發展和改進。其中一種想法就是在論文“Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks”中提出的,這篇論文的作者是 Alec Radford, Luke Metz, and Soumith Chintala,發表在 2016 年的 International Conference on Learning Representations (ICLR)會議上,這個方法因為提出採用深度卷積神經網路,被稱為 DCGANs,它主要採用小步長卷積( fractionally-strided convolution)方法來上取樣影象

那麼什麼是小步長卷積以及如何實現對圖片的上取樣呢? Vincent Dumoulin and Francesco Visin’s 在論文"A guide to convolution arithmetic for deep learning"以及 Github 專案都給出了這種卷積算術的詳細介紹,Github 地址如下:

github.com/vdumoulin/c…

上述 Github 專案給出了非常直觀的視覺化,如下圖所示,這讓我們可以很直觀瞭解小步長卷積是如何工作的。

首先,你要知道一個正常的卷積操作是一個卷積核劃過輸入區域(下圖中藍色區域)後生成一個輸出區域(下圖的綠色區域)。這裡,輸出區域的尺寸是小於輸入區域的。(當然,如果你還不知道,可以先看下斯坦福大學的CS231n 課程或者論文"A guide to convolution arithmetic for deep learning"。)

接下來,假設輸入是 3x3。我們的目標是通過上取樣讓輸出尺寸變大。你可以認為小步長卷積就是在畫素之間填充 0 值來拓展輸入區域的方法,然後再對輸入區域進行卷積操作,正如下圖所示,得到一個 5x5 的輸出。

注意,對於作為上取樣的卷積層有很多不同的名字,比如全卷積(full convolution), 網路內上取樣(in-network upsampling), 小步長卷積(fractionally-strided convolution), 反向卷積(backwards convolution), 反捲積(deconvolution), 上卷積(upconvolution), 轉置卷積(transposed convolution)。這裡並不鼓勵使用反捲積(deconvolution)這個詞語,因為在數學運算或者計算機視覺的其他應用中,這個詞語有著其他完全不同的意思,這是一個非常頻繁使用的詞語。

現在利用小步長卷積作為基礎,我們可以實現G(z)函式,讓它接收一個z \thicksim p_z的向量輸入,然後輸出一張尺寸是 64x64x3 的彩色圖片,其網路結構如下圖所示:

在 DCGAN 這篇論文中還提出了其他的一些技巧和改進來訓練 DCGANs,比如採用批歸一化(batch normalization)或者是 leaky ReLUs 啟用函式。

採用 G(z) 生成假的圖片

現在先讓我們暫停並欣賞下這種G(z)網路結構的強大,在 DCGAN 論文中給出瞭如何採用一個臥室圖片資料集訓練 一個 DCGAN 模型,然後採用G(z)生成如下的圖片,它們都是生成器網路 G 認為的臥室圖片,注意,下面這些圖片都是原始訓練資料集沒有的!

此外,你還可以對 z 輸入實現一個向量算術操作,下圖就是一個例子:

[ML-Heavy] 訓練 DCGAN

現在我們定義好了G(z),也知道它的能力有多強大,問題來了,怎麼訓練呢?我們需要確定很多隱變數(或者說引數),這也是採用對抗網路的原因了。

首先,我們先定義幾個符號。p_data表示訓練資料,但概率分佈未知,p_z表示從已知的概率分佈取樣的樣本,一般從高斯分佈或者均勻分佈取樣,z也被稱為隨機噪聲,最後一個,p_g就是 G 網路生成的資料,也可以說是生成概率分佈。

接著介紹下判別器(discriminator,D)網路,它是輸入一批圖片x,然後返回該圖片來自訓練資料p_{data}的概率。如果來自訓練資料,D 應該返回一個接近 1 的數值,否則應該是一個接近 0 的值來表示圖片是假的,來自 G 網路生成的。在 DCGANs 中,D 網路是一個傳統的卷積神經網路,如下圖所示,一個包含4層卷積層和1層全連線層的卷積神經網路結構。

因此,訓練 D 網路的目標有以下兩個:

  1. 如果x來自訓練資料集,最大化D(x)
  2. 如果x是來自 G 生成的資料,最小化D(x)

對應的 G 網路的目標就是要欺騙 D 網路,生成以假亂真的圖片。它生成的圖片也是 D 的輸入,所以 G 的目標就是最大化D(G(z)),也等價於最小化1-D(G(z)),因為 D 其實是一個概率估計,且輸出範圍是在 0 到 1 之間。

正如論文提到的,訓練對抗網路就如同在實現一個最小化最大化遊戲(minimax game)。如下面的公式所示,第一項是對真實資料分佈的期望,第二項是對生成資料的期望值。

訓練的步驟如下圖所示,具體可以看下我之前寫的文章[GAN學習系列2] GAN的起源有簡單介紹了這個訓練過程,或者是看下 GAN 論文[5]的介紹

目前的 GAN 和 DCGAN 實現

目前在 Github 上有許多 GAN 和 DCGAN 的實現(原文是寫於2016年八月份,現在的話程式碼就更多了):

本文實現的程式碼是基於 github.com/carpedm20/D…

[ML-Heavy] TensorFlow 實現 DCGAN

這部分的實現的原始碼可以在如下 Github 地址:

github.com/bamos/dcgan…

當然,主要實現部分程式碼是來自 github.com/carpedm20/D… 。但採用這個專案主要是方便實現下一部分的影象修復工作。

主要實現程式碼是在model.py中的類DCGAN。採用類來實現模型是有助於訓練後儲存中間層的狀態以及後續的載入使用。

首先,我們需要定義生成器和判別器網路結構。在ops.py會定義網路結構用到的函式,如linear,conv2d_transpose, conv2d以及 lrelu。程式碼如下所示

def generator(self, z):
    self.z_, self.h0_w, self.h0_b = linear(z, self.gf_dim*8*4*4,
                                           'g_h0_lin', with_w=True)

    self.h0 = tf.reshape(self.z_, [-1, 4, 4, self.gf_dim * 8])
    h0 = tf.nn.relu(self.g_bn0(self.h0))

    self.h1, self.h1_w, self.h1_b = conv2d_transpose(h0,
        [self.batch_size, 8, 8, self.gf_dim*4], name='g_h1', with_w=True)
    h1 = tf.nn.relu(self.g_bn1(self.h1))

    h2, self.h2_w, self.h2_b = conv2d_transpose(h1,
        [self.batch_size, 16, 16, self.gf_dim*2], name='g_h2', with_w=True)
    h2 = tf.nn.relu(self.g_bn2(h2))

    h3, self.h3_w, self.h3_b = conv2d_transpose(h2,
        [self.batch_size, 32, 32, self.gf_dim*1], name='g_h3', with_w=True)
    h3 = tf.nn.relu(self.g_bn3(h3))

    h4, self.h4_w, self.h4_b = conv2d_transpose(h3,
        [self.batch_size, 64, 64, 3], name='g_h4', with_w=True)

    return tf.nn.tanh(h4)

def discriminator(self, image, reuse=False):
    if reuse:
        tf.get_variable_scope().reuse_variables()

    h0 = lrelu(conv2d(image, self.df_dim, name='d_h0_conv'))
    h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim*2, name='d_h1_conv')))
    h2 = lrelu(self.d_bn2(conv2d(h1, self.df_dim*4, name='d_h2_conv')))
    h3 = lrelu(self.d_bn3(conv2d(h2, self.df_dim*8, name='d_h3_conv')))
    h4 = linear(tf.reshape(h3, [-1, 8192]), 1, 'd_h3_lin')

    return tf.nn.sigmoid(h4), h4
複製程式碼

當初始化這個類的時候,就相當於用上述函式來構建了這個模型。我們需要建立兩個 D 網路來共享引數,一個的輸入是真實資料,另一個是來自 G 網路的生成資料。

self.G = self.generator(self.z)
self.D, self.D_logits = self.discriminator(self.images)
self.D_, self.D_logits_ = self.discriminator(self.G, reuse=True)
複製程式碼

接下來是定義損失函式。這裡採用的是 D 的輸出之間的交叉熵函式,並且它的效果也不錯。D 是期望對真實資料的預測都是 1,對生成的假資料預測都是 0,相反,生成器 G 希望 D 的預測都是 1。程式碼的實現如下:

self.d_loss_real = tf.reduce_mean(
    tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits,
                                            tf.ones_like(self.D)))
self.d_loss_fake = tf.reduce_mean(
    tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits_,
                                            tf.zeros_like(self.D_)))
self.d_loss = self.d_loss_real + self.d_loss_fake

self.g_loss = tf.reduce_mean(
    tf.nn.sigmoid_cross_entropy_with_logits(self.D_logits_,
                                            tf.ones_like(self.D_)))
複製程式碼

接著是分別對 G 和 D 的引數聚集到一起,方便後續的梯度計算:

t_vars = tf.trainable_variables()

self.d_vars = [var for var in t_vars if 'd_' in var.name]
self.g_vars = [var for var in t_vars if 'g_' in var.name]
複製程式碼

現在才有 ADAM 作為優化器來計算梯度,ADAM 是一個深度學習中常用的自適應非凸優化方法,它相比於隨機梯度下降方法,不需要手動調整學習率、動量(momentum)以及其他的超引數。

d_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
                    .minimize(self.d_loss, var_list=self.d_vars)
g_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
                    .minimize(self.g_loss, var_list=self.g_vars)
複製程式碼

定義好模型和訓練策略後,接下來就是開始輸入資料進行訓練了。在每個 epoch 中,先採樣一個 mini-batch 的圖片,然後執行優化器來更新網路。有趣的是如果 G 只更新一次,D 的 loss 是不會變為0的。此外,在後面額外呼叫d_loss_faked_loss_real會增加不必要的計算量,並且也是多餘的,因為它們的數值在d_optimg_optim計算的時候已經計算到了。這裡你可以嘗試優化這部分程式碼,然後傳送一個 PR 到原始的 Github 專案中。

for epoch in xrange(config.epoch):
    ...
    for idx in xrange(0, batch_idxs):
        batch_images = ...
        batch_z = np.random.uniform(-1, 1, [config.batch_size, self.z_dim]) \
                    .astype(np.float32)

        # Update D network
        _, summary_str = self.sess.run([d_optim, self.d_sum],
            feed_dict={ self.images: batch_images, self.z: batch_z })

        # Update G network
        _, summary_str = self.sess.run([g_optim, self.g_sum],
            feed_dict={ self.z: batch_z })

        # Run g_optim twice to make sure that d_loss does not go to zero
        # (different from paper)
        _, summary_str = self.sess.run([g_optim, self.g_sum],
            feed_dict={ self.z: batch_z })

        errD_fake = self.d_loss_fake.eval({self.z: batch_z})
        errD_real = self.d_loss_real.eval({self.images: batch_images})
        errG = self.g_loss.eval({self.z: batch_z})
複製程式碼

完整的程式碼可以在 github.com/bamos/dcgan… 中檢視

在你的資料集上執行 DCGAN 模型

如果你跳過上一小節,但希望執行一些程式碼:這部分的實現的原始碼可以在如下 Github 地址:

github.com/bamos/dcgan…

當然,主要實現部分程式碼是來自 github.com/carpedm20/D… 。但採用這個專案主要是方便實現下一部分的影象修復工作。但必須注意的是,如果你沒有一個可以使用 CUDA 的 GPU 顯示卡,那麼訓練網路將會非常慢。

首先需要克隆兩份專案程式碼,地址分別如下:

github.com/bamos/dcgan…

cmusatyalab.github.io/openface

第一份就是作者的專案程式碼,第二份是採用 OpenFace 的預處理圖片的 Python 程式碼,並不需要安裝它的 Torch 依賴包。先建立一個新的工作資料夾,然後開始克隆,如下所示:

git clone https://github.com/cmusatyalab/openface.git
git clone https://github.com/bamos/dcgan-completion.tensorflow.git
複製程式碼

接著是安裝 Python2 版本的 OpenCVdlib(採用 Python2 版本是因為 OpenFace 採用這個版本,當然你也可以嘗試修改為適應 Python3 版本)。對於 OpenFace 的 Python 庫安裝,可以檢視其安裝指導教程,連結如下:

cmusatyalab.github.io/openface/se…

此外,如果你沒有采用一個虛擬環境,那麼需要加入sudo命令來執行setup.py實現全域性的安裝 OpenFace,當然如果安裝這部分有問題,也可以採用 OpenFace 的 docker 映象安裝。安裝的命令如下所示

cd openface
pip2 install -r requirements.txt
python2 setup.py install
models/get-models.sh
cd ..
複製程式碼

接著就是下載一些人臉圖片資料集了,這裡並不要求它們是否帶有標籤,因為不需要。目前開源可選的資料集包括

然後將資料集放到目錄dcgan-completion.tensorflow/data/your-dataset/raw下表示其是原始的圖片。

接著採用 OpenFace 的對齊工具來預處理圖片並調整成64x64的尺寸:

./openface/util/align-dlib.py data/dcgan-completion.tensorflow/data/your-dataset/raw align innerEyesAndBottomLip data/dcgan-completion.tensorflow/data/your-dataset/aligned --size 64
複製程式碼

最後是整理下儲存對齊圖片的目錄,保證只包含圖片而沒有其他的子資料夾:

cd dcgan-completion.tensorflow/data/your-dataset/aligned
find . -name '*.png' -exec mv {} . \;
find . -type d -empty -delete
cd ../../..
複製程式碼

然後確保已經安裝了 TensorFlow,那麼可以開始訓練 DCGAN了:

./train-dcgan.py --dataset ./data/your-dataset/aligned --epoch 20
複製程式碼

samples資料夾中可以檢視儲存的由 G 生成的圖片。這裡作者是採用手上有的兩個資料集 CASIA-WebFace 和 FaceScrub 進行訓練,並在訓練 14 個 epochs 後,生成的結果如下圖所示:

還可以通過 TensorBoard 來檢視 loss 的變化:

tensorboard --logdir ./logs
複製程式碼


小結

這就是本文的第二部分內容,主要是介紹了 DCGAN 的基本原理以及程式碼實現,還有就是訓練前的準備和開始訓練,訓練的實驗結果。

在下一篇將介紹最後一步內容,如何利用 DCGAN 來實現影象修復的工作!

歡迎關注我的微信公眾號--機器學習與計算機視覺,或者掃描下方的二維碼,在後臺留言,和我分享你的建議和看法,指正文章中可能存在的錯誤,大家一起交流,學習和進步!

我的個人部落格:ccc013.github.io/

CSDN 部落格:blog.csdn.net/lc013/artic…


推薦閱讀

1.機器學習入門系列(1)--機器學習概覽(上)

2.機器學習入門系列(2)--機器學習概覽(下)

3.[GAN學習系列] 初識GAN

4.[GAN學習系列2] GAN的起源

5.[GAN學習系列3]採用深度學習和 TensorFlow 實現圖片修復(上)