新聞中心
最近,數(shù)據(jù)科學(xué)界大力推廣生成性對(duì)抗網(wǎng)絡(luò)(Generative Adversarial Networks:簡(jiǎn)稱“GAN”)。但是,當(dāng)你開始了解它們時(shí),你馬上就會(huì)明白其中的緣由。GAN架構(gòu)簡(jiǎn)直是一個(gè)天才的設(shè)計(jì),主要是因?yàn)樗搬尫拧绷爽F(xiàn)實(shí)數(shù)據(jù)生成和增強(qiáng)的巨大潛力。在本文中,我將首先向您介紹GAN的基礎(chǔ)知識(shí),并向您展示如何使用Keras/Tensorflow庫(kù)在Python環(huán)境中編寫一個(gè)GAN。歸納起來(lái),主要包括下面幾項(xiàng)內(nèi)容:

- 機(jī)器學(xué)習(xí)算法中的GAN
- GAN架構(gòu)及其工作原理的直觀解釋
- 通過(guò)一個(gè)詳細(xì)的Python示例演示如何從頭開始構(gòu)建GAN
1.機(jī)器學(xué)習(xí)算法中的GAN
即使是經(jīng)驗(yàn)豐富的數(shù)據(jù)科學(xué)家也很容易在數(shù)百種不同的機(jī)器學(xué)習(xí)算法中迷失方向。為了歸納這些算法,我對(duì)一些最常見的算法進(jìn)行了分類,并創(chuàng)建了一個(gè)可視化的旭日?qǐng)D。
機(jī)器學(xué)習(xí)算法分類旭日?qǐng)D
注意,這其中的一些算法相當(dāng)靈活,可以應(yīng)用于不同的任務(wù)。因此,任何分類算法都永遠(yuǎn)不會(huì)是最完美的分類算法。盡管如此,能夠看到一個(gè)如此高層次的視圖仍然有重大意義。注意:原文中展示的圖表是交互式的,只要點(diǎn)擊不同類別的鏈接便可以了解更多對(duì)應(yīng)的提示信息。因此,本譯文中提供的靜態(tài)圖表只能讓各位稍有一些遺憾了。
你會(huì)發(fā)現(xiàn),GAN也不過(guò)是神經(jīng)網(wǎng)絡(luò)的一個(gè)子類,它自身也進(jìn)一步包含多個(gè)不同的子類型,如基本GAN(本文的重點(diǎn))、條件GAN(cGAN)、深度卷積GAN(DCGAN)和我將在未來(lái)文章中介紹的其他類型。
2.GAN架構(gòu)及其工作原理的直觀解釋
生成性對(duì)抗網(wǎng)絡(luò)是深度學(xué)習(xí)機(jī)器,它將兩個(gè)單獨(dú)的模型組合到一個(gè)架構(gòu)中。這兩個(gè)組件是:
- 生成器模型
- 鑒別器模型
這兩個(gè)模型在零和博弈中相互競(jìng)爭(zhēng)。生成器模型嘗試生成與問(wèn)題域中的數(shù)據(jù)樣本相似的新數(shù)據(jù)樣本。同時(shí),鑒別器嘗試識(shí)別給出的樣本是假的(來(lái)自生成器)還是真的(來(lái)自實(shí)際數(shù)據(jù)域)。
生成器和鑒別器之間的競(jìng)爭(zhēng)使它們成為對(duì)手,GAN由此而得名。
3.生成器模型
首先,讓我們分析一下生成器模型,看看它是如何生成新的數(shù)據(jù)樣本的。
生成器模型示意圖
- 生成器模型從潛在空間中采樣一個(gè)隨機(jī)向量。該空間遵循高斯分布,維數(shù)由我們指定。由于我們將隨機(jī)向量用作神經(jīng)網(wǎng)絡(luò)的輸入,因此隨機(jī)向量成為該生成過(guò)程的種子數(shù)據(jù)。
- 輸入遵循一個(gè)或多個(gè)隱藏層的網(wǎng)絡(luò)標(biāo)準(zhǔn)路徑。在簡(jiǎn)單的GAN架構(gòu)的情況下,這將是一組緊密連接的層,而深度卷積GAN(DCGAN)也是包含卷積層的。
- 數(shù)據(jù)流入輸出層,在這里我們可以進(jìn)行最終調(diào)整,以確保生成器輸出結(jié)果數(shù)據(jù)中包含所需的形狀,以饋送到鑒別器。
- 最后,我們可以使用這些假(生成)的樣本來(lái)測(cè)試和“愚弄”(fool)鑒別器。
4.鑒別器模型
接下來(lái),讓我們看看如何構(gòu)造鑒別器模型。
鑒別器模型
- 鑒別器模型的輸入是真實(shí)樣本(從問(wèn)題域中提?。┖吞摷贅颖荆ㄓ缮善髂P蛣?chuàng)建)的組合。
- 數(shù)據(jù)通過(guò)具有一個(gè)或多個(gè)隱藏層的網(wǎng)絡(luò),與任何其他神經(jīng)網(wǎng)絡(luò)中的數(shù)據(jù)相同。3、一旦我們到達(dá)輸出層,鑒別器就能夠決定樣本是真的還是假的(生成的)??傊?,鑒別器與標(biāo)準(zhǔn)神經(jīng)網(wǎng)絡(luò)分類模型沒有什么不同。
5.GAN模型
生成性對(duì)抗網(wǎng)絡(luò)結(jié)合了相互競(jìng)爭(zhēng)的生成器和鑒別器模型。下面的GAN架構(gòu)圖說(shuō)明了兩個(gè)模型是如何互連的。
GAN模型架構(gòu)示意圖
如圖所示,我們將假的(生成的)和真實(shí)的樣本數(shù)據(jù)輸入鑒別器模型,對(duì)其進(jìn)行訓(xùn)練以區(qū)分這兩種類型。
隨著鑒別器更好地區(qū)分真假樣本數(shù)據(jù),生成器模型的權(quán)重和偏差會(huì)得到更新,以使其產(chǎn)生更令人信服的假樣本數(shù)據(jù)。
該過(guò)程將循環(huán)執(zhí)行若干遍(根據(jù)指定次數(shù)的世代),直到生成器和鑒別器在其各自特定任務(wù)中變得更好。最后,在極限情況下,生成器模型的輸出與實(shí)際輸出無(wú)法區(qū)分,鑒別器模型收斂到大約0.5的中性預(yù)測(cè)結(jié)果。
6.從頭開始構(gòu)建一個(gè)基于Python的GAN示例
本示例的目的是讓您從根本上了解GAN的工作原理。因此,我們將其應(yīng)用于一個(gè)簡(jiǎn)單的問(wèn)題。
準(zhǔn)備工作
我們將使用到下面一些庫(kù):
- Pandas、Numpy和Math庫(kù),用于數(shù)據(jù)生成和操縱
- Matplotlib、Graphviz和Plotly(可選),用于數(shù)據(jù)可視化
- Tensorflow/Keras,用于構(gòu)建神經(jīng)網(wǎng)絡(luò)
首先,讓我們導(dǎo)入庫(kù):
# Tensorflow / Keras
from tensorflow import keras #用于構(gòu)建神經(jīng)網(wǎng)絡(luò)
print('Tensorflow/Keras: %s' % keras.__version__) #打印版本
from keras.models import Sequential #用于組裝神經(jīng)網(wǎng)絡(luò)模型
from keras.layers import Dense #給神經(jīng)網(wǎng)絡(luò)模型增添一些層
from tensorflow.keras.utils import plot_model #繪制模型圖
#數(shù)據(jù)操縱
import numpy as np #用于數(shù)據(jù)操縱
print('numpy: %s' % np.__version__) #打印版本
import pandas as pd # 用于數(shù)據(jù)操縱
print('pandas: %s' % pd.__version__) #打印版本
import math #用于生成真實(shí)數(shù)據(jù)(本例中指向一個(gè)圓)
#可視化
import matplotlib
import matplotlib.pyplot as plt #用于數(shù)據(jù)可視化
print('matplotlib: %s' % matplotlib.__version__) #打印版本
import graphviz # for showing model diagram
print('graphviz: %s' % graphviz.__version__) # 打印版本
import plotly
import plotly.express as px # 用于數(shù)據(jù)可視化
print('plotly: %s' % plotly.__version__) # 打印版本
#其他工具
import sys
import os
#把主目錄賦值給一個(gè)變量
main_dir=os.path.dirname(sys.path[0])
以上代碼將打印出本例中使用的包的版本信息,如下所示:
- Tensorflow/Keras: 2.7.0
- numpy: 1.21.4
- pandas: 1.3.4
- matplotlib: 3.5.1
- graphviz: 0.19.1
- plotly: 5.4.0
接下來(lái),我們將創(chuàng)建一個(gè)圓,并獲取其邊緣(圓周)上點(diǎn)的坐標(biāo)。然后,我們將通過(guò)訓(xùn)練生成器和鑒別器,讓GAN“識(shí)別”和“生成”這樣的圓。
#獲取其邊緣(圓周)上點(diǎn)的坐標(biāo)的函數(shù)
def PointsInCircum(r,n=100):
return [(math.cos(2*math.pi/n*x)*r,math.sin(2*math.pi/n*x)*r) for x in range(0,n+1)]
# 保存組成半徑為2的圓的一組實(shí)數(shù)點(diǎn)的坐標(biāo)
circle=np.array(PointsInCircum(r=2,n=1000))
#繪制圖表
plt.figure(figsize=(15,15), dpi=400)
plt.title(label='Real circle to be learned by the GAN generator', loc='center')
plt.scatter(circle[:,0], circle[:,1], s=5, color='black')
plt.show()
上面的代碼將生成1000個(gè)點(diǎn),并繪制一個(gè)圓形。
由1000個(gè)點(diǎn)組成的圓
創(chuàng)建GAN模型
現(xiàn)在,我們已經(jīng)準(zhǔn)備好了數(shù)據(jù)。接下來(lái),讓我們開始定義和組裝我們的模型。我們將從生成器開始:
# 定義生成器模型
def generator(latent_dim, n_outputs=2):
model = Sequential(name="Generator") # 模型
#添加層
model.add(Dense(32, activation='relu', kernel_initializer='he_uniform', input_dim=latent_dim, name='Generator-Hidden-Layer-1')) #隱藏層
model.add(Dense(16, activation='relu', kernel_initializer='he_uniform', name='Generator-Hidden-Layer-2')) #隱藏層
model.add(Dense(n_outputs, activation='linear', name='Generator-Output-Layer')) #輸出層
return model
# 實(shí)例化
latent_dim=3
gen_model = generator(latent_dim)
#顯示模型總結(jié)信息并繪制模型圖
gen_model.summary()
plot_model(gen_model, show_shapes=True, show_layer_names=True, dpi=400)
生成器模型圖
如您所見,我們的生成器有三個(gè)輸入節(jié)點(diǎn),因?yàn)槲覀儧Q定從三維潛在空間中繪制一個(gè)隨機(jī)向量。注意,我們可以自由選擇潛在空間維度。
同時(shí),輸出結(jié)果中顯示了兩個(gè)值,對(duì)應(yīng)于二維空間中的一個(gè)點(diǎn)的x和y坐標(biāo)。接下來(lái),我們建立鑒別器模型:
# 建立鑒別器模型
def discriminator(n_inputs=2):
model = Sequential(name="Discriminator") #模型
#添加層
model.add(Dense(32, activation='relu', kernel_initializer='he_uniform', input_dim=n_inputs, name='Discriminator-Hidden-Layer-1')) #隱藏層
model.add(Dense(16, activation='relu', kernel_initializer='he_uniform', name='Discriminator-Hidden-Layer-2')) #隱藏層
model.add(Dense(1, activation='sigmoid', name='Discriminator-Output-Layer')) # 輸出層
#編譯模型
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
return model
# 實(shí)例化
dis_model = discriminator()
#顯示模型總結(jié)信息并繪制模型圖
dis_model.summary()
plot_model(dis_model, show_shapes=True, show_layer_names=True, dpi=400)
鑒別器模型圖
鑒別器輸入采用兩個(gè)值,與生成器輸出對(duì)齊。同時(shí),鑒別器輸出只是一個(gè)值,告訴我們模型對(duì)數(shù)據(jù)真實(shí)/虛假的信心有多大。接下來(lái),我們結(jié)合這兩個(gè)模型來(lái)創(chuàng)建GAN。下面代碼中的一個(gè)關(guān)鍵細(xì)節(jié)是,我們使鑒別器模型不可訓(xùn)練。我們這樣做是因?yàn)?,我們想使用真?shí)和虛假(生成)數(shù)據(jù)的組合分別訓(xùn)練鑒別器。稍后,您將看到我們是如何做到這一點(diǎn)的。
def def_gan(generator, discriminator):
#我們不想在這個(gè)階段訓(xùn)練鑒別器的權(quán)重。因此,使其不可訓(xùn)練
discriminator.trainable = False
#結(jié)合這兩個(gè)模型
model = Sequential(name="GAN") # GAN 模型
model.add(generator) # 添加生成器
model.add(discriminator) # 添加鑒別器
#編譯模型
model.compile(loss='binary_crossentropy', optimizer='adam')
return model
# 實(shí)例化
gan_model = def_gan(gen_model, dis_model)
#顯示模型總結(jié)信息并繪制模型圖
gan_model.summary()
plot_model(gan_model, show_shapes=True, show_layer_names=True, dpi=400)
GAN模型示意圖
準(zhǔn)備生成器和鑒別器的輸入
我們將創(chuàng)建三個(gè)簡(jiǎn)單的函數(shù),用于輔助我們完成為兩個(gè)模型的采樣與生成數(shù)據(jù)。第一個(gè)函數(shù)的功能是從圓上采樣真實(shí)點(diǎn)數(shù)據(jù);第二個(gè)函數(shù)負(fù)責(zé)從潛在空間中提取隨機(jī)向量;第三個(gè)函數(shù)負(fù)責(zé)將潛在變量傳遞到生成器模型中以生成偽樣本數(shù)據(jù)。#構(gòu)造函數(shù),使負(fù)責(zé)從我們的圓上采集隨機(jī)點(diǎn)數(shù)據(jù)
def real_samples(n):
#真實(shí)樣本數(shù)據(jù)
X = circle[np.random.choice(circle.shape[0], n, replace=True), :]
#類標(biāo)簽
y = np.ones((n, 1))
return X, y
#生成潛在空間上的點(diǎn)數(shù)據(jù);我們將用于后面的生成器的輸入數(shù)據(jù)
def latent_points(latent_dim, n):
#生成潛在空間上的點(diǎn)數(shù)據(jù)
latent_input = np.random.randn(latent_dim * n)
#重新構(gòu)造形狀:使成為網(wǎng)絡(luò)的批輸出
latent_input = latent_input.reshape(n, latent_dim)
return latent_input
#使用生成器生成n個(gè)偽樣本數(shù)據(jù),結(jié)合類標(biāo)簽信息
def fake_samples(generator, latent_dim, n):
#生成潛在空間中的點(diǎn)
latent_output = latent_points(latent_dim, n)
#預(yù)測(cè)輸出(例如生成偽樣本數(shù)據(jù))
X = generator.predict(latent_output)
#創(chuàng)建類標(biāo)簽
y = np.zeros((n, 1))
return X, y
?模型訓(xùn)練和評(píng)估
最后兩個(gè)函數(shù)將幫助我們訓(xùn)練模型,并在指定的時(shí)間間隔評(píng)估結(jié)果數(shù)據(jù)。首先,讓我們創(chuàng)建模型性能評(píng)估函數(shù):
def performance_summary(epoch, generator, discriminator, latent_dim, n=100):
#獲取真實(shí)數(shù)據(jù)的樣本
x_real, y_real = real_samples(n)
#在真實(shí)數(shù)據(jù)上評(píng)估鑒別器
_, real_accuracy = discriminator.evaluate(x_real, y_real, verbose=1)
#獲取假的(生成的)樣本
x_fake, y_fake = fake_samples(generator, latent_dim, n)
#在虛假(生成的)數(shù)據(jù)上評(píng)估鑒別器
_, fake_accuracy = discriminator.evaluate(x_fake, y_fake, verbose=1)
#總結(jié)鑒別器性能
print("Epoch number: ", epoch)
print("Discriminator Accuracy on REAL points: ", real_accuracy)
print("Discriminator Accuracy on FAKE (generated) points: ", fake_accuracy)
#創(chuàng)建二維散點(diǎn)圖以顯示真實(shí)和虛假(生成的)數(shù)據(jù)點(diǎn)
plt.figure(figsize=(4,4), dpi=150)
plt.scatter(x_real[:, 0], x_real[:, 1], s=5, color='black')
plt.scatter(x_fake[:, 0], x_fake[:, 1], s=5, color='red')
plt.show()
如您所見,上述函數(shù)分別對(duì)真實(shí)和虛假(生成)點(diǎn)對(duì)鑒別器進(jìn)行了評(píng)估。然后繪制二維散點(diǎn)圖,以顯示這些點(diǎn)在二維平面上的位置。
最后,訓(xùn)練函數(shù)如下:
def train(g_model, d_model, gan_model, latent_dim, n_epochs=10001, n_batch=256, n_eval=1000):
#我們訓(xùn)練鑒別器的批次將包括一半真實(shí)點(diǎn)和一半假(生成的)點(diǎn)
half_batch = int(n_batch / 2)
#我們使用手動(dòng)方式枚舉世代( epochs )
for i in range(n_epochs):
#訓(xùn)練鑒別器
#準(zhǔn)備真實(shí)樣品數(shù)據(jù)
x_real, y_real = real_samples(half_batch)
#準(zhǔn)備假(生成)樣本數(shù)據(jù)
x_fake, y_fake = fake_samples(g_model, latent_dim, half_batch)
# 使用真實(shí)和虛假樣本訓(xùn)練鑒別器
d_model.train_on_batch(x_real, y_real)
d_model.train_on_batch(x_fake, y_fake)
#生成器訓(xùn)練
# 從潛在空間中獲取用作生成器輸入的點(diǎn)
x_gan = latent_points(latent_dim, n_batch)
# 當(dāng)我們生成假樣本時(shí),我們希望GAN生成器模型創(chuàng)建與真實(shí)樣本相似的樣本
# 因此,我們希望傳遞與真實(shí)樣本對(duì)應(yīng)的標(biāo)簽,即y=1,而不是0。
y_gan = np.ones((n_batch, 1))
# Train the generator via a composite GAN model
gan_model.train_on_batch(x_gan, y_gan)
# Evaluate the model at every n_eval epochs
if (i) % n_eval == 0:
performance_summary(i, g_model, d_model, latent_dim)
如前所述,我們通過(guò)傳遞一批50%真實(shí)和50%虛假(生成)的樣本數(shù)據(jù)來(lái)分別訓(xùn)練鑒別器。同時(shí),生成器訓(xùn)練通過(guò)組合的GAN模型進(jìn)行。
實(shí)驗(yàn)結(jié)果
讓我們調(diào)用訓(xùn)練函數(shù)來(lái)顯示一些上述實(shí)驗(yàn)的結(jié)果:
# 訓(xùn)練GAN模型
train(gen_model, dis_model, gan_model, latent_dim)
下圖展示的是在世代(epoch)0時(shí)期輸出的結(jié)果:
世代0完成后的GAN性能
在世代3,000時(shí)期輸出的結(jié)果:
在世代3,000完成后輸出的結(jié)果
在世代10,000時(shí)期輸出的結(jié)果:
在世代10,000完成后輸出的結(jié)果
我們可以看到生成器在每一步都得到了改進(jìn)。然而,在經(jīng)歷了10000個(gè)世代之后,鑒別器仍然表現(xiàn)良好,能夠識(shí)別大多數(shù)真實(shí)樣本和大部分虛假(生成)樣本。因此,我們可以繼續(xù)將該模型訓(xùn)練到下一個(gè)10000個(gè)世代,以取得更好的結(jié)果。比較上述模型性能的另一種方法是查看真實(shí)和虛假點(diǎn)分布的匯總統(tǒng)計(jì)結(jié)果:
# 生成1000個(gè)偽樣本數(shù)據(jù)
x_fake, y_fake = fake_samples(gen_model, latent_dim, 1000)
df_fake = pd.DataFrame(x_fake, columns=['x dimension', 'y dimension'])
# 1000個(gè)真實(shí)的樣本數(shù)據(jù)點(diǎn)
x_real, y_real = real_samples(1000)
df_real = pd.DataFrame(x_real, columns=['x dimension', 'y dimension'])
#顯示匯總統(tǒng)計(jì)結(jié)果
print("Distribution statistics of fake (generated) points")
print(df_fake.describe())
print("----------------------------------------")
print("Distribution statisticss of real points")
print(df_real.describe())
真實(shí)和虛假(生成)點(diǎn)分布統(tǒng)計(jì)的比較
上述實(shí)驗(yàn)數(shù)據(jù)清楚地表明了:分布差異相對(duì)較小。
7.結(jié)語(yǔ)
我希望閱讀完本文后,你能夠很好地理解了Gan 網(wǎng)絡(luò)的工作原理。
譯者介紹
朱先忠,社區(qū)編輯,專家博客、講師,濰坊一所高校計(jì)算機(jī)教師,自由編程界老兵一枚。早期專注各種微軟技術(shù)(編著成ASP.NET AJX、Cocos 2d-X相關(guān)三本技術(shù)圖書),近十多年投身于開源世界(熟悉流行全棧Web開發(fā)技術(shù)),了解基于OneNet/AliOS+Arduino/ESP32/樹莓派等物聯(lián)網(wǎng)開發(fā)技術(shù)與Scala+Hadoop+Spark+Flink等大數(shù)據(jù)開發(fā)技術(shù)。
原文鏈接:https://towardsdatascience.com/gans-generative-adversarial-networks-an-advanced-solution-for-data-generation-2ac9756a8a99
分享標(biāo)題:生成性對(duì)抗網(wǎng)絡(luò)——數(shù)據(jù)生成的高級(jí)解決方案
標(biāo)題URL:http://m.5511xx.com/article/dhcdghe.html


咨詢
建站咨詢
