[GAN學(xué)習(xí)系列3]采用深度學(xué)習(xí)和 TensorFlow 實現(xiàn)圖片修復(fù)(中)

上一篇文章--[GAN學(xué)習(xí)系列3]采用深度學(xué)習(xí)和 TensorFlow 實現(xiàn)圖片修復(fù)(上)中,我們先介紹了對于圖像修復(fù)的背景,需要利用什么信息來對缺失的區(qū)域進行修復(fù),以及將圖像當做概率分布采樣的樣本來看待,通過這個思路來開始進行圖像的修復(fù)。

這篇文章將繼續(xù)介紹原文的第二部分,利用對抗生成網(wǎng)絡(luò)來快速生成假圖片。目錄如下:

  • 第二步:快速生成假的圖片
    • 從未知的概率分布中學(xué)習(xí)生成新的樣本
    • [ML-Heavy] 建立 GAN 模型
    • 采用 G(z) 生成假的圖片
    • [ML-Heavy] 訓(xùn)練 DCGAN
    • 目前的 GAN 和 DCGAN 實現(xiàn)
    • [ML-Heavy] TensorFlow 實現(xiàn) DCGAN
    • 在你的數(shù)據(jù)集上運行 DCGAN 模型

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


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

從未知的概率分布中學(xué)習(xí)生成新的樣本

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

image

Yann LeCun 在回答中簡單介紹了 GAN 的基本原理,也就是兩個網(wǎng)絡(luò)相互博弈的過程。

實際上,深度學(xué)習(xí)還有其他方法來訓(xùn)練生成模型,比如 Variational Autoencoders(VAEs)。但在本文,主要介紹對抗生成網(wǎng)絡(luò)(GANs)

[ML-Heavy] 建立 GAN 模型

GANs 這個想法是 Ian Goodfellow 在其帶有里程碑意義的論文“Generative Adversarial Nets” (GANs)發(fā)表在 2014 年的 Neural Information Processing Systems (NIPS) 會議上后開始火遍整個深度學(xué)習(xí)領(lǐng)域的。這個想法就是我們首先定義一個簡單并眾所周知的概率分布,并表示為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])

現(xiàn)在我們有一個簡單的分布來進行采樣,接下來可以定義一個函數(shù)G(z)來從原始的概率分布中生成樣本,代碼例子如下所示:

def G(z):
   ...
   return imageSample

z = np.random.uniform(-1, 1, 5)
imageSample = G(z)

那么問題來了,怎么定義這個G(Z)函數(shù),讓它實現(xiàn)輸入一個向量然后返回一張圖片呢?答案就是采用一個深度神經(jīng)網(wǎng)絡(luò)。對于深度神經(jīng)網(wǎng)絡(luò)基礎(chǔ),網(wǎng)絡(luò)上有很多的介紹,本文就不再重復(fù)介紹了。這里推薦的一些參考有斯坦福大學(xué)的 CS231n 課程、Ian Goodfellow 等人編著的《深度學(xué)習(xí)》書籍形象解釋圖像的核心以及論文"A guide to convolution arithmetic for deep learning"

通過深度學(xué)習(xí)可以有多種方法來實現(xiàn)G(z)函數(shù)。在原始的 GAN 論文中提出一種訓(xùn)練方法并給出初步的實驗結(jié)果,這個方法得到了極大的發(fā)展和改進。其中一種想法就是在論文“Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks”中提出的,這篇論文的作者是 Alec Radford, Luke Metz, and Soumith Chintala,發(fā)表在 2016 年的 International Conference on Learning Representations (ICLR)會議上,這個方法因為提出采用深度卷積神經(jīng)網(wǎng)絡(luò),被稱為 DCGANs,它主要采用小步長卷積( fractionally-strided convolution)方法來上采樣圖像

那么什么是小步長卷積以及如何實現(xiàn)對圖片的上采樣呢? Vincent Dumoulin and Francesco Visin’s 在論文"A guide to convolution arithmetic for deep learning"以及 Github 項目都給出了這種卷積算術(shù)的詳細介紹,Github 地址如下:

https://github.com/vdumoulin/conv_arithmetic

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

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

image

接下來,假設(shè)輸入是 3x3。我們的目標是通過上采樣讓輸出尺寸變大。你可以認為小步長卷積就是在像素之間填充 0 值來拓展輸入?yún)^(qū)域的方法,然后再對輸入?yún)^(qū)域進行卷積操作,正如下圖所示,得到一個 5x5 的輸出。

image

注意,對于作為上采樣的卷積層有很多不同的名字,比如全卷積(full convolution), 網(wǎng)絡(luò)內(nèi)上采樣(in-network upsampling), 小步長卷積(fractionally-strided convolution), 反向卷積(backwards convolution), 反卷積(deconvolution), 上卷積(upconvolution), 轉(zhuǎn)置卷積(transposed convolution)。這里并不鼓勵使用反卷積(deconvolution)這個詞語,因為在數(shù)學(xué)運算或者計算機視覺的其他應(yīng)用中,這個詞語有著其他完全不同的意思,這是一個非常頻繁使用的詞語。

現(xiàn)在利用小步長卷積作為基礎(chǔ),我們可以實現(xiàn)G(z)函數(shù),讓它接收一個z \thicksim p_z的向量輸入,然后輸出一張尺寸是 64x64x3 的彩色圖片,其網(wǎng)絡(luò)結(jié)構(gòu)如下圖所示:

image

在 DCGAN 這篇論文中還提出了其他的一些技巧和改進來訓(xùn)練 DCGANs,比如采用批歸一化(batch normalization)或者是 leaky ReLUs 激活函數(shù)。

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

現(xiàn)在先讓我們暫停并欣賞下這種G(z)網(wǎng)絡(luò)結(jié)構(gòu)的強大,在 DCGAN 論文中給出了如何采用一個臥室圖片數(shù)據(jù)集訓(xùn)練 一個 DCGAN 模型,然后采用G(z)生成如下的圖片,它們都是生成器網(wǎng)絡(luò) G 認為的臥室圖片,注意,下面這些圖片都是原始訓(xùn)練數(shù)據(jù)集沒有的!

image

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

image

[ML-Heavy] 訓(xùn)練 DCGAN

現(xiàn)在我們定義好了G(z),也知道它的能力有多強大,問題來了,怎么訓(xùn)練呢?我們需要確定很多隱變量(或者說參數(shù)),這也是采用對抗網(wǎng)絡(luò)的原因了。

首先,我們先定義幾個符號。p_data表示訓(xùn)練數(shù)據(jù),但概率分布未知,p_z表示從已知的概率分布采樣的樣本,一般從高斯分布或者均勻分布采樣,z也被稱為隨機噪聲,最后一個,p_g就是 G 網(wǎng)絡(luò)生成的數(shù)據(jù),也可以說是生成概率分布。

接著介紹下判別器(discriminator,D)網(wǎng)絡(luò),它是輸入一批圖片x,然后返回該圖片來自訓(xùn)練數(shù)據(jù)p_{data}的概率。如果來自訓(xùn)練數(shù)據(jù),D 應(yīng)該返回一個接近 1 的數(shù)值,否則應(yīng)該是一個接近 0 的值來表示圖片是假的,來自 G 網(wǎng)絡(luò)生成的。在 DCGANs 中,D 網(wǎng)絡(luò)是一個傳統(tǒng)的卷積神經(jīng)網(wǎng)絡(luò),如下圖所示,一個包含4層卷積層和1層全連接層的卷積神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)。

image

因此,訓(xùn)練 D 網(wǎng)絡(luò)的目標有以下兩個:

  1. 如果x來自訓(xùn)練數(shù)據(jù)集,最大化D(x)
  2. 如果x是來自 G 生成的數(shù)據(jù),最小化D(x)

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

正如論文提到的,訓(xùn)練對抗網(wǎng)絡(luò)就如同在實現(xiàn)一個最小化最大化游戲(minimax game)。如下面的公式所示,第一項是對真實數(shù)據(jù)分布的期望,第二項是對生成數(shù)據(jù)的期望值。

image

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

image

目前的 GAN 和 DCGAN 實現(xiàn)

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

本文實現(xiàn)的代碼是基于 https://github.com/carpedm20/DCGAN-tensorflow

[ML-Heavy] TensorFlow 實現(xiàn) DCGAN

這部分的實現(xiàn)的源代碼可以在如下 Github 地址:

https://github.com/bamos/dcgan-completion.tensorflow

當然,主要實現(xiàn)部分代碼是來自 https://github.com/carpedm20/DCGAN-tensorflow 。但采用這個項目主要是方便實現(xiàn)下一部分的圖像修復(fù)工作。

主要實現(xiàn)代碼是在model.py中的類DCGAN。采用類來實現(xiàn)模型是有助于訓(xùn)練后保存中間層的狀態(tài)以及后續(xù)的加載使用。

首先,我們需要定義生成器和判別器網(wǎng)絡(luò)結(jié)構(gòu)。在ops.py會定義網(wǎng)絡(luò)結(jié)構(gòu)用到的函數(shù),如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

當初始化這個類的時候,就相當于用上述函數(shù)來構(gòu)建了這個模型。我們需要創(chuàng)建兩個 D 網(wǎng)絡(luò)來共享參數(shù),一個的輸入是真實數(shù)據(jù),另一個是來自 G 網(wǎng)絡(luò)的生成數(shù)據(jù)。

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)

接下來是定義損失函數(shù)。這里采用的是 D 的輸出之間的交叉熵函數(shù),并且它的效果也不錯。D 是期望對真實數(shù)據(jù)的預(yù)測都是 1,對生成的假數(shù)據(jù)預(yù)測都是 0,相反,生成器 G 希望 D 的預(yù)測都是 1。代碼的實現(xiàn)如下:

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 的參數(shù)聚集到一起,方便后續(xù)的梯度計算:

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]

現(xiàn)在才有 ADAM 作為優(yōu)化器來計算梯度,ADAM 是一個深度學(xué)習(xí)中常用的自適應(yīng)非凸優(yōu)化方法,它相比于隨機梯度下降方法,不需要手動調(diào)整學(xué)習(xí)率、動量(momentum)以及其他的超參數(shù)。

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)

定義好模型和訓(xùn)練策略后,接下來就是開始輸入數(shù)據(jù)進行訓(xùn)練了。在每個 epoch 中,先采樣一個 mini-batch 的圖片,然后運行優(yōu)化器來更新網(wǎng)絡(luò)。有趣的是如果 G 只更新一次,D 的 loss 是不會變?yōu)?的。此外,在后面額外調(diào)用d_loss_faked_loss_real會增加不必要的計算量,并且也是多余的,因為它們的數(shù)值在d_optimg_optim計算的時候已經(jīng)計算到了。這里你可以嘗試優(yōu)化這部分代碼,然后發(fā)送一個 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})

完整的代碼可以在 https://github.com/bamos/dcgan-completion.tensorflow/blob/master/model.py 中查看

在你的數(shù)據(jù)集上運行 DCGAN 模型

如果你跳過上一小節(jié),但希望運行一些代碼:這部分的實現(xiàn)的源代碼可以在如下 Github 地址:

https://github.com/bamos/dcgan-completion.tensorflow

當然,主要實現(xiàn)部分代碼是來自 https://github.com/carpedm20/DCGAN-tensorflow 。但采用這個項目主要是方便實現(xiàn)下一部分的圖像修復(fù)工作。但必須注意的是,如果你沒有一個可以使用 CUDA 的 GPU 顯卡,那么訓(xùn)練網(wǎng)絡(luò)將會非常慢。

首先需要克隆兩份項目代碼,地址分別如下:

https://github.com/bamos/dcgan-completion.tensorflow

http://cmusatyalab.github.io/openface

第一份就是作者的項目代碼,第二份是采用 OpenFace 的預(yù)處理圖片的 Python 代碼,并不需要安裝它的 Torch 依賴包。先創(chuàng)建一個新的工作文件夾,然后開始克隆,如下所示:

git clone https://github.com/cmusatyalab/openface.git
git clone https://github.com/bamos/dcgan-completion.tensorflow.git

接著是安裝 Python2 版本的 OpenCVdlib(采用 Python2 版本是因為 OpenFace 采用這個版本,當然你也可以嘗試修改為適應(yīng) Python3 版本)。對于 OpenFace 的 Python 庫安裝,可以查看其安裝指導(dǎo)教程,鏈接如下:

http://cmusatyalab.github.io/openface/setup/

此外,如果你沒有采用一個虛擬環(huán)境,那么需要加入sudo命令來運行setup.py實現(xiàn)全局的安裝 OpenFace,當然如果安裝這部分有問題,也可以采用 OpenFace 的 docker 鏡像安裝。安裝的命令如下所示

cd openface
pip2 install -r requirements.txt
python2 setup.py install
models/get-models.sh
cd ..

接著就是下載一些人臉圖片數(shù)據(jù)集了,這里并不要求它們是否帶有標簽,因為不需要。目前開源可選的數(shù)據(jù)集包括

然后將數(shù)據(jù)集放到目錄dcgan-completion.tensorflow/data/your-dataset/raw下表示其是原始的圖片。

接著采用 OpenFace 的對齊工具來預(yù)處理圖片并調(diào)整成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 ../../..

然后確保已經(jīng)安裝了 TensorFlow,那么可以開始訓(xùn)練 DCGAN了:

./train-dcgan.py --dataset ./data/your-dataset/aligned --epoch 20

samples文件夾中可以查看保存的由 G 生成的圖片。這里作者是采用手上有的兩個數(shù)據(jù)集 CASIA-WebFace 和 FaceScrub 進行訓(xùn)練,并在訓(xùn)練 14 個 epochs 后,生成的結(jié)果如下圖所示:

image

還可以通過 TensorBoard 來查看 loss 的變化:

tensorboard --logdir ./logs
image

小結(jié)

這就是本文的第二部分內(nèi)容,主要是介紹了 DCGAN 的基本原理以及代碼實現(xiàn),還有就是訓(xùn)練前的準備和開始訓(xùn)練,訓(xùn)練的實驗結(jié)果。

在下一篇將介紹最后一步內(nèi)容,如何利用 DCGAN 來實現(xiàn)圖像修復(fù)的工作!

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

image

我的個人博客:http://ccc013.github.io/

CSDN 博客:https://blog.csdn.net/lc013/article/details/84845439


推薦閱讀

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

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

3.[GAN學(xué)習(xí)系列] 初識GAN

4.[GAN學(xué)習(xí)系列2] GAN的起源

5.[GAN學(xué)習(xí)系列3]采用深度學(xué)習(xí)和 TensorFlow 實現(xiàn)圖片修復(fù)(上)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容