(第二部分:深度學(xué)習(xí))
第10章 使用Keras搭建人工神經(jīng)網(wǎng)絡(luò)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)
第12章 使用TensorFlow自定義模型并訓(xùn)練
第13章 使用TensorFlow加載和預(yù)處理數(shù)據(jù)
第14章 使用卷積神經(jīng)網(wǎng)絡(luò)實現(xiàn)深度計算機視覺
第15章 使用RNN和CNN處理序列
第16章 使用RNN和注意力機制進行自然語言處理
第17章 使用自編碼器和GAN做表征學(xué)習(xí)和生成式學(xué)習(xí)
第18章 強化學(xué)習(xí)
第19章 規(guī)奶钗铮化訓(xùn)練和部署TensorFlow模型
自編碼器是能夠在無監(jiān)督(即纹腌,訓(xùn)練集是未標記)的情況下學(xué)習(xí)輸入數(shù)據(jù)的緊密表征(叫做潛在表征或編碼)的人工神經(jīng)網(wǎng)絡(luò)。這些編碼通常具有比輸入數(shù)據(jù)低得多的維度滞磺,使得自編碼器對降維有用(參見第 8 章)升薯。自編碼器還可以作為強大的特征檢測器,它們可以用于無監(jiān)督的深度神經(jīng)網(wǎng)絡(luò)預(yù)訓(xùn)練(正如我們在第 11 章中討論過的)雁刷。最后覆劈,一些自編碼器是生成式模型:他們能夠隨機生成與訓(xùn)練數(shù)據(jù)非常相似的新數(shù)據(jù)。例如沛励,您可以在臉圖片上訓(xùn)練自編碼器责语,然后可以生成新臉。但是生成出來的圖片通常是模糊且不夠真實目派。
相反坤候,用對抗生成網(wǎng)絡(luò)(GAN)生成的人臉可以非常逼真,甚至讓人認為他們是真實存在的人企蹭。你可以去這個網(wǎng)址https://thispersondoesnotexist.com/白筹,這是用StyleGAN生成的人臉,自己判斷一下(還可以去https://thisrentaldoesnotexist.com/谅摄,看看GAN生成的臥室圖片)徒河,GAN現(xiàn)在廣泛用于超清圖片涂色,圖片編輯送漠,將草圖變?yōu)檎掌缯眨鰪姅?shù)據(jù)集,生成其它類型的數(shù)據(jù)(比如文本闽寡、音頻代兵、時間序列),找出其它模型的缺點并強化爷狈,等等植影。
自編碼器和GAN都是無監(jiān)督的,都可以學(xué)習(xí)緊密表征涎永,都可以用作生成模型思币,有許多相似的應(yīng)用鹿响,但原理非常不同:
自編碼器是通過學(xué)習(xí),將輸入復(fù)制到輸出支救。聽起來很簡單抢野,但內(nèi)部結(jié)構(gòu)會使其相當困難。例如各墨,你可以限制潛在表征的大小,或者可以給輸入添加噪音启涯,訓(xùn)練模型恢復(fù)原始輸入贬堵。這些限制組織自編碼器直接將輸入復(fù)制到輸出,可以強迫模型學(xué)習(xí)數(shù)據(jù)的高效表征结洼±枳觯總而言之,編碼是自編碼器在一些限制下學(xué)習(xí)恒等函數(shù)的副產(chǎn)品松忍。
GAN包括兩個神經(jīng)網(wǎng)絡(luò):一個生成器嘗試生成和訓(xùn)練數(shù)據(jù)相似的數(shù)據(jù)蒸殿,一個判別器來區(qū)分真實數(shù)據(jù)和假數(shù)據(jù)。特別之處在于鸣峭,生成器和判別器在訓(xùn)練過程中彼此競爭:生成器就像一個制造偽鈔的罪犯宏所,而判別器就像警察一樣,要把真錢挑出來摊溶。對抗訓(xùn)練(訓(xùn)練競爭神經(jīng)網(wǎng)絡(luò))爬骤,被認為是近幾年的一大進展。在2016年莫换,Yann LeCun甚至說GAN是過去10年機器學(xué)習(xí)領(lǐng)域最有趣的發(fā)明霞玄。
本章中,我們先探究自編碼器的工作原理開始拉岁,如何做降維坷剧、特征提取、無監(jiān)督預(yù)訓(xùn)練將喊暖、如何用作生成式模型惫企。然后過渡到GAN。先用GAN生成假圖片哄啄,可以看到訓(xùn)練很困難雅任。會討論對抗訓(xùn)練的主要難點,以及一些解決方法咨跌。先從自編碼器開始沪么。
有效的數(shù)據(jù)表征
以下哪一個數(shù)字序列更容易記憶?
- 40, 27, 25, 36, 81, 57, 10, 73, 19, 68
- 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14
乍一看锌半,第一個序列似乎應(yīng)該更容易禽车,因為它要短得多寇漫。 但是,如果仔細觀察第二個序列殉摔,就會發(fā)現(xiàn)它是從50到14的偶數(shù)州胳。一旦你注意到這個規(guī)律,第二個序列比第一個更容易記憶逸月,因為你只需要記住規(guī)律就成栓撞,開始的數(shù)字和結(jié)尾的數(shù)字。請注意碗硬,如果您可以快速輕松地記住非常長的序列瓤湘,則不會在意第二個序列中存在的規(guī)律。 只要記住每一個數(shù)字恩尾,就夠了弛说。 事實上,很難記住長序列翰意,因此識別規(guī)律非常有用木人,并且希望能夠澄清為什么在訓(xùn)練過程中限制自編碼器會促使它發(fā)現(xiàn)并利用數(shù)據(jù)中的規(guī)律。
記憶冀偶、感知和模式匹配之間的關(guān)系在 20 世紀 70 年代早期由 William Chase 和 Herbert Simon 研究醒第。 他們觀察到,專業(yè)棋手能夠通過觀看棋盤5秒鐘就能記住所有棋子的位置蔫磨,這是大多數(shù)人認為不可能完成的任務(wù)淘讥。 然而,只有當這些棋子被放置在現(xiàn)實位置(來自實際比賽)時才是這種情況堤如,而不是隨機放置棋子蒲列。 國際象棋專業(yè)棋手沒有比你更好的記憶,他們只是更容易看到國際象棋的規(guī)律搀罢,這要歸功于他們的比賽經(jīng)驗蝗岖。 觀察規(guī)律有助于他們有效地存儲信息。
就像這個記憶實驗中的象棋棋手一樣榔至,一個自編碼器會查看輸入信息抵赢,將它們轉(zhuǎn)換為高效的潛在表征,然后輸出一些(希望)看起來非常接近輸入的東西唧取。 自編碼器總是由兩部分組成:將輸入轉(zhuǎn)換為潛在表征的編碼器(或識別網(wǎng)絡(luò))铅鲤,然后是將潛在表征轉(zhuǎn)換為輸出的解碼器(或生成網(wǎng)絡(luò))(見圖 17-1)。
如你所見枫弟,自編碼器通常具有與多層感知器(MLP邢享,請參閱第 10 章)相同的體系結(jié)構(gòu),但輸出層中的神經(jīng)元數(shù)量必須等于輸入數(shù)量淡诗。 在這個例子中骇塘,只有一個由兩個神經(jīng)元(編碼器)組成的隱藏層和一個由三個神經(jīng)元(解碼器)組成的輸出層伊履。由于自編碼器試圖重構(gòu)輸入,所以輸出通常被稱為重建款违,并且損失函數(shù)包含重建損失唐瀑,當重建與輸入不同時,重建損失會對模型進行懲罰插爹。
由于內(nèi)部表征具有比輸入數(shù)據(jù)更低的維度(它是 2D 而不是 3D)哄辣,所以自編碼器被認為是不完整的。 不完整的自編碼器不能簡單地將其輸入復(fù)制到編碼赠尾,但它必須找到一種方法來輸出其輸入的副本柔滔。 它被迫學(xué)習(xí)輸入數(shù)據(jù)中最重要的特征(并刪除不重要的特征)。
我們來看看如何實現(xiàn)一個非常簡單的不完整的自編碼器萍虽,以降低維度。
用不完整的線性自編碼器來做PCA
如果自編碼器僅使用線性激活并且損失函數(shù)是均方誤差(MSE)形真,最終其實是做了主成分分析(參見第 8 章)杉编。
以下代碼創(chuàng)建了一個簡單的線性自編碼器,以在 3D 數(shù)據(jù)集上執(zhí)行 PCA咆霜,并將其投影到 2D:
from tensorflow import keras
encoder = keras.models.Sequential([keras.layers.Dense(2, input_shape=[3])])
decoder = keras.models.Sequential([keras.layers.Dense(3, input_shape=[2])])
autoencoder = keras.models.Sequential([encoder, decoder])
autoencoder.compile(loss="mse", optimizer=keras.optimizers.SGD(lr=0.1))
這段代碼與我們在前面章節(jié)中創(chuàng)建的所有 MLP 沒有什么大不同邓馒。只有以下幾點要注意:
自編碼器由兩部分組成:編碼器和解碼器。兩者都是常規(guī)的
Sequential
模型蛾坯,每個含有一個緊密層光酣,自編碼器是一個編碼器和解碼器連起來的Sequential
模型(模型可以用作其它模型中的層)。自編碼器的輸出等于輸入脉课。
簡單PCA不需要激活函數(shù)(即救军,所有神經(jīng)元是線性的),且損失函數(shù)是MSE倘零。后面會看到更復(fù)雜的自編碼器唱遭。
現(xiàn)在用生成出來的3D數(shù)據(jù)集訓(xùn)練模型,并用模型編碼數(shù)據(jù)集(即將其投影到 2D):
history = autoencoder.fit(X_train, X_train, epochs=20)
codings = encoder.predict(X_train)
注意呈驶,X_train
既用來做輸入拷泽,也用來做目標。圖 17-2 顯示了原始 3D 數(shù)據(jù)集(左側(cè))和自編碼器隱藏層的輸出(即編碼層袖瞻,右側(cè))司致。 可以看到,自編碼器找到了投影數(shù)據(jù)的最佳二維平面聋迎,保留了數(shù)據(jù)的盡可能多的差異(就像 PCA 一樣)脂矫。
筆記:可以將自編碼器當做某種形式的自監(jiān)督學(xué)習(xí)(帶有自動生成標簽功能的監(jiān)督學(xué)習(xí),這個例子中標簽等于輸入)
棧式自編碼器
就像我們討論過的其他神經(jīng)網(wǎng)絡(luò)一樣砌庄,自編碼器可以有多個隱藏層羹唠。 在這種情況下奕枢,它們被稱為棧式自編碼器(或深度自編碼器)。 添加更多層有助于自編碼器了解更復(fù)雜的編碼佩微。 但是缝彬,必須注意不要讓自編碼器功能太強大。 設(shè)想一個編碼器非常強大哺眯,只需學(xué)習(xí)將每個輸入映射到一個任意數(shù)字(并且解碼器學(xué)習(xí)反向映射)即可谷浅。 很明顯,這樣的自編碼器將完美地重構(gòu)訓(xùn)練數(shù)據(jù)奶卓,但它不會在過程中學(xué)習(xí)到任何有用的數(shù)據(jù)表征(并且它不可能很好地泛化到新的實例)一疯。
棧式自編碼器的架構(gòu)以中央隱藏層(編碼層)為中心通常是對稱的。 簡單來說夺姑,它看起來像一個三明治墩邀。 例如,一個用于 MNIST 的自編碼器(在第 3 章中介紹)可能有 784 個輸入盏浙,其次是一個隱藏層眉睹,有 100 個神經(jīng)元,然后是一個中央隱藏層废膘,有 30 個神經(jīng)元竹海,然后是另一個隱藏層,有 100 個神經(jīng)元丐黄,輸出層有 784 個神經(jīng)元斋配。 這個棧式自編碼器如圖 17-3 所示。
用Keras實現(xiàn)棧式自編碼器
你可以像常規(guī)深度 MLP 一樣實現(xiàn)棧式自編碼器灌闺。 特別是艰争,我們在第 11 章中用于訓(xùn)練深度網(wǎng)絡(luò)的技術(shù)也可以應(yīng)用。例如菩鲜,下面的代碼使用 SELU 激活函數(shù)為Fashion MNIST 創(chuàng)建了一個棧式自編碼器:
stacked_encoder = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
keras.layers.Dense(100, activation="selu"),
keras.layers.Dense(30, activation="selu"),
])
stacked_decoder = keras.models.Sequential([
keras.layers.Dense(100, activation="selu", input_shape=[30]),
keras.layers.Dense(28 * 28, activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
stacked_ae = keras.models.Sequential([stacked_encoder, stacked_decoder])
stacked_ae.compile(loss="binary_crossentropy",
optimizer=keras.optimizers.SGD(lr=1.5))
history = stacked_ae.fit(X_train, X_train, epochs=10,
validation_data=[X_valid, X_valid])
逐行看下這個代碼:
和之前一樣园细,自編碼器包括兩個子模塊:編碼器和解碼器。
編碼器接收28 × 28像素的灰度圖片接校,打平為大小等于784的矢量猛频,用兩個緊密層來處理,兩個緊密層都是用SELU激活函數(shù)(還可以加上LeCun歸一初始化蛛勉,但因為網(wǎng)絡(luò)不深中姜,效果不大)讥珍。對于每張輸入圖片醉途,編碼器輸出的矢量大小是30舍扰。
解碼器接收大小等于30的編碼(編碼器的輸出),用兩個緊密層來處理侣诵,最后的矢量轉(zhuǎn)換為 28 × 28 的數(shù)組痢法,使解碼器的輸出和編碼器的輸入形狀相同狱窘。
編譯時,使用二元交叉熵損失财搁,而不是MSE蘸炸。將重建任務(wù)當做多標簽分類問題:每個像素強度表示像素應(yīng)該為黑色的概率。這么界定問題(而不是當做回歸問題)尖奔,可以使模型收斂更快搭儒。
最后,使用
X_train
既作為輸入提茁,也作為目標淹禾,來訓(xùn)練模型(相似的,使用X_valid
既作為驗證的輸入也作為目標)茴扁。
可視化重建
確保自編碼器訓(xùn)練得當?shù)姆绞街涣宀恚潜容^輸入和輸出:差異不應(yīng)過大。畫一些驗證集的圖片峭火,及其重建:
def plot_image(image):
plt.imshow(image, cmap="binary")
plt.axis("off")
def show_reconstructions(model, n_images=5):
reconstructions = model.predict(X_valid[:n_images])
fig = plt.figure(figsize=(n_images * 1.5, 3))
for image_index in range(n_images):
plt.subplot(2, n_images, 1 + image_index)
plot_image(X_valid[image_index])
plt.subplot(2, n_images, 1 + n_images + image_index)
plot_image(reconstructions[image_index])
show_reconstructions(stacked_ae)
圖17-4 展示了比較結(jié)果德撬。
可以認出重建,但圖片有些失真躲胳。需要再訓(xùn)練模型一段時間,或使編碼器和解碼器更深纤勒,或使編碼更大坯苹。但如果使網(wǎng)絡(luò)太強大,就學(xué)不到數(shù)據(jù)中的規(guī)律摇天。
可視化Fashion MNIST數(shù)據(jù)集
訓(xùn)練好棧式自編碼器之后粹湃,就可以用它給數(shù)據(jù)集降維了∪可視化的話为鳄,結(jié)果不像(第8章其它介紹的)其它降維方法那么好,但自編碼器的優(yōu)勢是可以處理帶有多個實例多個特征的大數(shù)據(jù)集腕让。所以一個策略是利用自編碼器將數(shù)據(jù)集降維到一個合理的水平孤钦,然后使用另外一個降維算法做可視化。用這個策略來可視化Fashion MNIST纯丸。首先偏形,使用棧式自編碼器的編碼器將維度降到30,然后使用Scikit-Learn的t-SNE算法實現(xiàn)觉鼻,將維度降到2并做可視化:
from sklearn.manifold import TSNE
X_valid_compressed = stacked_encoder.predict(X_valid)
tsne = TSNE()
X_valid_2D = tsne.fit_transform(X_valid_compressed)
對數(shù)據(jù)集作圖:
plt.scatter(X_valid_2D[:, 0], X_valid_2D[:, 1], c=y_valid, s=10, cmap="tab10")
圖17-5 展示了結(jié)果的散點圖(并展示了一些圖片)俊扭。t-SNE算法區(qū)分除了幾類,比較符合圖片的類別(每個類的顏色不一樣)坠陈。
自編碼器的另一個用途是無監(jiān)督預(yù)訓(xùn)練萨惑。
使用棧式自編碼器做無監(jiān)督預(yù)訓(xùn)練
第11章討論過捐康,如果要處理一個復(fù)雜的監(jiān)督任務(wù),但又缺少標簽數(shù)據(jù)庸蔼,解決的方法之一解总,是找一個做相似任務(wù)的神經(jīng)網(wǎng)絡(luò),復(fù)用它的底層朱嘴。這么做就可以使用少量訓(xùn)練數(shù)據(jù)訓(xùn)練出高性能的模型倾鲫,因為模型不必學(xué)習(xí)所有低層次特征;模型可以復(fù)用之前的特征探測器萍嬉。
相似的乌昔,如果有一個大數(shù)據(jù)集,但大部分實例是無標簽的壤追,可以用全部數(shù)據(jù)訓(xùn)練一個棧式自編碼器磕道,然后使用其底層創(chuàng)建一個神經(jīng)網(wǎng)絡(luò),再用有標簽數(shù)據(jù)來訓(xùn)練行冰。例如溺蕉,圖17-6展示了如何使用棧式自編碼器來做分類的無監(jiān)督預(yù)訓(xùn)練。當訓(xùn)練分類器時悼做,如果標簽數(shù)據(jù)不足疯特,可以凍住預(yù)訓(xùn)練層(底層)。
筆記:無標簽數(shù)據(jù)很多肛走,有標簽數(shù)據(jù)數(shù)據(jù)很少漓雅,非常普遍。搭建一個大無便簽數(shù)據(jù)集很便宜(比如朽色,一段小腳本可以從網(wǎng)上下載許多圖片)邻吞,但是給這些圖片打標簽(比如,將其標簽為可愛或不可愛)只有人做才靠譜葫男。打標簽又耗時又耗錢抱冷,所以人工標注實例有幾千就不錯了。
代碼實現(xiàn)沒有特殊之處:用所有訓(xùn)練數(shù)據(jù)訓(xùn)練自編碼器梢褐,然后用編碼器層創(chuàng)建新的神經(jīng)網(wǎng)絡(luò)(本章有練習(xí)題例子)旺遮。
接下來,看看關(guān)聯(lián)權(quán)重的方法盈咳。
關(guān)聯(lián)權(quán)重
當自編碼器整齊地對稱時趣效,就像我們剛剛構(gòu)建的那樣,一種常用方法是將解碼器層的權(quán)重與編碼器層的權(quán)重相關(guān)聯(lián)猪贪。 這樣減半了模型中的權(quán)重數(shù)量跷敬,加快了訓(xùn)練速度,并限制了過度擬合的風(fēng)險。具體來說西傀,如果自編碼器總共具有N
個層(不算輸入層)斤寇,并且 WL 表示第Lth層的連接權(quán)重(例如,層 1 是第一隱藏層拥褂,則層N / 2
是編碼層娘锁,而層N
是輸出層),則解碼器層權(quán)重可以簡單地定義為:WN–L+1 = WLT(其中L = 1, 2, ..., N/2)饺鹃。
使用Keras將層的權(quán)重關(guān)聯(lián)起來莫秆,先定義一個自定義層:
class DenseTranspose(keras.layers.Layer):
def __init__(self, dense, activation=None, **kwargs):
self.dense = dense
self.activation = keras.activations.get(activation)
super().__init__(**kwargs)
def build(self, batch_input_shape):
self.biases = self.add_weight(name="bias", initializer="zeros",
shape=[self.dense.input_shape[-1]])
super().build(batch_input_shape)
def call(self, inputs):
z = tf.matmul(inputs, self.dense.weights[0], transpose_b=True)
return self.activation(z + self.biases)
自定義層的作用就像一個常規(guī)緊密層,但使用了另一個緊密層的權(quán)重悔详,并且做了轉(zhuǎn)置(設(shè)置transpose_b=True
等同于轉(zhuǎn)置第二個參數(shù)镊屎,但在matmul()
運算中實時做轉(zhuǎn)置更為高效)。但是茄螃,要使用自己的偏置矢量缝驳。然后,創(chuàng)建一個新的棧式自編碼器归苍,將解碼器的緊密層和編碼器的緊密層關(guān)聯(lián)起來:
dense_1 = keras.layers.Dense(100, activation="selu")
dense_2 = keras.layers.Dense(30, activation="selu")
tied_encoder = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
dense_1,
dense_2
])
tied_decoder = keras.models.Sequential([
DenseTranspose(dense_2, activation="selu"),
DenseTranspose(dense_1, activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
tied_ae = keras.models.Sequential([tied_encoder, tied_decoder])
這個模型的重建誤差小于前一個模型用狱,且參數(shù)量只有一半。
一次訓(xùn)練一個自編碼器
不是一次完成整個棧式自編碼器的訓(xùn)練拼弃,而是一次訓(xùn)練一個淺自編碼器夏伊,然后將所有這些自編碼器堆疊到一個棧式自編碼器(因此名稱)中,通常要快得多吻氧,如圖 17-7 所示署海。 這個方法如今用的不多了,但偶爾還會撞見談到“貪婪層級訓(xùn)練”的論文医男,所以還是看一看。
在訓(xùn)練的第一階段捻勉,第一個自編碼器學(xué)習(xí)重構(gòu)輸入镀梭。 然后,使用整個訓(xùn)練集訓(xùn)練第一個自編碼器踱启,得到一個新的(壓縮過的)訓(xùn)練集报账。然后用這個數(shù)據(jù)集訓(xùn)練第二個自編碼器。這是第二階段的訓(xùn)練埠偿。最后透罢,我們用所有這些自編碼器創(chuàng)建一個三明治結(jié)構(gòu),見圖17-7(即冠蒋,先把每個自編碼器的隱藏層疊起來羽圃,再加上輸出層)。這樣就得到了最終的棧式自編碼器(見notebook)抖剿。我們可以用這種方式訓(xùn)練更多的自編碼器朽寞,搭建非常深的棧式自編碼器识窿。
正如前面討論過的,現(xiàn)在的一大趨勢是Geoffrey Hinton等人在2006年發(fā)現(xiàn)的脑融,靠這種貪婪層級方法喻频,可以用無監(jiān)督方式訓(xùn)練神經(jīng)網(wǎng)絡(luò)。他們還使用了受限玻爾茲曼機(RBM肘迎,見附錄E)甥温。但在2007年,Yoshua Bengio發(fā)現(xiàn)只用自編碼器也可以達到不錯的效果妓布。在這幾年間姻蚓,自編碼器是唯一的有效訓(xùn)練深度網(wǎng)絡(luò)的方法,知道出現(xiàn)第11章介紹過的方法秋茫。
自編碼器不限于緊密網(wǎng)絡(luò):還有卷積自編碼器和循環(huán)自編碼器史简。
卷積自編碼器
如果處理的是圖片,則前面介紹的自編碼器的效果可能一般(除非圖片非常懈刂)圆兵。第14章介紹過,對于圖片任務(wù)枢贿,卷積神經(jīng)網(wǎng)絡(luò)比緊密網(wǎng)絡(luò)的效果更好殉农。所以如果想用自編碼器來處理圖片的話(例如,無監(jiān)督預(yù)訓(xùn)練或降維)局荚,你需要搭建一個卷積自編碼器超凳。編碼器是一個包含卷積層和池化層的常規(guī)CNN。通常降低輸入的空間維度(即耀态,高和寬)轮傍,同時增加深度(即,特征映射的數(shù)量)首装。解碼器的工作相反(放大圖片创夜,壓縮深度),要這么做的話仙逻,可以轉(zhuǎn)置卷積層(或者驰吓,可以將上采樣層和卷積層合并)。下面是一個卷積自編碼器處理Fashion MNIST的例子:
conv_encoder = keras.models.Sequential([
keras.layers.Reshape([28, 28, 1], input_shape=[28, 28]),
keras.layers.Conv2D(16, kernel_size=3, padding="same", activation="selu"),
keras.layers.MaxPool2D(pool_size=2),
keras.layers.Conv2D(32, kernel_size=3, padding="same", activation="selu"),
keras.layers.MaxPool2D(pool_size=2),
keras.layers.Conv2D(64, kernel_size=3, padding="same", activation="selu"),
keras.layers.MaxPool2D(pool_size=2)
])
conv_decoder = keras.models.Sequential([
keras.layers.Conv2DTranspose(32, kernel_size=3, strides=2, padding="valid",
activation="selu",
input_shape=[3, 3, 64]),
keras.layers.Conv2DTranspose(16, kernel_size=3, strides=2, padding="same",
activation="selu"),
keras.layers.Conv2DTranspose(1, kernel_size=3, strides=2, padding="same",
activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
conv_ae = keras.models.Sequential([conv_encoder, conv_decoder])
循環(huán)自編碼器
如果你想用自編碼器處理序列系奉,比如對時間序列或文本無監(jiān)督學(xué)習(xí)和降維檬贰,則循環(huán)神經(jīng)網(wǎng)絡(luò)要優(yōu)于緊密網(wǎng)絡(luò)。搭建循環(huán)自編碼器很簡單:編碼器是一個序列到矢量的RNN缺亮,而解碼器是矢量到序列的RNN:
recurrent_encoder = keras.models.Sequential([
keras.layers.LSTM(100, return_sequences=True, input_shape=[None, 28]),
keras.layers.LSTM(30)
])
recurrent_decoder = keras.models.Sequential([
keras.layers.RepeatVector(28, input_shape=[30]),
keras.layers.LSTM(100, return_sequences=True),
keras.layers.TimeDistributed(keras.layers.Dense(28, activation="sigmoid"))
])
recurrent_ae = keras.models.Sequential([recurrent_encoder, recurrent_decoder])
這個循環(huán)自編碼器可以處理任意長度的序列翁涤,每個時間步有28個維度。這意味著,可以將Fashion MNIST的圖片作為幾行序列來處理迷雪。注意限书,解碼器第一層用的是RepeatVector
,以保證在每個時間步將輸入矢量傳給解碼器章咧。
我們現(xiàn)在已經(jīng)看過了多種自編碼器(基本的倦西、棧式的、卷積的赁严、循環(huán)的)扰柠,學(xué)習(xí)了訓(xùn)練的方法(一次性訓(xùn)練或逐層訓(xùn)練)。還學(xué)習(xí)了兩種應(yīng)用:視覺可視化和無監(jiān)督學(xué)習(xí)疼约。
為了讓自編碼學(xué)習(xí)特征卤档,我們限制了編碼層的大小(使它處于不完整的狀態(tài))程剥。還可以使用許多其他的限制方法劝枣,可以讓編碼層和輸入層一樣大,甚至更大织鲸,得到一個過完成的自編碼器舔腾。下面就是其中一些方法。
降噪自編碼
另一種強制自編碼器學(xué)習(xí)特征的方法是為其輸入添加噪聲搂擦,對其進行訓(xùn)練以恢復(fù)原始的無噪聲輸入稳诚。 自 20 世紀 80 年代以來,使用自編碼器消除噪音的想法已經(jīng)出現(xiàn)(例如瀑踢,在 Yann LeCun 的 1987 年碩士論文中提到過)扳还。 在 2008 年的一篇論文中,帕斯卡爾文森特等人橱夭。 表明自編碼器也可用于特征提取氨距。 在 2010 年的一篇爐溫中, Vincent 等人引入了棧式降噪自編碼器棘劣。
噪聲可以是添加到輸入的純高斯噪聲俏让,或者可以隨機關(guān)閉輸入,就像 dropout(在第 11 章介紹)呈础。 圖 17-8 顯示了這兩種方法。
實現(xiàn)很簡單:常規(guī)的棧式自編碼器中有一個應(yīng)用于輸入的Dropout
層(或使用GaussianNoise
層)橱健。Dropout
層只在訓(xùn)練中起作用(GaussianNoise
層也只在訓(xùn)練中起作用):
dropout_encoder = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
keras.layers.Dropout(0.5),
keras.layers.Dense(100, activation="selu"),
keras.layers.Dense(30, activation="selu")
])
dropout_decoder = keras.models.Sequential([
keras.layers.Dense(100, activation="selu", input_shape=[30]),
keras.layers.Dense(28 * 28, activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
dropout_ae = keras.models.Sequential([dropout_encoder, dropout_decoder])
圖17-9展示了一些帶有造影的圖片(有一半像素被丟棄)而钞,重建圖片是用基于dropout的自編碼器實現(xiàn)的。注意自編碼器是如何猜測圖片中不存在的細節(jié)的拘荡,比如四張圖片的領(lǐng)口臼节。
稀疏自編碼器
通常良好特征提取的另一種約束是稀疏性:通過向損失函數(shù)添加適當?shù)捻棧屪跃幋a器減少編碼層中活動神經(jīng)元的數(shù)量。 例如网缝,可以讓編碼層中平均只有 5% 的活躍神經(jīng)元巨税。 這迫使自編碼器將每個輸入表示為少量激活的組合。 因此粉臊,編碼層中的每個神經(jīng)元通常都會代表一個有用的特征(如果每個月只能說幾個字草添,你會說的特別精煉)。
使用sigmoid激活函數(shù)可以實現(xiàn)這個目的扼仲。添加一個編碼層(比如远寸,有300個神經(jīng)元),給編碼層的激活函數(shù)添加?1正則(解碼器就是一個常規(guī)解碼器):
sparse_l1_encoder = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
keras.layers.Dense(100, activation="selu"),
keras.layers.Dense(300, activation="sigmoid"),
keras.layers.ActivityRegularization(l1=1e-3)
])
sparse_l1_decoder = keras.models.Sequential([
keras.layers.Dense(100, activation="selu", input_shape=[300]),
keras.layers.Dense(28 * 28, activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
sparse_l1_ae = keras.models.Sequential([sparse_l1_encoder, sparse_l1_decoder])
ActivityRegularization
只是返回輸入屠凶,但副作用是新增了訓(xùn)練損失驰后,大小等于輸入的絕對值之和(這個層只在訓(xùn)練中起作用)。等價的矗愧,可以移出ActivityRegularization
灶芝,并在前一層設(shè)置activity_regularizer=keras.regularizers.l1(1e-3)
。這項懲罰可以讓神經(jīng)網(wǎng)絡(luò)產(chǎn)生接近0的編碼唉韭,如果沒有正確重建輸入夜涕,還是會有損失,仍然會產(chǎn)生一些非0值纽哥。不使用?2钠乏,而使用?1,可以讓神經(jīng)網(wǎng)絡(luò)保存最重要的編碼春塌,同時消除輸入圖片不需要的編碼(而不是壓縮所有編碼)晓避。
另一種結(jié)果更好的方法是在每次訓(xùn)練迭代中測量編碼層的實際稀疏度,當偏移目標值只壳,就懲罰模型俏拱。 我們通過計算整個訓(xùn)練批次中編碼層中每個神經(jīng)元的平均激活來實現(xiàn)。 批量大小不能太小吼句,否則平均數(shù)不準確锅必。
一旦我們對每個神經(jīng)元進行平均激活,我們希望通過向損失函數(shù)添加稀疏損失來懲罰太活躍的神經(jīng)元惕艳,或不夠活躍的神經(jīng)元搞隐。 例如,如果我們測量一個神經(jīng)元的平均激活值為 0.3远搪,但目標稀疏度為 0.1劣纲,那么它必須受到懲罰才能激活更少。 一種方法可以簡單地將平方誤差(0.3-0.1)^2
添加到損失函數(shù)中谁鳍,但實際上更好的方法是使用 Kullback-Leibler 散度(在第 4 章中簡要討論)癞季,它具有比均方誤差更強的梯度劫瞳,如圖 17-10 所示。
給定兩個離散的概率分布P
和Q
绷柒,這些分布之間的 KL 散度志于,記為 DKL(P // Q),可以使用公式 17-1 計算废睦。
在我們的例子中伺绽,我們想要測量編碼層中的神經(jīng)元將激活的目標概率p
與實際概率q
(即,訓(xùn)練批次上的平均激活)之間的差異郊楣。 所以KL散度簡化為公式 17-2憔恳。
一旦我們已經(jīng)計算了編碼層中每個神經(jīng)元的稀疏損失,就相加這些損失净蚤,并將結(jié)果添加到損失函數(shù)中钥组。 為了控制稀疏損失和重構(gòu)損失的相對重要性,我們可以用稀疏權(quán)重超參數(shù)乘以稀疏損失今瀑。 如果這個權(quán)重太高程梦,模型會緊貼目標稀疏度,但它可能無法正確重建輸入橘荠,導(dǎo)致模型無用屿附。 相反,如果它太低哥童,模型將大多忽略稀疏目標挺份,它不會學(xué)習(xí)任何有趣的功能。
現(xiàn)在就可以實現(xiàn)基于KL散度的稀疏自編碼器了贮懈。首先匀泊,創(chuàng)建一個自定義正則器來實現(xiàn)KL散度正則:
K = keras.backend
kl_divergence = keras.losses.kullback_leibler_divergence
class KLDivergenceRegularizer(keras.regularizers.Regularizer):
def __init__(self, weight, target=0.1):
self.weight = weight
self.target = target
def __call__(self, inputs):
mean_activities = K.mean(inputs, axis=0)
return self.weight * (
kl_divergence(self.target, mean_activities) +
kl_divergence(1. - self.target, 1. - mean_activities))
使用KLDivergenceRegularizer
作為編碼層的激活函數(shù),創(chuàng)建稀疏自編碼器:
kld_reg = KLDivergenceRegularizer(weight=0.05, target=0.1)
sparse_kl_encoder = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
keras.layers.Dense(100, activation="selu"),
keras.layers.Dense(300, activation="sigmoid", activity_regularizer=kld_reg)
])
sparse_kl_decoder = keras.models.Sequential([
keras.layers.Dense(100, activation="selu", input_shape=[300]),
keras.layers.Dense(28 * 28, activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
sparse_kl_ae = keras.models.Sequential([sparse_kl_encoder, sparse_kl_decoder])
在Fashion MNIST上訓(xùn)練好稀疏自編碼器之后朵你,編碼層中的神經(jīng)元的激活大部分接近0(70%的激活小于0.1)各聘,所有神經(jīng)元的平均值在0.1附近(90%的平均激活在0.1和0.2之間)見圖17-11。
變分自編碼器(VAE)
Diederik Kingma 和 Max Welling 于 2013 年推出了另一類重要的自編碼器抡医,并迅速成為最受歡迎的自編碼器類型之一:變分自編碼器躲因。
它與我們迄今為止討論的所有自編碼器非常不同,特別是:
它們是概率自編碼器忌傻,意味著即使在訓(xùn)練之后大脉,它們的輸出部分也是偶然確定的(相對于僅在訓(xùn)練過程中使用隨機性的自編碼器的去噪)。
最重要的是水孩,它們是生成自編碼器镰矿,這意味著它們可以生成看起來像從訓(xùn)練集中采樣的新實例。
這兩個屬性使它們與 RBM 非常相似(見附錄 E)荷愕,但它們更容易訓(xùn)練衡怀,并且取樣過程更快(在 RBM 之前舌缤,您需要等待網(wǎng)絡(luò)穩(wěn)定在“熱平衡”之后才能進行取樣一個新的實例)匀奏。正如其名字智什,變分自編碼器要做變分貝葉斯推斷(第9章介紹過)肛搬,這是估計變微分推斷的一種有效方式盛杰。
我們來看看他們是如何工作的境肾。 圖 17-12(左)顯示了一個變分自編碼器蕴茴。 當然十嘿,您可以認識到所有自編碼器的基本結(jié)構(gòu)玉罐,編碼器后跟解碼器(在本例中屈嗤,它們都有兩個隱藏層),但有一個轉(zhuǎn)折點:不是直接為給定的輸入生成編碼 吊输,編碼器產(chǎn)生平均編碼μ
和標準差σ
饶号。 然后從平均值μ
和標準差σ
的高斯分布隨機采樣實際編碼。 之后季蚂,解碼器正常解碼采樣的編碼茫船。 該圖的右側(cè)部分顯示了一個訓(xùn)練實例通過此自編碼器。 首先扭屁,編碼器產(chǎn)生μ
和σ
算谈,隨后對編碼進行隨機采樣(注意它不是完全位于μ
處),最后對編碼進行解碼料滥,最終的輸出與訓(xùn)練實例類似然眼。
從圖中可以看出,盡管輸入可能具有非常復(fù)雜的分布葵腹,但變分自編碼器傾向于產(chǎn)生編碼高每,看起來好像它們是從簡單的高斯分布采樣的:在訓(xùn)練期間,損失函數(shù)(將在下面討論)推動 編碼在編碼空間(也稱為潛在空間)內(nèi)逐漸遷移以占據(jù)看起來像高斯點集成的云的大致(超)球形區(qū)域礁蔗。 一個重要的結(jié)果是觉义,在訓(xùn)練了一個變分自編碼器之后,你可以很容易地生成一個新的實例:只需從高斯分布中抽取一個隨機編碼浴井,對它進行解碼就可以了晒骇!
再來看看損失函數(shù)。 它由兩部分組成磺浙。 首先是通常的重建損失洪囤,推動自編碼器重現(xiàn)其輸入(我們可以使用交叉熵來解決這個問題,如前所述)撕氧。 第二種是潛在的損失瘤缩,推動自編碼器使編碼看起來像是從簡單的高斯分布中采樣,為此我們使用目標分布(高斯分布)與編碼實際分布之間的 KL 散度伦泥。 數(shù)學(xué)比以前復(fù)雜一點剥啤,特別是因為高斯噪聲锦溪,它限制了可以傳輸?shù)骄幋a層的信息量(從而推動自編碼器學(xué)習(xí)有用的特征)。 幸好府怯,這些方程經(jīng)過簡化刻诊,可以用公式17-3計算潛在損失:
在這個公式中,L是潛在損失牺丙,n是編碼維度则涯,μi 和 σi是編碼的第ith個成分的平均值和標準差。矢量u和σ是編碼器的輸出冲簿,見圖17-12的左邊粟判。
一種常見的變體是訓(xùn)練編碼器輸出γ= log(σ^2)
而不是σ
。 可以用公式17-4計算潛在損失峦剔。這個方法的計算更穩(wěn)定档礁,且可以加速訓(xùn)練。
給Fashion MNIST創(chuàng)建一個自編碼器(見圖17-12事秀,使用γ變體)。首先野舶,需要一個自定義層從編碼采樣易迹,給定μ 和 γ:
class Sampling(keras.layers.Layer):
def call(self, inputs):
mean, log_var = inputs
return K.random_normal(tf.shape(log_var)) * K.exp(log_var / 2) + mean
這個Sampling
層接收兩個輸入:mean (μ)
和 log_var (γ)
。使用函數(shù)K.random_normal()
根據(jù)正態(tài)分布隨機采樣矢量(形狀為γ)平均值為0標準差為1平道。然后乘以exp(γ / 2)(這個值等于σ)睹欲,最后加上μ并返回結(jié)果。這樣就能從平均值為0標準差為1的正態(tài)分布采樣編碼矢量一屋。
然后窘疮,創(chuàng)建編碼器,因為模型不是完全順序的冀墨,所以要使用Functional API:
codings_size = 10
inputs = keras.layers.Input(shape=[28, 28])
z = keras.layers.Flatten()(inputs)
z = keras.layers.Dense(150, activation="selu")(z)
z = keras.layers.Dense(100, activation="selu")(z)
codings_mean = keras.layers.Dense(codings_size)(z) # μ
codings_log_var = keras.layers.Dense(codings_size)(z) # γ
codings = Sampling()([codings_mean, codings_log_var])
variational_encoder = keras.Model(
inputs=[inputs], outputs=[codings_mean, codings_log_var, codings])
注意闸衫,輸出codings_mean
(μ)和codings_log_var
(γ)的Dense
層,有同樣的輸入(即诽嘉,第二個緊密層的輸出)蔚出。然后將codings_mean
和codings_log_var
傳給Sampling
層。最后虫腋,variational_encoder
模型有三個輸出骄酗,可以用來檢查codings_mean
和codings_log_var
的值。真正使用的是最后一個(codings
)悦冀。下面創(chuàng)建解碼器:
decoder_inputs = keras.layers.Input(shape=[codings_size])
x = keras.layers.Dense(100, activation="selu")(decoder_inputs)
x = keras.layers.Dense(150, activation="selu")(x)
x = keras.layers.Dense(28 * 28, activation="sigmoid")(x)
outputs = keras.layers.Reshape([28, 28])(x)
variational_decoder = keras.Model(inputs=[decoder_inputs], outputs=[outputs])
對于解碼器趋翻,因為是簡單棧式結(jié)構(gòu),可以不使用Functional API盒蟆,而使用Sequential API踏烙。最后师骗,創(chuàng)建變分自編碼器:
_, _, codings = variational_encoder(inputs)
reconstructions = variational_decoder(codings)
variational_ae = keras.Model(inputs=[inputs], outputs=[reconstructions])
注意,我們忽略了編碼器的前兩個輸出讨惩。最后丧凤,必須將潛在損失和重建損失加起來:
latent_loss = -0.5 * K.sum(
1 + codings_log_var - K.exp(codings_log_var) - K.square(codings_mean),
axis=-1)
variational_ae.add_loss(K.mean(latent_loss) / 784.)
variational_ae.compile(loss="binary_crossentropy", optimizer="rmsprop")
我們首先用公式17-4計算批次中每個實例的潛在損失。然后計算所有實例的平均損失步脓,然后除以,使其量綱與重建損失一致浩螺。實際上靴患,變分自編碼器的重建損失是像素重建誤差的和,但當Keras計算"binary_crossentropy"
損失時要出,它計算的是784個像素的平均值鸳君,而不是和。因此患蹂,重建損失比真正要的值小784倍或颊。我們可以定義一個自定義損失來計算誤差和,但除以784更簡單传于。
注意囱挑,這里使用了RMSprop
優(yōu)化器。最后沼溜,我們可以訓(xùn)練自編碼器平挑。
history = variational_ae.fit(X_train, X_train, epochs=50, batch_size=128,
validation_data=[X_valid, X_valid])
生成Fashion MNIST圖片
接下來用上面的變分自編碼器生成圖片。我們要做的只是從高斯分布隨機采樣編碼系草,然后做解碼:
codings = tf.random.normal(shape=[12, codings_size])
images = variational_decoder(codings).numpy()
圖17-13展示了12張生成的圖片通熄。
大多數(shù)生成的圖片很逼真,就是有些模糊找都。其它的效果一般唇辨,這是因為自編碼器只學(xué)習(xí)了幾分鐘。經(jīng)過微調(diào)和更長時間的訓(xùn)練能耻,效果就能編號赏枚。
變分自編碼器也可以做語義插值:不是對兩張圖片做像素級插值(結(jié)果就像是兩張圖重疊),而是在編碼級插值晓猛。先用編碼層運行兩張圖片嗡贺,然后對兩個編碼層插值,然后解碼插值編碼鞍帝,得到結(jié)果圖片诫睬。結(jié)果就像一個常規(guī)的Fashion MINIST圖片,但還是介于原始圖之間帕涌。在接下來的代碼中摄凡,將12個生成出來的編碼续徽,排列成3 × 4的網(wǎng)格,然后用TensorFlow的tf.image.resize()
函數(shù)亲澡,將其縮放為5 × 7钦扭。默認條件下,resize()
函數(shù)會做雙線性插值床绪,所以每兩個行或列都會包含插值編碼客情。然后用解碼器生成所有圖片:
codings_grid = tf.reshape(codings, [1, 3, 4, codings_size])
larger_grid = tf.image.resize(codings_grid, size=[5, 7])
interpolated_codings = tf.reshape(larger_grid, [-1, codings_size])
images = variational_decoder(interpolated_codings).numpy()
圖17-14 展示了結(jié)果。畫框的是原始圖癞己,其余是根據(jù)附近圖片做出的語義插值圖膀斋。注意,第4行第5列的鞋痹雅,是上下兩張圖的完美融合仰担。
變分自編碼器流行幾年之后,就被GAN超越了绩社,后者可以生成更為真實的圖片摔蓝。
對抗生成網(wǎng)絡(luò)(GAN)
對抗生成網(wǎng)絡(luò)是Ian Goodfellow在2014年的一篇論文中提出的,盡管一開始就引起了眾人的興趣愉耙,但用了幾年時間才客服了訓(xùn)練GAN的一些難點贮尉。和其它偉大的想法一樣,GAN的本質(zhì)很簡單:讓神經(jīng)網(wǎng)絡(luò)互相競爭朴沿,讓其在競爭中進步绘盟。見圖17-15,GAN包括兩個神經(jīng)網(wǎng)絡(luò):
生成器
使用隨機分布作為輸入(通常為高斯分布)悯仙,并輸出一些數(shù)據(jù)龄毡,比如圖片∥ⅲ可以將隨機輸入作為生成文件的潛在表征(即沦零,編碼)。生成器的作用和變分自編碼器中的解碼器差不多货岭,可以用同樣的方式生成圖片(只要輸入一些高斯噪音路操,就能輸出全新的圖片)。但是千贯,生成器的訓(xùn)練過程很不一樣屯仗。判別器
從訓(xùn)練集取出一張圖片,判斷圖片是真是假搔谴。
在訓(xùn)練中魁袜,生成器和判別器的目標正好相反:判別器判斷圖片的真假,生成器盡力生成看起來像真圖的圖片。因為GAN由這兩個目的不同的網(wǎng)絡(luò)組成峰弹,所以不能像常規(guī)網(wǎng)絡(luò)那樣訓(xùn)練店量。每次訓(xùn)練迭代分成兩個階段:
第一個階段,訓(xùn)練判別器鞠呈。從訓(xùn)練集取樣一批真實圖片融师,數(shù)量與假圖片相同。假圖片的標簽設(shè)為0蚁吝,真圖片的標簽設(shè)為1旱爆,判別器用這個有標簽的批次訓(xùn)練一步,使用二元交叉熵損失窘茁。反向傳播在這一階段只優(yōu)化判別器的權(quán)重怀伦。
第二個階段,訓(xùn)練生成器庙曙。首先用生成器產(chǎn)生另一個批次的假圖片,再用判別器來判斷圖片是真是假浩淘。這一次不添加真圖片捌朴,但所有標簽都設(shè)為1(真):換句話說,我們想讓生成器產(chǎn)生可以讓判別器信以為真的圖片张抄。判別器的權(quán)重在這一步是冷凍的砂蔽,所以反向傳播只影響生成器。
筆記:生成器看不到真圖署惯,但卻逐漸生成出逼真的不騙左驾。它只是使用了經(jīng)過判別器返回的梯度。幸好极谊,隨著判別器的優(yōu)化诡右,這些二手梯度中包含的關(guān)于真圖的信息也越來越多,所以生成器才能進步轻猖。
接下來為Fashion MNIST創(chuàng)建一個簡單的GAN模型帆吻。
首先,創(chuàng)建生成器和判別器咙边。生成器很像自編碼器的解碼器猜煮,判別器就是一個常規(guī)的二元分類器(圖片作為輸入,輸出是包含一個神經(jīng)元的緊密層败许,使用sigmoid激活函數(shù))王带。對于每次訓(xùn)練迭代中的第二階段,需要完整的GAN模型:
codings_size = 30
generator = keras.models.Sequential([
keras.layers.Dense(100, activation="selu", input_shape=[codings_size]),
keras.layers.Dense(150, activation="selu"),
keras.layers.Dense(28 * 28, activation="sigmoid"),
keras.layers.Reshape([28, 28])
])
discriminator = keras.models.Sequential([
keras.layers.Flatten(input_shape=[28, 28]),
keras.layers.Dense(150, activation="selu"),
keras.layers.Dense(100, activation="selu"),
keras.layers.Dense(1, activation="sigmoid")
])
gan = keras.models.Sequential([generator, discriminator])
然后市殷,我們需要編譯這些模型愕撰。因為判別器是一個二元分類器,我們可以使用二元交叉熵損失。生成器只能通過GAN訓(xùn)練盟戏,所以不需要編譯生成器绪妹。gan
模型也是一個二元分類器,所以可以使用二元交叉熵損失柿究。重要的邮旷,不能在第二個階段訓(xùn)練判別器,所以編譯模型之前蝇摸,使其不可訓(xùn)練:
discriminator.compile(loss="binary_crossentropy", optimizer="rmsprop")
discriminator.trainable = False
gan.compile(loss="binary_crossentropy", optimizer="rmsprop")
筆記:Keras只有在編譯模型時才會考慮
trainable
屬性婶肩,所以運行這段代碼后,如果調(diào)用fit()
方法或train_on_batch()
方法貌夕,discriminator
就是可訓(xùn)練的了律歼。但在gan
模型上調(diào)用這些方法,判別器是不可訓(xùn)練的啡专。
因為訓(xùn)練循環(huán)是非常規(guī)的险毁,我們不能使用常規(guī)的fit()
方法。但我們可以寫一個自定義的訓(xùn)練循環(huán)们童。要這么做畔况,需要先創(chuàng)建一個Dataset
迭代這些圖片:
batch_size = 32
dataset = tf.data.Dataset.from_tensor_slices(X_train).shuffle(1000)
dataset = dataset.batch(batch_size, drop_remainder=True).prefetch(1)
現(xiàn)在就可以來寫訓(xùn)練循環(huán)了。用train_gan()
函數(shù)來包裝:
def train_gan(gan, dataset, batch_size, codings_size, n_epochs=50):
generator, discriminator = gan.layers
for epoch in range(n_epochs):
for X_batch in dataset:
# phase 1 - training the discriminator
noise = tf.random.normal(shape=[batch_size, codings_size])
generated_images = generator(noise)
X_fake_and_real = tf.concat([generated_images, X_batch], axis=0)
y1 = tf.constant([[0.]] * batch_size + [[1.]] * batch_size)
discriminator.trainable = True
discriminator.train_on_batch(X_fake_and_real, y1)
# phase 2 - training the generator
noise = tf.random.normal(shape=[batch_size, codings_size])
y2 = tf.constant([[1.]] * batch_size)
discriminator.trainable = False
gan.train_on_batch(noise, y2)
train_gan(gan, dataset, batch_size, codings_size)
和前面討論的一樣慧库,每次迭代都有兩個階段:
在第一階段跷跪,向生成器輸入高斯噪音來生成假圖片,然后再補充同等數(shù)量的真圖片齐板。假圖片的目標
y1
設(shè)為0吵瞻,真圖片的目標y1
設(shè)為1。然后用這個批次訓(xùn)練判別器甘磨。注意橡羞,將判別器的trainable
屬性設(shè)為True
:這是為了避免Keras檢查到現(xiàn)在是False
而在訓(xùn)練時為True
,顯示警告济舆。在第二階段尉姨,向GAN輸入一些高斯噪音。它的生成器會開始假圖片吗冤,然后判別器會判斷其真假又厉。我們希望判別器判斷圖片是真的,所以
y2
設(shè)為1椎瘟。注意覆致,為了避免警告,將trainable
屬性設(shè)為False
肺蔚。
這樣就好了律想!如果展示生成出來的圖片(見圖17-16)闲擦,可以看到在第一個周期的后期嘴瓤,圖片看起來已經(jīng)接近Fashion MNIST的圖片了。
不過,再怎么訓(xùn)練之宿,圖片的質(zhì)量并沒有提升族操,還發(fā)現(xiàn)在有的周期GAN完全忘了學(xué)到了什么。為什么會這樣比被?貌似訓(xùn)練GAN很有挑戰(zhàn)色难。接下來看看原因。
訓(xùn)練GAN的難點
在訓(xùn)練中等缀,生成器和判別器不斷試圖超越對方枷莉,這是一個零和博弈。隨著訓(xùn)練的進行尺迂,可能會達成博弈學(xué)家稱為納什均衡的狀態(tài):每個選手都不改變策略笤妙,并認為對方也不會改變策略。例如噪裕,當所有司機都靠左行駛時蹲盘,就達到了納什均衡:沒有司機會選擇換邊。當然州疾,也有第二種可能:每個人都靠右行駛辜限。不同的初始狀態(tài)和動力學(xué)會導(dǎo)致不同的均衡皇拣。在這個例子中严蓖,達到均衡時,只有一種最優(yōu)策略氧急,但納什均衡包括多種競爭策略(比如颗胡,捕食者追逐獵物,獵物試圖逃跑吩坝,兩者都要改變策略)毒姨。
如何將博弈論應(yīng)用到GAN上呢?論文作者證明钉寝,GAN只能達到一種均衡狀態(tài):生成器產(chǎn)生完美的真實圖片弧呐,同時讓判別器來判斷(50%為真,50%為假)嵌纲。這是件好事:看起來只要訓(xùn)練GAN足夠久俘枫,就會達到均衡,獲得完美的生成器逮走。不過鸠蚪,并沒有這么簡單:沒有人能保證一定能達到均衡。
最大的困難是模式坍塌:生成器的輸出逐漸變得不那么豐富。為什么會這樣茅信?假設(shè)生成器產(chǎn)生的鞋子圖片比其它類的圖片更讓人信服盾舌,假鞋子圖片就會更多的欺騙判別器,就會導(dǎo)致生成更多的鞋子圖片蘸鲸。逐漸的妖谴,生成器會忘掉如何生成其它類的圖片。同時棚贾,判別器唯一能看到的就是鞋子圖片窖维,所以判別器也會忘掉如何判斷其它類的圖片。最終妙痹,當判別器想要區(qū)分假鞋和真鞋時铸史,生成器會被迫生成其它類。生成器可能變成善于襯衫怯伊,而忘了鞋子琳轿,判別器也會發(fā)生同樣的轉(zhuǎn)變。GAN會逐漸在一些類上循環(huán)耿芹,從而對哪一類都不擅長崭篡。
另外,因為生成器和判別器不斷試探對方吧秕,它們的參數(shù)可能不斷搖擺琉闪。訓(xùn)練可能一開始正常,但因為不穩(wěn)定性砸彬,會突然發(fā)散颠毙。又因為多種因素可能會影響動力學(xué),GAN會對超參數(shù)特別敏感:微調(diào)超參數(shù)會特別花費時間砂碉。
這些問題自從2014年就一直困擾著人們:人們發(fā)表了許多論文蛀蜜,一些論文提出新的損失函數(shù)、或穩(wěn)定化訓(xùn)練的手段增蹭、或避免模式坍塌滴某。例如,經(jīng)驗接力:將生成器在每個迭代產(chǎn)生的圖片存儲在接力緩存中(逐次丟棄舊的生成圖)滋迈,使用真實圖片和從緩存中取出的圖片訓(xùn)練判別器霎奢。這樣可以降低判別器對生成器的最后一個輸出過擬合的幾率。另外一個方法是小批次判別:測量批次中圖片的相似度饼灿,然后將數(shù)據(jù)傳給判別器幕侠,判別器就可以刪掉缺乏散度的假圖片。這可以鼓勵生成器產(chǎn)生更多類的圖片赔退,避免模式坍塌橙依。
總而言之证舟,這是一個非常活躍的研究領(lǐng)域窗骑,GAN的動力學(xué)仍然沒有徹底搞清女责。好消息是人們已經(jīng)取得了一定成果,效果不俗创译。接下來看看一些成功的架構(gòu)抵知,從深度卷積GAN開始,這是幾年前的前沿成果软族。然后再看兩個新近的(更復(fù)雜的)架構(gòu)刷喜。
深度卷積GAN
2014年的原始GAN論文是用卷積層實驗的,但只用來生成小圖片立砸。不久之后掖疮,許多人使用深度卷積網(wǎng)絡(luò)為大圖片創(chuàng)建GAN。過程艱難颗祝,因為訓(xùn)練不穩(wěn)定浊闪,但最終Alec Radford等人試驗了許多不同的架構(gòu)和超參數(shù),在2015年取得了成功螺戳。他們將最終架構(gòu)稱為深度卷積GAN(DCGAN)搁宾。他們提出的搭建穩(wěn)定卷積GAN的建議如下:
(判別器中)用卷積步長(strided convolutions)、(生成器中)用轉(zhuǎn)置卷積倔幼,替換池化層盖腿。
生成器和判別器都使用批歸一化,除了生成器的輸出層和判別器的輸入層损同。
去除深層架構(gòu)中的全連接隱藏層翩腐。
生成器的輸出層使用tanh激活,其它層使用ReLU激活揖庄。
判別器的所有層使用leaky ReLU激活栗菜。
這些建議在許多任務(wù)中有效欠雌,但存在例外蹄梢,所以你還是需要嘗試不同的超參數(shù)(事實上,改變隨機種子富俄,再訓(xùn)練模型禁炒,可能就成功了)。例如霍比,下面是一個小型的DCGAN幕袱,在Fashion MNIST上效果不錯:
codings_size = 100
generator = keras.models.Sequential([
keras.layers.Dense(7 * 7 * 128, input_shape=[codings_size]),
keras.layers.Reshape([7, 7, 128]),
keras.layers.BatchNormalization(),
keras.layers.Conv2DTranspose(64, kernel_size=5, strides=2, padding="same",
activation="selu"),
keras.layers.BatchNormalization(),
keras.layers.Conv2DTranspose(1, kernel_size=5, strides=2, padding="same",
activation="tanh")
])
discriminator = keras.models.Sequential([
keras.layers.Conv2D(64, kernel_size=5, strides=2, padding="same",
activation=keras.layers.LeakyReLU(0.2),
input_shape=[28, 28, 1]),
keras.layers.Dropout(0.4),
keras.layers.Conv2D(128, kernel_size=5, strides=2, padding="same",
activation=keras.layers.LeakyReLU(0.2)),
keras.layers.Dropout(0.4),
keras.layers.Flatten(),
keras.layers.Dense(1, activation="sigmoid")
])
gan = keras.models.Sequential([generator, discriminator])
生成器的編碼大小為100,將其投影到6272個維度上(7 * 7 * 128)悠瞬,將結(jié)果變形為7 × 7 × 128的張量们豌。這個張量經(jīng)過批歸一化涯捻,然后輸入給步長為2的轉(zhuǎn)置卷積層,從7 × 7上采樣為14 × 14望迎,深度從128降到64障癌。結(jié)果再做一次批歸一化,傳給另一個步長為2的轉(zhuǎn)置卷積層辩尊,從14 × 14上采樣為28 × 28涛浙,深度從64降到1。這個層使用tanh激活函數(shù)摄欲,輸出范圍是-1到1轿亮。因為這個原因,在訓(xùn)練GAN之前胸墙,需要收縮訓(xùn)練集到相同的范圍我注。還需要變形,加上通道維度:
X_train = X_train.reshape(-1, 28, 28, 1) * 2. - 1. # 變形和收縮
判別器看起來很像英語二元分類的常規(guī)CNN迟隅,除了使用的不是最大池化層降采樣圖片仓手,而是使用卷積步長。另外玻淑,使用的激活函數(shù)是leaky ReLU嗽冒。
總之,我們遵守了DCGAN的建議补履,除了將判別器中的BatchNormalization
替換成了Dropout
層(否則訓(xùn)練會變得不穩(wěn)定)添坊,生成器的ReLU替換為SELU。你可以隨意調(diào)整這個架構(gòu):可以看到對超參數(shù)(特別是學(xué)習(xí)率)的敏感度箫锤。
最后贬蛙,要創(chuàng)建數(shù)據(jù)集,然后編譯訓(xùn)練模型谚攒,使用和之前一樣的代碼阳准。經(jīng)過50個周期的訓(xùn)練,生成器的圖片見圖17-17馏臭。還是不怎么完美野蝇,但一些圖片已經(jīng)很逼真了。
如果擴大這個架構(gòu)绕沈,然后用更大的面部數(shù)據(jù)集訓(xùn)練,可以得到相當逼真的圖片帮寻。事實上乍狐,DCGAN可以學(xué)習(xí)到許多有意義的潛在表征,見圖17-18:從生成的諸多圖片中手動選取了九張(左上)固逗,包括三張戴眼鏡的男性浅蚪,三張不戴眼鏡的男性藕帜,和三張不戴眼鏡的女性。對于每一類惜傲,對其編碼做平均耘戚,用平均的結(jié)果再生成一張圖片(放在下方)〔倌總之收津,下方的圖片是上方圖片的平均。但不是簡單的像素平均浊伙,而是潛在空間的平均撞秋,所以看起來仍是正常的人臉。如果用戴眼鏡的男性嚣鄙,減去不戴眼鏡的男性吻贿,加上不戴眼鏡的女性,使用平均編碼哑子,就得到了右邊3 × 3網(wǎng)格的正中的圖片舅列,一個戴眼鏡的女性!其它八張是添加了一些噪聲的結(jié)果卧蜓,用于解釋DCGAN的語義插值能力帐要。可以用人臉做加減法就像科幻小說一樣弥奸!
提示:如果將圖片的類作為另一個輸入榨惠,輸入給生成器和判別器,它們都能學(xué)到每個類的樣子盛霎,你就可以控制生成器產(chǎn)生圖片的類赠橙。這被稱為條件GAN(CGAN)。
但是愤炸,DCGAN并不完美期揪。比如,當你使用DCGAN生成非常大的圖片時规个,通常是局部逼真凤薛,但整體不協(xié)調(diào)(比如T恤的一個袖子比另一個長很多)。如何處理這種問題呢绰姻?
GAN的漸進式變大
Nvidia研究員Tero Karras等人在2018年發(fā)表了一篇論文枉侧,提出了一個重要方法:他們建議在訓(xùn)練時引瀑,先從生成小圖片開始狂芋,然后逐步給生成器和判別器添加卷積層,生成越來越大的圖片(4 × 4, 8 × 8, 16 × 16, …, 512 × 512, 1,024 × 1,024)憨栽。這個方法和棧式自編碼器的貪婪層級訓(xùn)練很像帜矾。余下的層添加到生成器的末端和判別器的前端翼虫,之前訓(xùn)練好的層仍然可訓(xùn)練。
例如屡萤,當生成器的輸出從4 × 4變?yōu)? × 8時(見圖17-19)珍剑,在現(xiàn)有的卷積層上加上一個上采樣層(使用近鄰過濾),使其輸出8 × 8的特征映射死陆。再接著傳給一個新的卷積層(使用same填充招拙,步長為1,輸出為8 × 8)措译。接著是一個新的輸出卷積層:這是一個常規(guī)卷積層别凤,核大小為1,將輸出投影到定好的顏色通道上(比如3)领虹。為了避免破壞第一個訓(xùn)練好的卷積層的權(quán)重规哪,最后的輸出是原始輸出層(現(xiàn)在的輸出是8 × 8的特征映射)的權(quán)重之和。新輸出的權(quán)重是α塌衰,原始輸出的權(quán)重是1-α诉稍,α逐漸從0變?yōu)?。換句話說最疆,新的卷積層(圖17-19中的虛線)是淡入的杯巨,而原始輸出層淡出。向判別器(跟著平均池化層做降采樣)添加新卷積層時努酸,也是用相似的淡入淡出方法舔箭。
這篇文章還提出了一些其它的方法蚊逢,用于提高輸出的散度(避免模式坍塌)层扶,使訓(xùn)練更穩(wěn)定:
-
小批次標準差層
添加在判別器的靠近末端的位置。對于輸入的每個位置烙荷,計算批次(
S = tf.math.reduce_std(inputs, axis=[0, -1])
)中镜会,所有通道所有實例的標準差。接著终抽,這些標準差對所有點做平均戳表,得到一個單值(v = tf.reduce_mean(S)
)。最后昼伴,給批次中的每個實例添加一個額外的特征映射匾旭,填入計算得到的單值(tf.concat([inputs, tf.fill([batch_size, height, width, 1], v)], axis=-1)
)。這樣又什么用呢圃郊?如果生成器產(chǎn)生的圖片沒有什么偏差价涝,則判別器的特征映射的標準差會特別小。有了這個層持舆,判別器就可以做出判斷色瘩∥苯眩可以讓生成器產(chǎn)生高散度的輸出,降低模式坍塌的風(fēng)險居兆。 -
相等的學(xué)習(xí)率
使用一個簡單的高斯分布(平均值為0覆山,標準差為1)初始化權(quán)重,而不使用He初始化泥栖。但是簇宽,權(quán)重在運行時(即,每次執(zhí)行層)會變邪上怼:會除以
晦毙,ninputs是層的輸入數(shù)。這篇論文說耙蔑,使用這個方法可以顯著提升GAN使用RMSProp见妒、Adam和其它適應(yīng)梯度優(yōu)化器時的性能。事實上甸陌,這些優(yōu)化器用估計標準差(見第11章)歸一化了梯度更新须揣,所以有較大動態(tài)范圍的參數(shù)需要更長時間訓(xùn)練,而較小動態(tài)范圍的參數(shù)可能更新過快,會導(dǎo)致不穩(wěn)定。通過縮放模型的部分參數(shù)挺益,可以保證參數(shù)的動態(tài)范圍在訓(xùn)練過程中一致,可以用相同的速度學(xué)習(xí)卵酪。這樣既加速了訓(xùn)練,也做到了穩(wěn)定谤碳。
-
像素級歸一化層
生成器的每個卷積層之后添加溃卡。它能歸一化每個激活函數(shù),基于相同圖片相同位置的所有激活蜒简,而且跨通道(除以平均激活平方的平方根)瘸羡。在TensorFlow的代碼中,這是
inputs / tf.sqrt(tf.reduce_mean(tf.square(X), axis=-1, keepdims=True) + 1e-8)
(平滑項1e-8用于避免零除)搓茬。這種方法可以避免生成器和判別器的過分競爭導(dǎo)致的激活爆炸犹赖。
使用所有這些方法,作者制作出了非常逼真的人臉圖片卷仑。但如何給“逼真”下定義呢峻村?GAN的評估時一大挑戰(zhàn):盡管可以自動評估生成圖片的散度,判斷質(zhì)量要棘手和主觀的多锡凝。一種方法是讓人來打分粘昨,但成本高且耗時。因此作者建議比較生成圖和訓(xùn)練圖的局部圖片結(jié)構(gòu),在各個層次比較雾棺。這個想法使他們創(chuàng)造出了另一個突破性的成果:StyleGAN膊夹。
StyleGAN
相同的Nvidia團隊在2018年的一篇論文中提出了高性能的高清圖片生成架構(gòu)衬浑,StyleGAN捌浩。作者在生成器中使用了風(fēng)格遷移方法,使生成的圖片和訓(xùn)練圖片在每個層次工秩,都有相同的局部結(jié)構(gòu)尸饺,極大提升了圖片的質(zhì)量。判別器和損失函數(shù)沒有變動助币,只修改了生成器浪听。StyleGAN包含兩個網(wǎng)絡(luò)(見圖17-20):
-
映射網(wǎng)絡(luò)
一個八層的MLP,將潛在表征
z
(即眉菱,編碼)映射為矢量w
迹栓。矢量然后傳給仿射變換(即,沒有激活函數(shù)的緊密層俭缓,用圖17-20中的框A表示)克伊,輸出許多矢量。這些矢量在不同級別控制著生成圖片的風(fēng)格华坦,從細粒度紋理(比如愿吹,頭發(fā)顏色)到高級特征(比如,成人或孩子)惜姐±绻颍總而言之,映射網(wǎng)絡(luò)將編碼變?yōu)樵S多風(fēng)格矢量歹袁。 -
合成網(wǎng)絡(luò)
負責生成圖片坷衍。它有一個固定的學(xué)好的輸入(這個輸入在訓(xùn)練之后是不變的,但在訓(xùn)練中被反向傳播更新)条舔。和之前一樣惫叛,合成網(wǎng)絡(luò)使用多個卷積核上采樣層處理輸入,但有兩處不同:首先逞刷,輸入和所有卷積層的輸出(在激活函數(shù)之前)都添加了噪音嘉涌。第二,每個噪音層的后面是一個適應(yīng)實例歸一化(AdaIN)層:它獨立標準化每個特征映射(減去平均值夸浅,除以標準差)仑最,然后使用風(fēng)格矢量確定每個特征映射的縮放和偏移(風(fēng)格矢量對每個特征映射包含一個縮放和一個偏置項)。
在編碼層獨立添加噪音非常重要帆喇。圖片的一些部分是很隨機的警医,比如雀斑和頭發(fā)的確切位置。在早期的GAN中,這個隨機性要么來自編碼预皇,要么是生成器的一些偽噪音侈玄。如果來自編碼,意味著生成器要用編碼的很重要的一部分來存儲噪音:這樣會非常浪費吟温。另外序仙,噪音會在網(wǎng)絡(luò)中流動,直到生成器的最后一層:這是一種沒有必要的約束鲁豪,會顯著減慢訓(xùn)練潘悼。最后,因為噪音的存在爬橡,會出現(xiàn)一些視覺偽影治唤。如果是生成器來制造偽噪音,噪音可能不夠真實糙申,造成更多的視覺偽影宾添。另外,用生成器的一部分權(quán)重來生成偽噪音柜裸,這也是一種浪費缕陕。通過添加額外的噪音輸入,可以避免所有這些問題粘室;GAN可以利用噪音榄檬,給圖片的每個部分添加隨機量。
添加的噪音在每個級別都不同衔统。每個噪音輸入包含一個單獨的包含高斯噪音的特征映射鹿榜,廣播到所有特征映射上(給定級別),然后在添加前用每個特征的縮放因子縮放(這是圖17-20的框B)锦爵。
最后舱殿,StyleGAN使用了一種稱為混合正則(或風(fēng)格混合)的方法,生成圖的一定比例使用兩個編碼來生成险掀。特別的沪袭,編碼c1 和 c2發(fā)送給映射網(wǎng)絡(luò),得到兩個風(fēng)格矢量w1 和 w2樟氢。然后合成網(wǎng)絡(luò)使用風(fēng)格w1生成第一級冈绊,用w2生成其余的。級的選取是隨機的埠啃。這可以防止模型認為臨近的級是有關(guān)聯(lián)的死宣,會導(dǎo)致GAN的局部性,每個風(fēng)格矢量只會影響生成圖的有限數(shù)量的特性碴开。
GAN的種類如此之多毅该,用一本書才能介紹全博秫。希望這里的內(nèi)容可以告訴你GAN的主要觀點,以及繼續(xù)學(xué)習(xí)的動力眶掌。如果你對數(shù)學(xué)概念掌握不好挡育,可以看看網(wǎng)上的博客。然后就可以創(chuàng)建自己的GAN了朴爬,如果一開始碰到問題即寒,千萬別氣餒:有問題是正常的,通常要好好練習(xí)寝殴,才能掌握好蒿叠。如果對實現(xiàn)細節(jié)不明白明垢,可以看看別人的Keras和TensorFlow實現(xiàn)蚣常。事實上,如果你只是想快速獲得一些經(jīng)驗的結(jié)果痊银,可以使用預(yù)訓(xùn)練模型(例如抵蚊,存在適用于Keras的StyleGAN預(yù)訓(xùn)練模型)。
下一章會介紹深度學(xué)習(xí)的另一領(lǐng)域:深度強化學(xué)習(xí)溯革。
練習(xí)
自編碼器主要用來做什么贞绳?
假設(shè)你想訓(xùn)練一個分類器,有許多未打標簽的訓(xùn)練數(shù)據(jù)致稀,只有一千多打了標簽的數(shù)據(jù)冈闭。如何使用自編碼器來解決這個問題?
如果自編碼器完美重建了輸入抖单,它一定是個好的自編碼器嗎萎攒?如何評估自編碼器的表現(xiàn)?
自編碼器的欠完成和過完成是什么矛绘?超欠完成的風(fēng)險是什么耍休?過完成的風(fēng)險是什么?
如何將棧式自編碼器的權(quán)重連起來货矮?這么做的意義是什么羊精?
什么是生成式模型?可以舉出生成式自編碼器的例子嗎囚玫?
GAN是什么喧锦?可以用于什么任務(wù)?
訓(xùn)練GAN的難點是什么抓督?
-
用去噪音自編碼器預(yù)訓(xùn)練一個圖片分類器燃少。可以使用MNIST本昏,或是更復(fù)雜的圖片數(shù)據(jù)集供汛,比如CIFAR10。不管用的是什么數(shù)據(jù)集,遵循下面的步驟:
將數(shù)據(jù)集分成訓(xùn)練集和測試集怔昨。在完整訓(xùn)練集上雀久,訓(xùn)練一個深度去噪音自編碼器。
檢查圖片正確重建了趁舀±蛋疲可視化最激活編碼層神經(jīng)元的圖片。
搭建一個分類DNN矮烹,使用自編碼器的淺層越庇。用訓(xùn)練集中的500張圖片來訓(xùn)練。然后判斷預(yù)訓(xùn)練是否提升了性能奉狈?
用剛才選擇的數(shù)據(jù)集卤唉,訓(xùn)練一個變分自編碼器。用它來生成圖片仁期∩G或者,用一個沒有標簽的數(shù)據(jù)集跛蛋,來生成新樣本熬的。
訓(xùn)練一個DCGAN來處理選擇的數(shù)據(jù)集,生成新圖片赊级。添加經(jīng)驗接力押框,看看它是否有作用。再將其變?yōu)橐粋€條件GAN理逊,可以控制生成的類橡伞。
參考答案見附錄A。
(第二部分:深度學(xué)習(xí))
第10章 使用Keras搭建人工神經(jīng)網(wǎng)絡(luò)
第11章 訓(xùn)練深度神經(jīng)網(wǎng)絡(luò)
第12章 使用TensorFlow自定義模型并訓(xùn)練
第13章 使用TensorFlow加載和預(yù)處理數(shù)據(jù)
第14章 使用卷積神經(jīng)網(wǎng)絡(luò)實現(xiàn)深度計算機視覺
第15章 使用RNN和CNN處理序列
第16章 使用RNN和注意力機制進行自然語言處理
第17章 使用自編碼器和GAN做表征學(xué)習(xí)和生成式學(xué)習(xí)
第18章 強化學(xué)習(xí)
第19章 規(guī)牡舶埃化訓(xùn)練和部署TensorFlow模型