# 十三、生成對抗網絡
盡管我在本書中花了很多時間談論分類或估計的網絡,但在本章中,我將向您展示一些具有創建能力的深度神經網絡。 **生成對抗網絡**(**GAN**)通過兩個內部深層網絡之間的內部競爭來學習如何做到這一點,我們將在下面討論。 在**深度卷積生成對抗網絡**(**DCGAN**)的情況下,這是我將在本章中重點介紹的 GAN 類型,該網絡將學習創建類似于訓練數據集的圖像。
我們將在本章介紹以下主題:
* GAN 概述
* 深度卷積 GAN 架構
* GAN 如何失敗
* GAN 的安全選擇
* 使用 Keras GAN 生成 MNIST 圖像
* 使用 Keras GAN 生成 CIFAR-10 圖像
# GAN 概述
生成對抗網絡都是關于生成新內容的。 GAN 能夠學習一些分布并從該分布創建新樣本。 該樣本可能只是我們訓練數據中未出現的直線上的新點,但也可能是非常復雜的數據集中的新點。 GAN 已用于生成新的音樂,聲音和圖像。 根據 Yann LeCun 所說,[《對抗訓練是切片以來最酷的事情》](https://www.quora.com/session/Yann-LeCun/1)。 我不確定切片面包是否特別酷,但是 Yann LeCun 是??一個非常酷的家伙,所以我會信守諾言。 無論如何,GAN 都非常受歡迎,雖然它可能不如我們在業務環境中涵蓋的其他一些主題那么實用,但在我們對深度學習技術的調查中值得考慮。
2014 年,伊恩·古德費洛(Ian Goodfellow)等人。 撰寫了一篇名為[**生成對抗網絡**](https://arxiv.org/pdf/1406.2661.pdf)的論文,提出了使用兩個深度網絡進行對抗訓練的框架,每個都嘗試打敗對方。 該框架由兩個獨立的網絡組成:判別器和生成器。
判別器正在查看來自訓練集的真實數據和來自生成器的假數據。 它的工作是將每一個作為傳入數據實例分類為真實還是偽造。
生成器試圖使判別器誤以為所生成的數據是真實的。
生成器和判別器被鎖定在一個游戲中,它們各自試圖超越彼此。 這種競爭驅使每個網絡不斷改進,直到最終判別器將生成器的輸出與訓練集中的數據區分開。 當生成器和判別器都正確配置時,它們將達到納什均衡,在納什均衡中,兩者都無法找到優勢。
# 深度卷積 GAN 架構
關于 GAN 的論文很多,每篇都提出了新的新穎架構和調整。 但是,它們中的大多數至少在某種程度上基于**深度卷積 GAN**(**DCGAN**)。 在本章的其余部分中,我們將重點介紹這種模型,因為當您采用此處未介紹的新的令人興奮的 GAN 架構(例如**條件 GAN**(**CGAN**),Stack GAN,InfoGAN 或 Wasserstein GAN),或者可能還有一些其他的新變種,您可能會選擇接下來看看。
DCGAN 由 Alex Radford,Luke Metz 和 Soumith Chintala 在論文[《深度卷積生成對抗網絡》](https://arxiv.org/pdf/1511.06434.pdf)中提出。
接下來讓我們看一下 DCGAN 的總體架構。
# 對抗訓練架構
GAN 的整體架構如下圖所示。 生成器和判別器分別是單獨的深度神經網絡,為了易于使用,將它們簡化為黑匣子。 我們將很快介紹它們的各個架構,但首先,我想著重介紹它們的交互方式:

給生成器一個隨機噪聲向量(`z`),并創建一個輸出`G(z)`(對于 DCGAN,這是一個圖像),希望它能欺騙判別器。
判別器既得到實際訓練數據(`X`),又得到生成器輸出`G(z)`。 要做的是確定其輸入實際上是真實的概率`P(X)`。
判別器和生成器都在棧中一起訓練。 隨著一個方面的改進,另一個方面也有所改進,直到希望生成器產生如此好的輸出,從而使判別器不再能夠識別該輸出與訓練數據之間的差異。
當然,在您準備好構建自己的 GAN 之前,我們還要介紹更多細節。 接下來,讓我們更深入地研究生成器。
# 生成器架構
在此示例中,我們使用適合于生成`28 x 28`灰度圖像的層大小,這正是我們稍后在 MNIST 示例中將要執行的操作。 如果您以前沒有使用過生成器,那么生成器的算法可能會有些棘手,因此我們將在遍歷每一層時進行介紹。 下圖顯示了架構:

生成器的輸入只是`100 x 1`的隨機向量,我們將其稱為噪聲向量。 當此噪聲向量是從正態分布生成時,GAN 往往工作得最好。
網絡的第一層是密集的并且完全連接。 它為我們提供了一種建立線性代數的方法,以便最終得到正確的輸出形狀。 對于每個卷積塊,我們最終將第一軸和第二軸(最終將成為圖像的高度和寬度的行和列)加倍,而通道數逐漸縮小到 1。我們最終需要輸出的高度和寬度為 28。因此,我們將需要從`7 x 7 x 128`張量開始,以便它可以移動到`14 x 14`,然后最終是`28 x 28`。 為此,我們將密集層的大小設置為`128 x 7 x 7`神經元或 6,272 單元。 這使我們可以將密集層的輸出重塑為`7 x 7 x 128`。 如果現在看來這還不算什么,請不用擔心,在編寫代碼后,這才有意義。
在完全連接的層之后,事情變得更加簡單。 就像我們一直一樣,我們正在使用卷積層。 但是,這次我們反向使用它們。 我們不再使用最大池來縮減樣本量。 取而代之的是,我們進行上采樣,在學習視覺特征時使用卷積來構建我們的網絡,并最終輸出適當形狀的張量。
通常,生成器中最后一層的激活是雙曲正切,并且訓練圖像矩陣中的元素被歸一化為 -1 和 1 之間。這是我將在整章中提到的眾多 GAN 黑魔法之一。 研究人員已經發現了一些經驗證明可以幫助構建穩定的 GAN 的黑魔法,Soumith Chintala 可以在此 Git 上找到大多數黑客,[而 Soumith Chintala 也是 DCGAN 原始論文的作者之一](https://github.com/soumith/ganhacks)。 深度學習研究的世界無疑是一個很小的領域。
# 判別器架構
判別器的架構更像我們在前幾章中已經看到的。 它實際上只是一個典型的圖像分類器,如下圖所示。 輸出是 Sigmoid 的,因為判別器將預測輸入圖像是真實圖像集的成員的概率。 判別器正在解決二分類問題:

現在,我們已經介紹了 DCGAN 的架構以及它的各個層次,下面讓我們看一下如何訓練框架。
# DCGAN
DCGAN 框架是使用迷你批量來進行訓練的,這與我之前在本書中對網絡進行訓練的方式相同。 但是,稍后在構建代碼時,您會注意到我們正在構建一個訓練循環,該循環明確控制每個更新批量的情況,而不僅僅是調用`models.fit()`方法并依靠 Keras 為我們處理它。 我這樣做是因為 GAN 訓練需要多個模型來更新同一批次中的權重,所以它比我們以前所做的單個參數更新要稍微復雜一些。
對 DCGAN 進行訓練的過程分為兩步,每批次進行一次。
# 步驟 1 – 訓練判別器
批量訓練 DCGAN 的第一步是在實際數據和生成的數據上訓練判別器。 賦予真實數據的標簽顯然是`1`,而用于假數據的標簽則是`0`。
# 步驟 2 – 訓練棧
判別器更新權重后,我們將判別器和生成器一起訓練為一個模型。 這樣做時,我們將使判別器的權重不可訓練,將其凍結在適當的位置,但仍允許判別器將梯度反向傳播到生成器,以便生成器可以更新其權重。
對于訓練過程中的這一步,我們將使用噪聲向量作為輸入,這將導致生成器生成圖像。 判別器將顯示該圖像,并要求預測該圖像是否真實。 下圖說明了此過程:

判別器將提出一些預測,我們可以稱之為`y_hat`。 此棧的`loss`函數將是二元交叉熵,并且我們將`loss`函數的標簽傳遞為 1,我們可以考慮`y`。 如您在本書前面所提到的, `y`和`y_hat`之間的`loss`轉換為梯度,然后通過判別器傳給生成器。 這將更新生成器權重,使它可以從判別者對問題空間的了解中受益,以便它可以學習創建更逼真的生成圖像。
然后重復這兩個步驟,直到生成器能夠創建與訓練集中的數據相似的數據,使得判別器無法再將兩個數據集區分開,這成為了一個猜謎游戲。 判別器。 此時,生成器將不再能夠改進。 當我們找到納什均衡時,就對網絡進行了訓練。
# GAN 如何失敗
至少可以說,訓練 GAN 是一件棘手的事情。 訓練 GAN 失敗的方法有很多種。 實際上,在撰寫本章時,我發現自己大大擴展了褻瀆向量的詞匯量,同時還花了一點時間在云 GPU 上! 在本章稍后向您展示兩個可用的 GAN 之前,讓我們考慮可能發生的故障以及如何修復這些問題。
# 穩定性
訓練 GAN 需要在判別器和生成器之間進行仔細的平衡。 判別器和生成器都在爭奪深度網絡優勢。 另一方面,他們也需要彼此學習和成長。 為了使它起作用,任何一個都不能壓倒另一個。
在不穩定的 GAN 中,判別器可能會使生成器過載,并絕對確定生成器是假的。 損失為零,并且沒有可用于發送到生成器的梯度,因此它不再可以改善。 網絡游戲結束。 解決此問題的最佳方法是降低判別器的學習率。 您也可以嘗試減少整個判別器架構中神經元的數量。 但是,您可能會在訓練過程的后期錯過這些神經元。 最終,調整網絡架構和超參數是避免這種情況的最佳方法。
當然,這可能是相反的方式,如模式崩潰的情況。
# 模式崩潰
**模式崩潰**是 GAN 失敗的類似且相關的方式。 在模式崩潰中,生成器在多模式分布中學習一種模式,并選擇始終使用該方法來利用判別器。 如果您的訓練集中有魚和小貓,并且您的生成器僅生成奇怪的小貓而沒有魚,則您經歷了模式崩潰。 在這種情況下,增加判別器的威力可能會有所幫助。
# GAN 的安全選擇
我之前已經提到過 Soumith Chintala 的 [GAN 黑魔法 Git](https://github.com/soumith/ganhacks),當您試圖使 GAN 穩定時,這是一個很好的起點。 既然我們已經討論了訓練穩定的 GAN 會有多么困難,讓我們來談談一些安全的選擇,這些選擇可能會幫助您成功找到自己的地方。 盡管有很多技巧,但以下是本章中尚未涵蓋的我的主要建議:
* **批量規范**:使用批量規范化時,請為真實數據和偽數據構造不同的微型批量,并分別進行更新。
* **泄漏的 ReLU**:泄漏的 ReLU 是 ReLU 激活函數的變異。 回想一下 ReLU 函數是`f(x) = max(0, x)`。
但是,泄漏的 ReLU 可以表示為:

當設備不工作時,泄漏的 ReLU 允許非常小的非零梯度。 這可以消除消失的梯度,當我們像在判別器和生成器的組合中那樣將多個層堆疊在一起時,這總是一個問題。
* **在生成器中使用丟棄**:這將產生噪聲并防止模式崩潰。
* **使用軟標簽**:對于真實示例,請使用介于 0.7 和 1 之間的標簽,對于偽示例,請使用介于 0 和 0.3 之間的標簽。 這種噪聲有助于保持信息從判別器流向生成器。
在本章的其他地方,我們還將介紹許多其他的 GAN 黑魔法。 但是,我認為在成功實現 GAN 時,這幾項技巧是最重要的。
# 使用 Keras GAN 生成 MNIST 圖像
我們之前曾與 MNIST 合作,但是這次我們將使用 GAN 生成新的 MNIST 圖像。 訓練 GAN 可能需要很長時間。 但是,此問題很小,可以在幾個小時內在大多數筆記本電腦上運行,這是一個很好的例子。 稍后,我們將把這個例子擴展到 CIFAR-10 圖像。
我在這里使用的網絡架構已被許多人發現并進行了優化,包括 DCGAN 論文的作者以及像 ErikLinder-Norén 這樣的人,他是 GAN 實現的優秀集合,稱為 [**Keras GAN**](https://github.com/eriklindernoren/Keras-GAN) 作為我在此處使用的代碼的基礎。 如果您想知道我是如何在這里使用的架構選擇的,這些就是我試圖站在肩膀上的巨人。
# 加載數據集
`MNIST`數據集由 60,000 個手繪數字(從 0 到 9)組成。Keras 為我們提供了一個內置加載程序,可將其分為 50,000 個訓練圖像和 10,000 個測試圖像。 我們將使用以下代碼加載數據集:
```py
from keras.datasets import mnist
def load_data():
(X_train, _), (_, _) = mnist.load_data()
X_train = (X_train.astype(np.float32) - 127.5) / 127.5
X_train = np.expand_dims(X_train, axis=3)
return X_train
```
您可能已經注意到,我沒有返回任何標簽或測試數據集。 我將只使用訓練數據集。 不需要標簽,因為我要使用的唯一標簽是`0`代表假貨,`1`代表真貨。 這些是真實的圖像,因此將在判別器上將它們全部分配為標簽 1。
# 創建生成器
生成器使用了一些新的層,我們將在本節中討論這些層。 首先,有機會略讀以下代碼:
```py
def build_generator(noise_shape=(100,)):
input = Input(noise_shape)
x = Dense(128 * 7 * 7, activation="relu")(input)
x = Reshape((7, 7, 128))(x)
x = BatchNormalization(momentum=0.8)(x)
x = UpSampling2D()(x)
x = Conv2D(128, kernel_size=3, padding="same")(x)
x = Activation("relu")(x)
x = BatchNormalization(momentum=0.8)(x)
x = UpSampling2D()(x)
x = Conv2D(64, kernel_size=3, padding="same")(x)
x = Activation("relu")(x)
x = BatchNormalization(momentum=0.8)(x)
x = Conv2D(1, kernel_size=3, padding="same")(x)
out = Activation("tanh")(x)
model = Model(input, out)
print("-- Generator -- ")
model.summary()
return model
```
我們以前沒有使用過`UpSampling2D`層。 該層將增加輸入張量的行和列,從而使通道保持不變。 它通過重復輸入張量中的值來實現。 默認情況下,它將使輸入加倍。 如果給`UpSampling2D`層一個`7 x 7 x 128`輸入,它將給我們一個`14 x 14 x 128`輸出。
通常,當我們構建一個 CNN 時,我們從一個非常高和寬的圖像開始,并使用卷積層來獲得一個非常深但又不高又不寬的張量。 在這里,我將相反。 我將使用一個密集層并進行重塑,以`7 x 7 x 128`張量開始,然后將其加倍兩次后,剩下`28 x 28`張量。 由于我需要灰度圖像,因此可以使用具有單個單元的卷積層來獲得`28 x 28 x 1`輸出。
這種生成器運算法則有點令人反感,乍一看似乎很尷尬,但是經過幾個小時的痛苦之后,您就會掌握它了!
# 創建判別器
判別符實際上在很大程度上與我之前談到的任何其他 CNN 相同。 當然,我們應該談論一些新事物。 我們將使用以下代碼來構建判別器:
```py
def build_discriminator(img_shape):
input = Input(img_shape)
x =Conv2D(32, kernel_size=3, strides=2, padding="same")(input)
x = LeakyReLU(alpha=0.2)(x)
x = Dropout(0.25)(x)
x = Conv2D(64, kernel_size=3, strides=2, padding="same")(x)
x = ZeroPadding2D(padding=((0, 1), (0, 1)))(x)
x = (LeakyReLU(alpha=0.2))(x)
x = Dropout(0.25)(x)
x = BatchNormalization(momentum=0.8)(x)
x = Conv2D(128, kernel_size=3, strides=2, padding="same")(x)
x = LeakyReLU(alpha=0.2)(x)
x = Dropout(0.25)(x)
x = BatchNormalization(momentum=0.8)(x)
x = Conv2D(256, kernel_size=3, strides=1, padding="same")(x)
x = LeakyReLU(alpha=0.2)(x)
x = Dropout(0.25)(x)
x = Flatten()(x)
out = Dense(1, activation='sigmoid')(x)
model = Model(input, out)
print("-- Discriminator -- ")
model.summary()
return model
```
首先,您可能會注意到形狀奇怪的`zeroPadding2D()`層。 第二次卷積后,我們的張量從`28 x 28 x 3`變為`7 x 7 x 64`。 這一層使我們回到偶數,在行和列的一側都加零,這樣我們的張量現在為`8 x 8 x 64`。
更不尋常的是同時使用批量規范化和丟棄法。 通常,這兩層不能一起使用。 但是,就 GAN 而言,它們似乎確實使網絡受益。
# 創建棧式模型
現在我們已經組裝了`generator`和`discriminator`,我們需要組裝第三個模型,這是兩個模型的棧,在`discriminator`損失的情況下,我們可以用來訓練生成器。
為此,我們可以創建一個新模型,這次使用以前的模型作為新模型中的層,如以下代碼所示:
```py
discriminator = build_discriminator(img_shape=(28, 28, 1))
generator = build_generator()
z = Input(shape=(100,))
img = generator(z)
discriminator.trainable = False
real = discriminator(img)
combined = Model(z, real)
```
注意,在建立模型之前,我們將判別器的訓練屬性設置為`False`。 這意味著對于該模型,在反向傳播期間,我們將不會更新判別器的權重。 正如我們在“棧式訓練”部分中提到的,我們將凍結這些權重,僅將生成器的權重與棧一起移動。 判別器將單獨訓練。
現在,所有模型都已構建,需要對其進行編譯,如以下代碼所示:
```py
gen_optimizer = Adam(lr=0.0002, beta_1=0.5)
disc_optimizer = Adam(lr=0.0002, beta_1=0.5)
discriminator.compile(loss='binary_crossentropy',
optimizer=disc_optimizer,
metrics=['accuracy'])
generator.compile(loss='binary_crossentropy', optimizer=gen_optimizer)
combined.compile(loss='binary_crossentropy', optimizer=gen_optimizer)
```
如果您會注意到,我們將創建兩個自定義 **Adam 優化器**。 這是因為很多時候,我們只想更改判別器或生成器的學習率,從而減慢一個或另一個的學習速度,以至于我們得到一個穩定的 GAN,而后者卻無法勝任另一個。 您還會注意到我正在使用`beta_1 = 0.5`。 這是我發揚光大并取得成功的 DCGAN 原始論文的推薦。 從原始 DCGAN 論文中可以發現,0.0002 的學習率也是一個很好的起點。
# 訓練循環
以前,我們曾很奢侈地在模型上調用`.fit()`,讓 Keras 處理將數據分成小批和為我們訓練的痛苦過程。
不幸的是,因為我們需要為一個批量器對判別器和堆疊模型一起執行單獨的更新,所以我們將不得不用老式的方式來做一些循環。 這就是過去一直做的事情,因此雖然可能需要做更多的工作,但它的確使我感到懷舊。 以下代碼說明了訓練技術:
```py
num_examples = X_train.shape[0]
num_batches = int(num_examples / float(batch_size))
half_batch = int(batch_size / 2)
for epoch in range(epochs + 1):
for batch in range(num_batches):
# noise images for the batch
noise = np.random.normal(0, 1, (half_batch, 100))
fake_images = generator.predict(noise)
fake_labels = np.zeros((half_batch, 1))
# real images for batch
idx = np.random.randint(0, X_train.shape[0], half_batch)
real_images = X_train[idx]
real_labels = np.ones((half_batch, 1))
# Train the discriminator (real classified as ones and
generated as zeros)
d_loss_real = discriminator.train_on_batch(real_images,
real_labels)
d_loss_fake = discriminator.train_on_batch(fake_images,
fake_labels)
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
noise = np.random.normal(0, 1, (batch_size, 100))
# Train the generator
g_loss = combined.train_on_batch(noise, np.ones((batch_size, 1)))
# Plot the progress
print("Epoch %d Batch %d/%d [D loss: %f, acc.: %.2f%%] [G loss:
%f]" %
(epoch,batch, num_batches, d_loss[0], 100 * d_loss[1], g_loss))
if batch % 50 == 0:
save_imgs(generator, epoch, batch)
```
可以肯定,這里發生了很多事情。 和以前一樣,讓我們??逐個細分。 首先,讓我們看一下生成噪聲向量的代碼:
```py
noise = np.random.normal(0, 1, (half_batch, 100))
fake_images = generator.predict(noise)
fake_labels = np.zeros((half_batch, 1))
```
這段代碼生成了一個噪聲向量矩陣(我們之前將其稱為`z`)并將其發送到生成器。 它返回了一組生成的圖像,我稱之為偽圖像。 我們將使用它們來訓練判別器,因此我們要使用的標簽為 0,表示這些實際上是生成的圖像。
注意,這里的形狀是`half_batch x 28 x 28 x 1`。 `half_batch`正是您所想的。 我們將創建一半的生成圖像,因為另一半將是真實數據,我們將在下一步進行組裝。 要獲取真實圖像,我們將在`X_train`上生成一組隨機索引,并將`X_train`的切片用作真實圖像,如以下代碼所示:
```py
idx = np.random.randint(0, X_train.shape[0], half_batch)
real_images = X_train[idx]
real_labels = np.ones((half_batch, 1))
```
是的,在這種情況下,我們正在抽樣更換。 它確實可以解決,但可能不是實現小批量訓練的最佳方法。 但是,它可能是最簡單,最常見的。
由于我們正在使用這些圖像來訓練判別器,并且由于它們是真實圖像,因此我們將它們分配為`1`作為標簽,而不是`0`。 現在我們已經組裝了判別器訓練集,我們將更新判別器。 還要注意,我們沒有使用我們之前討論的軟標簽。 那是因為我想讓事情盡可能地容易理解。 幸運的是,在這種情況下,網絡不需要它們。 我們將使用以下代碼來訓練判別器:
```py
# Train the discriminator (real classified as ones and generated as zeros)
d_loss_real = discriminator.train_on_batch(real_images, real_labels)
d_loss_fake = discriminator.train_on_batch(fake_images, fake_labels)
d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)
```
請注意,這里我使用的是判別符的`train_on_batch()`方法。 這是我第一次在本書中使用此方法。 `train_on_batch()`方法正好執行一輪正向和反向傳播。 每次我們調用它時,它都會從模型的先前狀態更新一次模型。
另請注意,我正在分別對真實圖像和偽圖像進行更新。 這是我先前在“生成器架構”部分中引用的 GAN 黑魔法 Git 上給出的建議。 尤其是在訓練的早期階段,當真實圖像和偽圖像來自完全不同的分布時,如果我們將兩組數據放在同一更新中,則批量歸一化將導致訓練問題。
現在,判別器已經更新,是時候更新生成器了。 這是通過更新組合棧間接完成的,如以下代碼所示:
```py
noise = np.random.normal(0, 1, (batch_size, 100))
g_loss = combined.train_on_batch(noise, np.ones((batch_size, 1)))
```
為了更新組合模型,我們創建了一個新的噪聲矩陣,這次它將與整個批次一樣大。 我們將其用作棧的輸入,這將使生成器生成圖像,并使用判別器評估該圖像。 最后,我們將使用`1`標簽,因為我們想在實際圖像和生成的圖像之間反向傳播誤差。
最后,訓練循環報告`epoch`/`batch`處的判別器和生成器損失,然后每`epoch`中的每 50 批,我們將使用`save_imgs`生成示例圖像并將其保存到磁盤,如以下代碼所示:
```py
print("Epoch %d Batch %d/%d [D loss: %f, acc.: %.2f%%] [G loss: %f]" %
(epoch,batch, num_batches, d_loss[0], 100 * d_loss[1], g_loss))
if batch % 50 == 0:
save_imgs(generator, epoch, batch)
```
`save_imgs`函數使用生成器在運行時創建圖像,因此我們可以看到工作的成果。 我們將使用以下代碼來定義`save_imgs`:
```py
def save_imgs(generator, epoch, batch):
r, c = 5, 5
noise = np.random.normal(0, 1, (r * c, 100))
gen_imgs = generator.predict(noise)
gen_imgs = 0.5 * gen_imgs + 0.5
fig, axs = plt.subplots(r, c)
cnt = 0
for i in range(r):
for j in range(c):
axs[i, j].imshow(gen_imgs[cnt, :, :, 0], cmap='gray')
axs[i, j].axis('off')
cnt += 1
fig.savefig("images/mnist_%d_%d.png" % (epoch, batch))
plt.close()
```
它通過創建噪聲矩陣并檢索圖像矩陣來僅使用生成器。 然后,使用`matplotlib.pyplot`將這些圖像保存到`5 x 5`網格中的磁盤上。
# 模型評估
當您構建深層神經網絡來創建圖像時,好壞有點主觀。 讓我們看一下訓練過程的一些示例,以便您可以親自了解 GAN 如何開始學習如何生成 MNIST。
這是第一個周期的第一批網絡。 顯然,此時生成器對生成 MNIST 并不了解。 只是噪音,如下圖所示:

但是只有 50 個批次,正在發生一些事情,如下面的圖像所示:

在 200 個批次的周期 0 之后,我們幾乎可以看到數字,如下圖所示:

一個完整的周期后,這是我們的生成器。 我認為這些生成的數字看起來不錯,而且我可以看到判別符可能會被它們欺騙。 在這一點上,我們可能會繼續改善一點,但是隨著計算機生成一些令人信服的 MNIST 數字,我們的 GAN 似乎已經發揮了作用,如下圖所示:

盡管大多數代碼是相同的,但在結束本章之前,讓我們再看一個使用彩色圖像的示例。
# 使用 Keras GAN 生成 CIFAR-10 圖像
雖然網絡架構在很大程度上保持不變,但我認為有必要向您展示一個使用彩色圖像的示例,并在 Git 中提供示例,以便在想要將 GAN 應用于您的 GAN 時有一些起點。 自己的數據。
`CIFAR-10`是一個著名的數據集,包含 60,000 張`32 x 32 x 3` RGB 彩色圖像,分布在 10 個類別中。 這些類別是飛機,汽車,鳥類,貓,鹿,狗,青蛙,馬,船和卡車。 希望以后看到生成的圖像時,您可能會看到一些可以想象的東西,就像那些對象。
# 加載 CIFAR-10
加載數據集幾乎完全相同,因為 Keras 還使用以下代碼為`CIFAR-10`提供了一個加載器:
```py
from keras.datasets import cifar10
def load_data():
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
X_train = (X_train.astype(np.float32) - 127.5) / 127.5
return X_train
```
# 創建生成器
生成器需要產生`32 x 32 x 3`圖像。 這需要對我們的網絡架構進行兩項細微更改,您可以在此處看到它們:
```py
input = Input(noise_shape)
x = Dense(128 * 8 * 8, activation="relu")(input)
x = Reshape((8, 8, 128))(x)
x = BatchNormalization(momentum=0.8)(x)
x = UpSampling2D()(x)
x = Conv2D(128, kernel_size=3, padding="same")(x)
x = Activation("relu")(x)
x = BatchNormalization(momentum=0.8)(x)
x = UpSampling2D()(x)
x = Conv2D(64, kernel_size=3, padding="same")(x)
x = Activation("relu")(x)
x = BatchNormalization(momentum=0.8)(x)
x = Conv2D(3, kernel_size=3, padding="same")(x)
out = Activation("tanh")(x)
model = Model(input, out)
```
由于我們需要在 32 處結束,并且我們將兩次上采樣,因此我們應該從 8 開始。這可以通過將密集層及其相應的重塑層從`128 * 7 * 7`更改為`128 * 8 * 8`來輕松實現。
由于我們的圖像現在包含三個通道,因此最后的卷積層也需要包含三個通道,而不是一個。 這里的所有都是它的; 我們現在可以生成彩色圖像!
# 創建判別器
判別符幾乎完全不變。 輸入層需要從`28 x 28 x 1`更改為`32 x 32 x 3`。 另外`ZeroPadding2D`可以毫無問題地刪除,因為沒有它的層算術就可以工作。
# 訓練循環
訓練循環保持不變,區別器構建調用除外,該調用需要與 CIFAR-10 圖像大小相對應的新尺寸,如以下代碼所示:
```py
discriminator = build_discriminator(img_shape=(32, 32, 3))
```
當從一個數據集移動到另一個數據集時,通常會需要調整我們的學習率或網絡架構。 幸運的是,在此示例中并非如此。
# 模型評估
`CIFAR-10`數據集當然更加復雜,并且網絡具有更多的參數。 因此,事情將需要更長的時間。 這是在周期 0(批次 300)中我們的圖像的樣子:

我可能開始看到一些邊緣,但是看起來并不像什么。 但是,如果我們等待幾個周期,我們顯然處在松鼠和怪異的魚類地區。 我們可以看到一些東西正在成形,只是有些模糊,如下圖所示:

下圖顯示了 12 個周期后的生成器:

我看到分辨率很低的鳥,魚,甚至還有飛機和卡車。 當然,我們還有很長的路要走,但是我們的網絡已經學會了創建圖像,這非常令人興奮。
# 總結
在本章中,我們研究了 GAN 以及如何將其用于生成新圖像。 我們學習了一些很好地構建 GAN 的規則,甚至學習了模擬 MNIST 和 CIFAR-10 圖像。 毫無疑問,您可能已經在媒體上看到了一些由 GANs 制作的驚人圖像。 在閱讀了本章并完成了這些示例之后,您將擁有執行相同操作的工具。 我希望您可以采納這些想法并加以調整。 剩下的唯一限制是您自己的想象力,數據和 GPU 預算。
在這本書中,我們涵蓋了深度學習的許多應用,從簡單的回歸到生成對抗網絡。 我對這本書的最大希望是,它可以幫助您實際使用深度學習技術,而其中的許多技術已經存在于學術界和研究領域,而這超出了實踐數據科學家或機器學習工程師的能力。 在此過程中,我希望我能就如何構建更好的深度神經網絡以及何時使用深度網絡(而不是更傳統的模型)提供一些建議。 如果您在這 13 章中一直跟著我,請多多關照。
“我們都是手工藝品的學徒,沒人能成為大師。”
——歐內斯特·海明威
- TensorFlow 1.x 深度學習秘籍
- 零、前言
- 一、TensorFlow 簡介
- 二、回歸
- 三、神經網絡:感知器
- 四、卷積神經網絡
- 五、高級卷積神經網絡
- 六、循環神經網絡
- 七、無監督學習
- 八、自編碼器
- 九、強化學習
- 十、移動計算
- 十一、生成模型和 CapsNet
- 十二、分布式 TensorFlow 和云深度學習
- 十三、AutoML 和學習如何學習(元學習)
- 十四、TensorFlow 處理單元
- 使用 TensorFlow 構建機器學習項目中文版
- 一、探索和轉換數據
- 二、聚類
- 三、線性回歸
- 四、邏輯回歸
- 五、簡單的前饋神經網絡
- 六、卷積神經網絡
- 七、循環神經網絡和 LSTM
- 八、深度神經網絡
- 九、大規模運行模型 -- GPU 和服務
- 十、庫安裝和其他提示
- TensorFlow 深度學習中文第二版
- 一、人工神經網絡
- 二、TensorFlow v1.6 的新功能是什么?
- 三、實現前饋神經網絡
- 四、CNN 實戰
- 五、使用 TensorFlow 實現自編碼器
- 六、RNN 和梯度消失或爆炸問題
- 七、TensorFlow GPU 配置
- 八、TFLearn
- 九、使用協同過濾的電影推薦
- 十、OpenAI Gym
- TensorFlow 深度學習實戰指南中文版
- 一、入門
- 二、深度神經網絡
- 三、卷積神經網絡
- 四、循環神經網絡介紹
- 五、總結
- 精通 TensorFlow 1.x
- 一、TensorFlow 101
- 二、TensorFlow 的高級庫
- 三、Keras 101
- 四、TensorFlow 中的經典機器學習
- 五、TensorFlow 和 Keras 中的神經網絡和 MLP
- 六、TensorFlow 和 Keras 中的 RNN
- 七、TensorFlow 和 Keras 中的用于時間序列數據的 RNN
- 八、TensorFlow 和 Keras 中的用于文本數據的 RNN
- 九、TensorFlow 和 Keras 中的 CNN
- 十、TensorFlow 和 Keras 中的自編碼器
- 十一、TF 服務:生產中的 TensorFlow 模型
- 十二、遷移學習和預訓練模型
- 十三、深度強化學習
- 十四、生成對抗網絡
- 十五、TensorFlow 集群的分布式模型
- 十六、移動和嵌入式平臺上的 TensorFlow 模型
- 十七、R 中的 TensorFlow 和 Keras
- 十八、調試 TensorFlow 模型
- 十九、張量處理單元
- TensorFlow 機器學習秘籍中文第二版
- 一、TensorFlow 入門
- 二、TensorFlow 的方式
- 三、線性回歸
- 四、支持向量機
- 五、最近鄰方法
- 六、神經網絡
- 七、自然語言處理
- 八、卷積神經網絡
- 九、循環神經網絡
- 十、將 TensorFlow 投入生產
- 十一、更多 TensorFlow
- 與 TensorFlow 的初次接觸
- 前言
- 1.?TensorFlow 基礎知識
- 2. TensorFlow 中的線性回歸
- 3. TensorFlow 中的聚類
- 4. TensorFlow 中的單層神經網絡
- 5. TensorFlow 中的多層神經網絡
- 6. 并行
- 后記
- TensorFlow 學習指南
- 一、基礎
- 二、線性模型
- 三、學習
- 四、分布式
- TensorFlow Rager 教程
- 一、如何使用 TensorFlow Eager 構建簡單的神經網絡
- 二、在 Eager 模式中使用指標
- 三、如何保存和恢復訓練模型
- 四、文本序列到 TFRecords
- 五、如何將原始圖片數據轉換為 TFRecords
- 六、如何使用 TensorFlow Eager 從 TFRecords 批量讀取數據
- 七、使用 TensorFlow Eager 構建用于情感識別的卷積神經網絡(CNN)
- 八、用于 TensorFlow Eager 序列分類的動態循壞神經網絡
- 九、用于 TensorFlow Eager 時間序列回歸的遞歸神經網絡
- TensorFlow 高效編程
- 圖嵌入綜述:問題,技術與應用
- 一、引言
- 三、圖嵌入的問題設定
- 四、圖嵌入技術
- 基于邊重構的優化問題
- 應用
- 基于深度學習的推薦系統:綜述和新視角
- 引言
- 基于深度學習的推薦:最先進的技術
- 基于卷積神經網絡的推薦
- 關于卷積神經網絡我們理解了什么
- 第1章概論
- 第2章多層網絡
- 2.1.4生成對抗網絡
- 2.2.1最近ConvNets演變中的關鍵架構
- 2.2.2走向ConvNet不變性
- 2.3時空卷積網絡
- 第3章了解ConvNets構建塊
- 3.2整改
- 3.3規范化
- 3.4匯集
- 第四章現狀
- 4.2打開問題
- 參考
- 機器學習超級復習筆記
- Python 遷移學習實用指南
- 零、前言
- 一、機器學習基礎
- 二、深度學習基礎
- 三、了解深度學習架構
- 四、遷移學習基礎
- 五、釋放遷移學習的力量
- 六、圖像識別與分類
- 七、文本文件分類
- 八、音頻事件識別與分類
- 九、DeepDream
- 十、自動圖像字幕生成器
- 十一、圖像著色
- 面向計算機視覺的深度學習
- 零、前言
- 一、入門
- 二、圖像分類
- 三、圖像檢索
- 四、對象檢測
- 五、語義分割
- 六、相似性學習
- 七、圖像字幕
- 八、生成模型
- 九、視頻分類
- 十、部署
- 深度學習快速參考
- 零、前言
- 一、深度學習的基礎
- 二、使用深度學習解決回歸問題
- 三、使用 TensorBoard 監控網絡訓練
- 四、使用深度學習解決二分類問題
- 五、使用 Keras 解決多分類問題
- 六、超參數優化
- 七、從頭開始訓練 CNN
- 八、將預訓練的 CNN 用于遷移學習
- 九、從頭開始訓練 RNN
- 十、使用詞嵌入從頭開始訓練 LSTM
- 十一、訓練 Seq2Seq 模型
- 十二、深度強化學習
- 十三、生成對抗網絡
- TensorFlow 2.0 快速入門指南
- 零、前言
- 第 1 部分:TensorFlow 2.00 Alpha 簡介
- 一、TensorFlow 2 簡介
- 二、Keras:TensorFlow 2 的高級 API
- 三、TensorFlow 2 和 ANN 技術
- 第 2 部分:TensorFlow 2.00 Alpha 中的監督和無監督學習
- 四、TensorFlow 2 和監督機器學習
- 五、TensorFlow 2 和無監督學習
- 第 3 部分:TensorFlow 2.00 Alpha 的神經網絡應用
- 六、使用 TensorFlow 2 識別圖像
- 七、TensorFlow 2 和神經風格遷移
- 八、TensorFlow 2 和循環神經網絡
- 九、TensorFlow 估計器和 TensorFlow HUB
- 十、從 tf1.12 轉換為 tf2
- TensorFlow 入門
- 零、前言
- 一、TensorFlow 基本概念
- 二、TensorFlow 數學運算
- 三、機器學習入門
- 四、神經網絡簡介
- 五、深度學習
- 六、TensorFlow GPU 編程和服務
- TensorFlow 卷積神經網絡實用指南
- 零、前言
- 一、TensorFlow 的設置和介紹
- 二、深度學習和卷積神經網絡
- 三、TensorFlow 中的圖像分類
- 四、目標檢測與分割
- 五、VGG,Inception,ResNet 和 MobileNets
- 六、自編碼器,變分自編碼器和生成對抗網絡
- 七、遷移學習
- 八、機器學習最佳實踐和故障排除
- 九、大規模訓練
- 十、參考文獻