生成式對抗網絡GAN
1、 基本GAN
在論文《Generative Adversarial Nets》提出的GAN是最原始的框架,可以看成極大極小博弈的過程,因此稱為“對抗網絡”。一般包含兩個部分:生成器(Generator)和判別器(Discriminator)。訓練的過程是無監督學習。
先總結一下訓練的過程。一般而言,輸入是一個一維向量z,它從先驗生成。假設現在Generator生成的是圖像
。我們知道,無監督學習目的是學習數據集中的特征(或者說分布),假設真實的分布為
,而Generator的生成圖像的過程其實隱式地定義了一個學習到的分布
。把生成的圖像
輸入到Discriminator中即
,計算的是樣本來自於真實分布而不是由
生成的概率,因為Discriminator最終只有一個輸出。上述無論是Generator或者Discriminator都是一般常見的網絡,如下圖:
如上所說,訓練的過程是極大極小的博弈過程,歸納成下式:
即Generator希望極大化Discriminator誤判的概率,而Discriminator極小化把生成樣本判成來自真實data的概率。論文中證明,上式的最優解在於。
可以看到,學習到的樣本分布(或者特征表示)並沒有一個顯式的結果,這算是GAN的一個缺點了。訓練的過程是同時訓練兩個網絡,由於Discriminator可以更好地指導Generator的調整,所以一般會讓Discriminator循環的次數更多。最優化過程使用的是梯度下降算法,如下:
疑惑:在訓練D的時候,按照公式應該是一個極大化的過程,為什么可以使用SGD呢?因此我覺得上式應該是不對的,之前應該缺少一個負號轉換成一個極小化的問題。
而開始時,可能會很接近於0,這使得log函數也接近於0,最終結果是梯度下降時由於回流梯度過小無法更新淺層網絡。因此,論文建議訓練開始時可以求解極大化
。
2、深度卷積生成對抗網絡DCGAN
論文《UNSUPERVISED REPRESENTATION L EARNING WITH DEEP CONVOLUTIONAL GENERATIVE ADVERSARIAL NETWORKS》提出DCGAN,可以看成是GAN應用在CNN的嘗試。論文更多的是在CNN工程上的嘗試經驗,由於GAN在訓練時的不穩定性,因此提出了幾點改變:
1、把所有的pooling層用strided convolution替代。在D網絡即是跨步長的卷積,在G網絡則是上采樣(此處稱為fractional-strided,但很多代碼實現似乎都用了deconv,在tensorflow有這樣一個函數)。
2、在G和D都應用BN,但是在G的輸出層和D的輸入層不應用BN。
3、移除全連接層
4、在G中的激活函數使用RELU,但在輸出層使用的是Tanh
5、在D中的激活函數全部使用Leaky ReLu。
結構圖如下:
這里主要看它的實現過程。代碼來自https://github.com/carpedm20/DCGAN-tensorflow/blob/master/model.py 。
D網絡部分和一般的卷積網絡沒有什么區別,主要最后一步是把feature map進行一個flatten的操作,然后全部feed到一個sigmoid的單元,即下式的h4。
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, [self.batch_size, -1]), 1, 'd_h3_lin') return tf.nn.sigmoid(h4), h4
注意這里的conv2d實現時已經加上了bias。
在G網絡部分,主要關注z到project and reshape部分和如何進行fractional-strided convolution。
對於project and reshape,代碼中的實現是:
self.z_, self.h0_w, self.h0_b = linear(z, self.gf_dim*8*s16*s16, 'g_h0_lin', with_w=True) self.h0 = tf.reshape(self.z_, [-1, s16, s16, self.gf_dim * 8])
其中linear()函數是通過matrix相乘把z變成self.gf_dim*8*s16*s16
大小的向量。然后通過
reshape
得到
feature maps
。
而對於
fractional-strided convolution,這里使用一個函數deconv2d(),代碼如下:
w = tf.get_variable('w', [k_h, k_w, output_shape[-1], input_.get_shape()[-1]],initializer=tf.random_normal_initializer(stddev=stddev)) deconv = tf.nn.conv2d_transpose(input_, w, output_shape=output_shape, strides=[1, d_h, d_w, 1]) biases = tf.get_variable('biases', [output_shape[-1]], initializer=tf.constant_initializer(0.0)) deconv = tf.reshape(tf.nn.bias_add(deconv, biases), deconv.get_shape())
通過一個tf.nn.conv2d_transpose()函數實現反卷積。但有一點注意的是,tf.nn.conv2d_transpose()函數不是什么shape都可以輸出的,驗證是否正確的方法是,把output與卷積核w做一次卷積,如果得到的shape和input的一致,代表是正確的。
可以參考http://stackoverflow.com/questions/35488717/confused-about-conv2d-transpose 。
3、條件GAN conditional GAN
條件GAN,我認為很多blog是寫錯了的,它和條件概率應該是沒有關系的。GAN的一個很大的優點是,它的輸入很靈活沒有過大的限制。而條件GAN其實是在一般輸入時添加了額外的input,這作為一個可控制的變量去指導着網絡的訓練。因為基本GAN的訓練應該是無方向(這里表達不准確)的。
一個簡單的網絡如下:
Y作為額外的輸入的變量,是可控的。在論文《Conditional Generative Adversarial Nets》中的一個例子是訓練mnist,其中y則是0~9的標簽的一個one-hot的編碼向量。
此外,由於GAN輸入的靈活,可以很容易想到可以加入多模態的信息對網絡訓練進行指導,事實上已經有了不少的嘗試。