生成對抗網絡的簡單介紹(TensorFlow 代碼)

原文地址:

An introduction to Generative Adversarial Networks (with code in TensorFlow)

引言

最近,研究者們對生成模型的興趣一直很大(參見OpenAI的這篇博客文章)。這些生成模型是可以學習創建類似於我們給它們的數據。這樣的直觀感受是,如果我們可以得到一個能寫出高質量的新聞文章的模型,那麼它一般也會學到很多關於新聞文章的內容。換句話說,這個模型也應該有一個關於新聞文章的很好的內部表示。然後,我們可以希望使用這種表示來幫助我們進行其他相關任務,例如按主題分類新聞文章。

實際上,像這樣製作數據的訓練模式並不容易,但是近年來,出現了一些能夠運行得很好的方法。其中之一便是生成對抗網絡(GAN)。着名的深度學習研究人員和Facebook AI研究主管Yann LeCun最近引用GAN作爲深度學習中最重要的新發展之一

“There are many interesting recent development in deep learning…The most important one, in my opinion, is adversarial training (also called GAN for Generative Adversarial Networks). This, and the variations that are now being proposed is the most interesting idea in the last 10 years in ML, in my opinion.” – Yann LeCun

判別模型與生成模型

在看GAN之前,讓我們簡單回顧一下生成模式和判別模型之間的區別:

  • 判別模型學習將輸入數據(x)映射到某個所需輸出類標籤(y)的函數。從概率出發,它們直接學習條件分佈Py|x
  • 生成模型嘗試同時學習輸入數據和標籤的聯合概率,即Pxy 。這可以通過貝葉斯規則轉換爲Py|x 進行分類,但生成能力也可以用於別的東西,例如創建可能的新xy 樣本。

兩種類型的模型都是有用的,但是生成模型比判別模型有一個有趣的優勢 - 即使沒有標籤,它們也有潛力理解和解釋輸入數據的基礎結構。在現實世界中處理數據建模問題時,這是非常可取的,因爲未標記的數據當然是豐富的,但是獲得標籤數據通常是非常昂貴,不切實際的。

生成對抗網絡

GANs是一個有趣的想法,由德國蒙特利爾大學的Ian Goodfellow(現OpenAI)領導的一組研究人員於2014年首次推出。 GAN的主要思想是擁有兩個競爭的神經網絡模型。 一個將噪聲數據作爲輸入,併產生樣本(所謂的生成器)。 另一個模型(稱爲判別器)從生成器和訓練數據接收樣本,並且必須能夠區分兩個來源。 這兩個網絡進行連續的博弈,生成器學習產生越來越多的現實樣本,鑑別器正在學習越來越好地區分生成的數據和實際數據。 這兩個網絡同時進行訓練,最後的希望是競爭能夠使生成器生成的樣本與實際數據不可區分。

這裏寫圖片描述

GAN overview. Source: https://ishmaelbelghazi.github.io/ALI

這裏經常使用的類比是,生成器就像僞造的一些物品,而判別器就像警方試圖檢測僞造的物品。這種設置也可能似乎讓人聯想到強化學習,其中生成器從判別器接收到獎勵信號,讓它知道生成的數據是否準確。然而,與GAN的關鍵區別在於,我們可以將梯度信息從判別器反向傳播回生成器網絡,因此生成器知道如何調整其參數,以產生可以欺騙判別器的輸出數據。

到目前爲止,GAN主要應用於建模自然圖像。他們現在在圖像生成任務中產生出色的結果,產生的圖像比基於最大可能性訓練目標的其他領先的生成方法訓練有素的圖像更加尖銳。以下是GAN生成的圖像示例:

Generated bedrooms. Source: “Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks”

Generated bedrooms. Source: “Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks” https://arxiv.org/abs/1511.06434v2*

這裏寫圖片描述

Generated CIFAR-10 samples. Source: “Improved Techniques for Training GANs” https://arxiv.org/abs/1606.03498*

近似一維高斯分佈

爲了更好地瞭解這一切如何工作,我們將在TensorFlow中使用GAN來解決一個簡單的問題 - 學習近似一維高斯分佈。 這是基於Eric Jang的類似目標的博文。 我們的演示的完整源代碼可以在Github(https://github.com/AYLIEN/gan-intro)上找到,在這裏我們將專注於一些更有趣的部分代碼。

首先我們創建“真實”數據分佈,一個簡單的高斯,平均值爲4,標準偏差爲0.5。 它具有一個樣本函數,它從分佈返回給定數量的樣本(按值排序)。

    class DataDistribution(object):
        def init(self):
            self.mu = 4
            self.sigma = 0.5
        def sample(self, N):
            samples = np.random.normal(self.mu, self.sigma, N)
            samples.sort()
            return samples 

我們將嘗試學習的數據分佈如下所示:

這裏寫圖片描述

我們還定義生成器輸入噪聲分佈(具有相似的採樣功能)。 在Eric Jang的例子之後,我們還採用分層採樣方法對生成器輸入噪聲進行分析 - 樣本首先在指定範圍內均勻生成,然後隨機擾動。

  class GeneratorDistribution(object):
        def __init__(self, range):
            self.range = range

        def sample(self, N):
            return np.linspace(-self.range, self.range, N) + \
                np.random.random(N) * 0.01

我們的生成器和判別器網絡非常簡單。 生成器是通過非線性(softplus函數)的線性變換,接着是另一個線性變換。

    def generator(input, hidden_size):
        h0 = tf.nn.softplus(linear(input, hidden_size, 'g0'))
        h1 = linear(h0, 1, 'g1')
        return h1

在這種情況下,我們發現重要的是確保判別器比發生器更強大,否則它們沒有足夠的能力學習從而準確區分生成的和實際的樣本。 所以我們做了一個更深層的神經網絡,具有更大的維度。 它使用除了最後一個層之外的所有層中的tanh非線性,其是sigmoid(其輸出可以被解釋爲概率)。

   def discriminator(input, hidden_size):
        h0 = tf.tanh(linear(input, hidden_size * 2, 'd0'))
        h1 = tf.tanh(linear(h0, hidden_size * 2, 'd1'))
        h2 = tf.tanh(linear(h1, hidden_size * 2, 'd2'))
        h3 = tf.sigmoid(linear(h2, 1, 'd3'))
        return h3

然後,我們可以在TensorFlow圖中連接這些部件。 我們還爲每個網絡定義損失函數,生成器的目的是簡單地愚弄判別器。

  with tf.variable_scope('G'):
        z = tf.placeholder(tf.float32, shape=(None, 1))
        G = generator(z, hidden_size)

    with tf.variable_scope('D') as scope:
        x = tf.placeholder(tf.float32, shape=(None, 1))
        D1 = discriminator(x, hidden_size)
        scope.reuse_variables()
        D2 = discriminator(G, hidden_size)

    loss_d = tf.reduce_mean(-tf.log(D1) - tf.log(1 - D2))
    loss_g = tf.reduce_mean(-tf.log(D2))

我們使用TensorFlow中的普通GradientDescentOptimizer以指數學習速率衰減爲每個網絡創建優化器。 我們還應該注意,在這裏找到好的優化參數需要一些調整。

    def optimizer(loss, var_list):
        initial_learning_rate = 0.005
        decay = 0.95
        num_decay_steps = 150
        batch = tf.Variable(0)
        learning_rate = tf.train.exponential_decay(
            initial_learning_rate,
            batch,
            num_decay_steps,
            decay,
            staircase=True
        )
        optimizer = GradientDescentOptimizer(learning_rate).minimize(
            loss,
            global_step=batch,
            var_list=var_list
        )
        return optimizer

    vars = tf.trainable_variables()
    d_params = [v for v in vars if v.name.startswith('D/')]
    g_params = [v for v in vars if v.name.startswith('G/')]

    opt_d = optimizer(loss_d, d_params)
    opt_g = optimizer(loss_g, g_params)

爲了訓練模型,我們從數據分佈和噪聲分佈中抽取樣本,並優化判別器和生成器的參數。

  with tf.Session() as session:
        tf.initialize_all_variables().run()

        for step in xrange(num_steps):
            # update discriminator
            x = data.sample(batch_size)
            z = gen.sample(batch_size)
            session.run([loss_d, opt_d], {
                x: np.reshape(x, (batch_size, 1)),
                z: np.reshape(z, (batch_size, 1))
            })

            # update generator
            z = gen.sample(batch_size)
            session.run([loss_g, opt_g], {
                z: np.reshape(z, (batch_size, 1))
            })

以下動畫顯示了生成器如何在訓練過程中學習如何近似數據分佈:

https://youtu.be/mObnwR-u8pc

我們可以看到,在訓練過程開始時,生成器正在產生與實際數據非常不同的分佈。 它最終終於學會了相當接近與真實數據(在750幀附近),然後收斂到一個較窄的分佈集中在輸入分佈的平均值。 訓練後,這兩個分佈看起來像這樣:

這裏寫圖片描述

這是很直觀的。 生成器正在從實際數據和我們的判別器中查看各個樣本。 如果生成器只是在這個簡單的例子中產生實際數據的平均值,那麼很可能會愚弄判別器。

這個問題有很多可能的解決方案。 在這種情況下,我們可以添加一些提前停止的標準,當達到兩個分佈之間的相似性閾值時暫停訓練。 然而,如果將這個概念化爲更大的問題,即使在簡單的情況下也可能難以保證,我們的生成器將始終達到提前停止的意義。 一個更有吸引力的解決方案是通過給判別者一次性檢查多個示例的能力來直接解決問題。

提高樣本多樣性

根據Tim Salimans和OpenAI的合作者最近的一篇文章,生成器崩潰到其輸出非常窄的點分佈的參數設置的問題是GAN的主要失敗模式之一。 幸運的是,他們還提出了一個解決方案:允許判別器同時查看多個樣本,這是一種稱之爲“小批量判別”的技術。

在這篇文章中,小批量判別被定義爲任何判別器能夠查看整批樣本以便確定它們是來自生成器還是實際數據的方法。 他們還提出了一種更具體的算法,通過建模給定樣品與同一批次中的所有其他樣品之間的距離來工作。 然後將這些距離與原始樣品組合並通過鑑別器,因此可以選擇在分類過程中使用距離測量值和樣品值。

該方法可以大致地總結如下:

  • 取出判別器的一些中間層的輸出。
  • 將其乘以3D張量以產生矩陣(在下面的代碼中大小爲num_kernels x kernel_dim)。
  • 在批量中的所有樣本之間計算該矩陣中的行之間的L1距離,然後應用負指數。
  • 樣本的minibatch 特徵是這些取冪距離的總和。
  • 將原始輸入連接到新創建的最小匹配特徵的最小匹配層(前一個判別層的輸出),並將其作爲輸入傳遞給判別的下一層。

在TensorFlow中:

  def minibatch(input, num_kernels=5, kernel_dim=3):
        x = linear(input, num_kernels * kernel_dim)
        activation = tf.reshape(x, (-1, num_kernels, kernel_dim))
        diffs = tf.expand_dims(activation, 3) - \
            tf.expand_dims(tf.transpose(activation, [1, 2, 0]), 0)
        abs_diffs = tf.reduce_sum(tf.abs(diffs), 2)
        minibatch_features = tf.reduce_sum(tf.exp(-abs_diffs), 2)
        return tf.concat(1, [input, minibatch_features])

我們實施了這種 minibatch discrimination技術,看看它是否有助於我們的示例中生成器輸出分佈的崩潰。 訓練期間生成器網絡的新行爲如下所示。

https://youtu.be/0r3g7-4bMYU

在這種情況下,很明顯,添加 minibatch discrimination會導致生成器維持原始數據分佈的大部分寬度(儘管仍然不完美)。 收斂後,分佈現在看起來像這樣:

這裏寫圖片描述

minibatch discrimination的最後一點是,使批量大小作爲超參數更爲重要。 在我們的示例中,我們不得不保持批量相當小(不到16個左右)進行訓練。 也許僅僅限制對每個距離測量有貢獻的樣本數量,而不是使用完整批次,但是再次調整另一個參數就足夠了。

最後的想法

生成對抗網絡是一個有趣的發展,爲我們提供了一種新的無監督學習方法。 GAN的大部分成功應用一直處於計算機視覺領域,但在這裏,我們正在研究將這些技術應用於自然語言處理的方法。如果您正在處理相同的想法,並希望比較想法,請聯繫我們。

在這方面的一個大問題是如何最好地評估這些模型。在圖像域中,至少看看生成的樣本是很容易的,雖然這顯然不是令人滿意的解決方案。在文本領域,這甚至不太有用(除非你的目標是產生散文)。使用基於最大似然訓練的生成模型,我們通常可以根據看不見的測試數據的可能性(或可能性的一些下限)產生一些度量,但這在這裏是不適用的。一些GAN論文根據生成的樣本的核密度估計值產生了似然估計,但是這種技術似乎在較高維空間中分解。另一個解決方案是隻評估一些下游任務(如分類)。如果您有任何其他建議,我們很樂意聽到您的意見。

更多信息

如果您想了解更多有關GAN的信息,我們建議您從以下出版物開始:

  • Generative Adversarial Networks
  • Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks
  • InfoGAN: Interpretable Representation Learning by Information Maximizing Generative Adversarial Nets
  • Improved Techniques for Training GANs

隨意重用我們的GAN代碼,當然還要關注我們的博客。 歡迎評論,更正和反饋。

原文地址:

An introduction to Generative Adversarial Networks (with code in TensorFlow)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章