LSGAN:最小二乘生成對抗網路

PaperWeekly發表於2018-10-10

筆者這次選擇復現的是 Least Squares Generative Adversarial Networks,也就是LSGANs

LSGAN:最小二乘生成對抗網路

近幾年來 GAN 是十分火熱的,由 Goodfellow 在 14 年發表論文 Generative Adversarial Nets [1] 開山之作以來,生成式對抗網路一直都備受機器學習領域的關注,這種兩人零和博弈的思想十分有趣,充分體現了數學的美感。從 GAN 到 WGAN [2] 的優化,再到本文介紹的 LSGANs,再到最近很火的 BigGAN [3],可以說生成式對抗網路的魅力無窮,而且它的用處也是非常奇妙,如今還被用在例如無負樣本的情況下如何訓練分類器,例如 AnoGAN [4]。 

LSGANs 這篇經典的論文主要工作是交叉熵損失函式換做了最小二乘損失函式,這樣做作者認為改善了傳統 GAN 的兩個問題,即傳統 GAN 生成的圖片質量不高,而且訓練過程十分不穩定。

LSGANs 試圖使用不同的距離度量來構建一個更加穩定而且收斂更快的,生成質量高的對抗網路。但是我看過 WGAN 的論文之後分析這一損失函式,其實並不符合 WGAN 作者的分析。在下面我會詳細分析一下為什麼 LSGANs 其實並沒有那麼好用。

論文復現程式碼: 

http://aistudio.baidu.com/aistudio/#/projectdetail/25767

LSGANs的優點

我們知道傳統 GAN 生成的圖片質量不高,傳統的 GANs 使用的是交叉熵損失(sigmoid cross entropy)作為判別器的損失函式。 

在這裡說一下我對交叉熵的理解,有兩個分佈,分別是真實分佈 p 和非真實分佈 q。

資訊熵LSGAN:最小二乘生成對抗網路,就是按照真實分佈 p 這樣的樣本空間表達能力強度的相反值,資訊熵越大,不確定性越大,表達能力越弱,我們記作 H(p)。 交叉熵就是LSGAN:最小二乘生成對抗網路,可以理解為按照不真實分佈 q 這樣的樣本空間表達能力強度的相反值,記作 H(p,q)。 

KL 散度就是 D(p||q) = H(p,q) - H(p),它表示的是兩個分佈的差異,因為真實分佈 p 的資訊熵固定,所以一般由交叉熵來決定,所以這就是為什麼傳統 GAN 會採用交叉熵的緣故,論文也證明了 GAN 損失函式與 KL 散度的關係。 

我們知道交叉熵一般都是拿來做邏輯分類的,而像最小二乘這種一般會用在線性迴歸中,這裡為什麼會用最小二乘作為損失函式的評判呢? 

使用交叉熵雖然會讓我們分類正確,但是這樣會導致那些在決策邊界被分類為真的、但是仍然遠離真實資料的假樣本(即生成器生成的樣本)不會繼續迭代,因為它已經成功欺騙了判別器,更新生成器的時候就會發生梯度彌散的問題。 

論文指出最小二乘損失函式會對處於判別成真的那些遠離決策邊界的樣本進行懲罰,把遠離決策邊界的假樣本拖進決策邊界,從而提高生成圖片的質量。作者用下圖詳細表達了這一說法:LSGAN:最小二乘生成對抗網路

我們知道傳統 GAN 的訓練過程十分不穩定,這很大程度上是因為它的目標函式,尤其是在最小化目標函式時可能發生梯度彌散,使其很難再去更新生成器。而論文指出 LSGANs 可以解決這個問題,因為 LSGANs 會懲罰那些遠離決策邊界的樣本,這些樣本的梯度是梯度下降的決定方向。

論文指出因為傳統 GAN 辨別器 D 使用的是 sigmoid 函式,並且由於 sigmoid 函式飽和得十分迅速,所以即使是十分小的資料點 x,該函式也會迅速忽略樣本 x 到決策邊界 w 的距離。這就意味著 sigmoid 函式本質上不會懲罰遠離決策邊界的樣本,並且也說明我們滿足於將 x 標註正確,因此辨別器 D 的梯度就會很快地下降到 0。

我們可以認為,交叉熵並不關心距離,而是僅僅關注於是否正確分類。正如論文作者在下圖中所指出的那樣,(a)圖看到交叉熵損失很容易就達到飽和狀態,而(b)圖最小二乘損失只在一點達到飽和,作者認為這樣訓練會更加穩定。

LSGAN:最小二乘生成對抗網路

LSGANs的損失函式

傳統 GAN 的損失函式

LSGAN:最小二乘生成對抗網路

LSGANs 的損失函式

LSGAN:最小二乘生成對抗網路

其中 G 為生成器(Generator),D 為判別器(Discriminator),z 為噪音,它可以服從歸一化或者高斯分佈LSGAN:最小二乘生成對抗網路為真實資料 x 服從的概率分佈,為 z 服從的概率分佈。LSGAN:最小二乘生成對抗網路為期望值,LSGAN:最小二乘生成對抗網路同為期望值。

def generator(z, name="G"):
    with fluid.unique_name.guard(name+'_'):
        fc1 = fluid.layers.fc(input = z, size = 1024)
        fc1 = fluid.layers.fc(fc1, size = 128 * 7 * 7)
        fc1 = fluid.layers.batch_norm(fc1,act = 'tanh')
        fc1 = fluid.layers.reshape(fc1, shape=(-1, 128, 7, 7))


        conv1 = fluid.layers.conv2d(fc1, num_filters = 4*64,
                                    filter_size=5, stride=1, 
                                    padding=2, act='tanh')
        conv1 = fluid.layers.reshape(conv1, shape=(-1,64,14,14))

        conv2 = fluid.layers.conv2d(conv1, num_filters = 4*32, 
                                    filter_size=5, stride=1,
                                    padding=2, act='tanh')
        conv2 = fluid.layers.reshape(conv2, shape=(-1,32,28,28))

        conv3 = fluid.layers.conv2d(conv2, num_filters = 1, 
                                    filter_size=5, stride=1,
                                    padding=2,act='tanh')
#         conv3 = fluid.layers.reshape(conv3, shape=(-1,1,28,28))
        print("conv3",conv3)
        return conv3

▲ 生成器程式碼展示

def discriminator(image, name="D"):
    with fluid.unique_name.guard(name+'_'):
        conv1 = fluid.layers.conv2d(input=image, num_filters=32,
                                    filter_size=6, stride=2,
                                    padding=2)
        conv1_act = fluid.layers.leaky_relu(conv1)

        conv2 = fluid.layers.conv2d(conv1_act, num_filters=64, 
                                    filter_size=6, stride=2,
                                    padding=2)
        conv2 = fluid.layers.batch_norm(conv2)
        conv2_act = fluid.layers.leaky_relu(conv2)

        fc1 = fluid.layers.reshape(conv2_act, shape=(-1,64*7*7))
        fc1 = fluid.layers.fc(fc1, size=512)
        fc1_bn = fluid.layers.batch_norm(fc1)
        fc1_act = fluid.layers.leaky_relu(fc1_bn)

        fc2 = fluid.layers.fc(fc1_act, size=1)
        print("fc2",fc2)
        return fc2

▲ 判別器程式碼展示

作者提出了兩種 abc 的取值方法: 

1. 使 b - c = 1,b - a = 2,例如 a = -1,b = 1,c = 0:

LSGAN:最小二乘生成對抗網路

2. 使 c = b,用 0-1 二元標籤,我們可以得到:

LSGAN:最小二乘生成對抗網路

作者在文獻中有詳細推倒過程,詳細說明了 LSGAN 與 f 散度之間的關係,這裡簡述一下。

通過對下式求一階導可得到 D 的最優解:

LSGAN:最小二乘生成對抗網路

代入:

LSGAN:最小二乘生成對抗網路

其中另加項LSGAN:最小二乘生成對抗網路並不影響LSGAN:最小二乘生成對抗網路的值,因為它不包含引數 G。

最後我們設 b - c = 1,b - a =2 就可以得到:

LSGAN:最小二乘生成對抗網路

其中LSGAN:最小二乘生成對抗網路就是皮爾森卡方散度。

LSGANs未能解決的地方

下面我會指出 LSGANs 給出的損失函式到底符不符合 WGAN 前作的理論。關於 WGAN 前作及 WGAN 論文的分析可以參考本文 [5]。

上面我們指出了 D 的最優解為公式(5),我們最常用的設 a=-1,b=1,c=0 可以得出:

LSGAN:最小二乘生成對抗網路

把最優判別器帶入上面加附加項的生成器損失函式可以表示為:

LSGAN:最小二乘生成對抗網路

也就是優化上面說的皮爾森卡方散度,其實皮爾森卡方散度和 KL 散度、JS 散度有一樣的問題,根據 WGAN 給出的理論,下面用 P1,P2 分別表示LSGAN:最小二乘生成對抗網路LSGAN:最小二乘生成對抗網路

當 P1 與 P2 的支撐集(support)是高維空間中的低維流形(manifold)時,P1 與 P2 重疊部分測度(measure)為 0 的概率為 1。也就是 P1 和 P2 不重疊或重疊部分可忽略的可能性非常大。

對於資料點 x,只可能發生如下四種情況:

1. P1(x)=0,P2(x)=0

2. P1(x)!=0,P2(x)!=0

3. P1(x)=0,P2(x)!=0

4. P1(x)!=0,P2(x)=0

可以想象成下面這幅圖,假設 P1(x) 分佈就是 AB 線段,P2(x) 分佈就是 CD 線段,資料點要麼在兩條線段的其中一條,要麼都不在,同時在兩條線段上的可能性忽略不計。

LSGAN:最小二乘生成對抗網路

情況 1 是沒有意義的,而情況 2 由於重疊部分可忽略的可能性非常大所以對計算損失貢獻為 0,情況 3 可以算出 D*=-1,損失是個定值 1,情況 4 類似。

所以我們可以得出結論,當 P1 和 P2 不重疊或重疊部分可忽略的可能性非常大時,當判別器達到最優時,生成器仍然是不迭代的,因為此時損失是定值,提供的梯度仍然為 0。同時我們也可以從另一個角度出發,WGAN 的 Wasserstein 距離可以變換如下:

LSGAN:最小二乘生成對抗網路

它要求函式 f 要符合 Lipschitz 連續,可是最小二乘損失函式是不符合的,他的導數是沒有上界的。所以結論就是 LSGANs 其實還是未能解決判別器足夠優秀的時候,生成器還是會發生梯度彌散的問題。

兩種模型架構和訓練

模型的結構

作者也提出了兩類架構:

第一種處理類別少的情況,例如 MNIST、LSUN。網路設計如下:

LSGAN:最小二乘生成對抗網路

第二類處理類別特別多的情形,實際上是個條件版本的 LSGAN。針對手寫漢字資料集,有 3740 類,提出的網路結構如下:

LSGAN:最小二乘生成對抗網路

訓練資料

論文中使用了很多場景的資料集,然後比較了傳統 GANs 和 LSGANs 的穩定性,最後還通過訓練 3740 個類別的手寫漢字資料集來評價 LSGANs。

LSGAN:最小二乘生成對抗網路

▲ 本文使用的資料集列表

在 LSUN 和 HWDB1.0 的這兩個資料集上使用 LSGANs 的效果圖如下,其中 LSUN 使用了裡面的 bedroom, kitchen, church, dining room 和 conference room 五個場景,bedroom 場景還對比了 DCGANs 和 EBGANs 的效果在圖 5 中,可以觀察到 LSGANs 生成的效果要比那兩種的效果好。

LSGAN:最小二乘生成對抗網路

LSGAN:最小二乘生成對抗網路

圖 7 則體現了 LSGANs 和傳統 GANs 生成的圖片對比。

LSGAN:最小二乘生成對抗網路

通過實驗觀察,作者發現 4 點技巧: 

1. 生成器 G 帶有 batch normalization 批處理標準化(以下簡稱 BN)並且使用 Adam 優化器的話,LSGANs 生成的圖片質量好,但是傳統 GANs 從來沒有成功學習到,會出現 mode collapse 現象;

2. 生成器 G 和判別器 D 都帶有 BN 層,並且使用 RMSProp 優化器處理,LSGANs 會生成質量比 GANs 高的圖片,並且 GANs 會出現輕微的 mode collapse 現象;

3. 生成器 G 帶有 BN 層並且使用 RMSProp 優化器,生成器 G 判別器 D 都帶有 BN 層並且使用 Adam 優化器時,LSGANs 與傳統 GANs 有著相似的表現;

4. RMSProp 的表現比 Adam 要穩定,因為傳統 GANs 在 G 帶有 BN 層時,使用 RMSProp 優化可以成功學習,但是使用 Adam 優化卻不行。

下面是使用 LSGANs 和 GANs 學習混合高斯分佈的資料集,下圖展現了生成資料分佈的動態結果,可以看到傳統 GAN 在 Step 15k 時就會發生 mode collapse 現象,但 LSGANs 非常成功地學習到了混合高斯分佈

LSGAN:最小二乘生成對抗網路

論文具體實現

筆者使用了 MNIST 資料集進行實驗,具體實現效果如下:

LSGANs:

LSGAN:最小二乘生成對抗網路

GAN:

LSGAN:最小二乘生成對抗網路

從本次用 MNIST 資料訓練的效果來看,LSGANs 生成的效果似乎是比 GAN 的要清晰高質量一些。

總結

LSGANs 是對 GAN 的一次優化,從實驗的情況中,筆者也發現了一些奇怪的現象。我本來是參考論文把判別器 D 的損失值,按真假兩種 loss 加起來一併放入 Adam 中優化,但是無論如何都學習不成功,梯度還是彌散了,最後把 D_fake_loss 和 D_real_loss 分為兩個 program,放入不同的 Adam 中優化判別器D 的引數才達到預期效果。

這篇論文中的思想是非常值得借鑑的,從最小二乘的距離的角度考量,並不是判別器分類之後就完事了,但是 LSGANs 其實還是未能解決判別器足夠優秀的時候,生成器梯度彌散的問題。

關於PaddlePaddle

筆者反饋:幫助文件有點少,而且我本來就直接寫好了想改成使用 GPU 運算,沒找到怎麼改;

PaddlePaddle團隊:關於如何使用 GPU 執行,可以看下執行器 Executor(單 GPU 或單執行緒 CPU 執行器)或 ParallelExecutor(多 GPU 或多執行緒 CPU 執行器,也可以單 GPU/執行緒 CPU 執行)的文件,前者指定 place 為 CUDAPlace,後者介面有個 use_cuda,具體請參考文件。也可以看 models repo 例子,比如 image_classification 或 text_classification 的例子。 

筆者反饋:Program 這個概念有點新穎,一個模型可以有多個 Program,但是我實現的 GAN 可以只用一個,也可以分別放進三個 Program,沒有太瞭解到 Program 這個概念的優越之處,我還是像計算圖那樣使用了,官方也沒給出與 TensorFlow 的對比。

PaddlePaddle團隊:關於 Program 設計可以參考官方文件。這裡提一點,在使用者使用的直觀感受中和 TensorFlow graph 不同的是,凡是放在一個 Program 裡 op,只要執行該 Program,這些 op 就都會執行;而 TensorFlow,指定一個 variable,只執行以該 variable 為葉子節點的 graph,其他多餘 node 不執行,這是最大的使用者感受到的區別。 

至於一個 Program 還是多個 Program,看使用者使用需求而定,多個 Program 時要注意的東西就比較多,例如是否要引數共享等,當然執行多次的時間代價也稍多。 如果是 GAN 也可以參考 models repo 的例子。

小道訊息:聽說全新版本的 PaddlePaddle 已於今日釋出哦。

相關文章