變分自動編碼器(VAE)是生成模型的一種,它透過學習資料的潛在表示來生成新的樣本。相較於傳統自動編碼器,VAE 的核心差異在於其編碼器輸出並非單個潛在向量,而是一個描述機率分佈的引數,通常是均值和方差。藉由從這個分佈中取樣,VAE 能夠生成更具多樣性的資料。這種機制有效地組織了潛在空間,避免了傳統自動編碼器中潛在空間缺乏結構的問題,進而提升了生成影像的品質和真實性。VAE 的訓練過程涉及重建損失和 KL 散度兩個關鍵組成部分,前者確保生成的影像與輸入影像相似,後者則引導潛在空間的分佈逼近標準正態分佈,從而使潛在空間更加連續和平滑,避免孤立區域的出現,並確保生成的樣本具有多樣性。
自動編碼器(Autoencoder)原理與實作
自動編碼器是一種無監督學習的神經網路,用於學習輸入資料的有效表示。它由編碼器(encoder)和解碼器(decoder)兩部分組成,兩者共同訓練以實作輸入資料的壓縮和重建。
自動編碼器的工作原理
自動編碼器的訓練過程中,輸入影像本身作為標籤,學習如何透過有限的網路結構還原原始影像。這種方法本質上是在學習有失真壓縮,即如何在壓縮資訊的同時還原原始影像。希望透過這種方式,可以去除資料中的噪聲,學習到訊號的有效對映。
與透過監督任務訓練的嵌入(embedding)相比,自動編碼器由於其「標籤」是整個輸入影像,因此輸入的每個部分都與輸出相關,從而保留了更多的輸入資訊。由於自動編碼器是自監督的,不需要額外的標籤步驟,因此可以使用更多的資料進行訓練,從而獲得更好的編碼效果。
編碼器與解碼器的結構
典型的編碼器和解碼器形成一個沙漏形狀,每一層在編碼器中逐漸縮小維度,而在解碼器中逐漸擴大維度,如圖 12-4 所示。在編碼器的末端和解碼器的起始處,維度達到最小值,形成了一個潛在向量(latent vector),這是輸入資料的簡潔表示。
自動編碼器的架構實作
為了簡化討論,我們選擇一個簡單的手寫數字資料集 MNIST 來應用自動編碼器。輸入影像大小為 28x28,具有單一的灰度通道。
編碼器實作
encoder = tf.keras.Sequential([
keras.Input(shape=(28, 28, 1), name="image_input"),
layers.Conv2D(32, 3, activation="relu", strides=2, padding="same"),
layers.Conv2D(64, 3, activation="relu", strides=2, padding="same"),
layers.Flatten(),
layers.Dense(2) # latent dim
], name="encoder")
解碼器實作
decoder = tf.keras.Sequential([
keras.Input(shape=(latent_dim,), name="d_input"),
layers.Dense(7 * 7 * 64, activation="relu"),
layers.Reshape((7, 7, 64)),
layers.Conv2DTranspose(32, 3, activation="relu", strides=2, padding="same"),
layers.Conv2DTranspose(1, 3, activation="sigmoid", strides=2, padding="same")
], name="decoder")
自動編碼器的組合與訓練
將編碼器和解碼器組合起來,形成一個可訓練的模型:
encoder_inputs = keras.Input(shape=(28, 28, 1))
x = encoder(encoder_inputs)
decoder_output = decoder(x)
autoencoder = keras.Model(encoder_inputs, decoder_output)
訓練模型以最小化輸入和輸出影像之間的重建誤差,例如使用均方誤差(MSE)作為損失函式:
autoencoder.compile(optimizer=keras.optimizers.Adam(), loss='mse')
history = autoencoder.fit(mnist_digits, mnist_digits, epochs=30, batch_size=128)
潛在向量(Latent Vectors)
訓練完成後,可以丟棄解碼器,使用編碼器將影像轉換為潛在向量:
z = encoder.predict(img)
內容解密:
layers.Conv2D和layers.Conv2DTranspose的作用:layers.Conv2D用於逐步減少輸入影像的維度,將資訊壓縮到更小的空間中;layers.Conv2DTranspose則用於逐步擴大維度,將壓縮的資訊重建回原始大小。layers.Flatten()和layers.Reshape()的使用:layers.Flatten()將多維輸入展平成一維,以便輸入到全連線層;layers.Reshape()將一維向量重新塑形為多維結構,以便進行反捲積操作。latent_dim的選擇:潛在向量的維度大小影響壓縮和重建的效果。太小會導致資訊丟失,太大則可能保留過多的噪聲。通常需要透過實驗來確定合適的值。
常見 Keras 層的反向操作
在撰寫自動編碼器時,瞭解常見 Keras 層的「反向」操作非常有幫助:
Dense層的反向操作是另一個Dense層,但輸入輸出形狀互換。Flatten層的反向操作是Reshape層,同樣是輸入輸出形狀互換。Conv2D層的反向操作是Conv2DTranspose層,用於將畫素擴充套件到鄰域以進行上取樣。
自動編碼器(Autoencoder)在影像重建與生成中的應用與限制
自動編碼器是一種神經網路架構,主要由編碼器(Encoder)和解碼器(Decoder)兩個子網路組成。編碼器負責將輸入影像壓縮成低維度的潛在向量(Latent Vector),而解碼器則將這個潛在向量重建回原始影像。在訓練過程中,自動編碼器學習如何有效地壓縮和重建影像。
自動編碼器的工作原理與應用
當自動編碼器成功學習到如何重建影像後,相似影像的潛在向量會傾向於聚集在一起,如圖 12-5 所示。這意味著具有相似特徵的影像在潛在空間中會被對映到相近的位置。由於輸入影像的所有資訊都必須透過瓶頸層(Bottleneck Layer),因此瓶頸層在訓練後會保留足夠的資訊,以便解碼器能夠重建出與輸入影像接近的版本。
影像重建與潛在向量
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 影像重建與潛在向量
rectangle "編碼器" as node1
rectangle "解碼器" as node2
node1 --> node2
@enduml
此圖示展示了自動編碼器的基本架構:輸入影像透過編碼器被壓縮成潛在向量,然後透過解碼器重建回影像。
內容解密:
- 編碼器 將輸入影像轉換為潛在向量,實作了影像的壓縮表示。
- 潛在向量 是輸入影像在低維度空間的表示,捕捉了影像的主要特徵。
- 解碼器 將潛在向量重建為原始影像,實作了影像的重建。
這種特性使得自動編碼器可以用於降維。我們可以丟棄解碼器,只使用編碼器將影像轉換為潛在向量,這些向量可以用於下游任務,如分類別和聚類別。與傳統的降維技術(如主成分分析,PCA)相比,如果自動編碼器使用非線性啟用函式,其編碼能夠捕捉輸入特徵之間的非線性關係。
自動編碼器在影像生成中的限制
雖然自動編碼器的解碼器可以用來從潛在向量生成影像,但直接使用隨機生成的潛在向量並不能保證生成高品質的影像。如圖 12-7 所示,在 MNIST 資料集上,某些區域的潛在向量能夠生成逼真的數字,而其他區域則生成無意義的影像。這是因為訓練過程中並未對潛在空間進行正則化,導致存在大量未被對映的空白區域。
影像生成問題分析
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 影像生成問題分析
rectangle "解碼器" as node1
rectangle "導致" as node2
node1 --> node2
@enduml
此圖示說明瞭直接使用隨機潛在向量透過解碼器生成影像可能導致的問題。
內容解密:
- 隨機潛在向量 未必符合編碼器學習到的潛在空間分佈,因此可能無法生成有意義的影像。
- 未正則化的潛在空間 存在大量未被有效利用的區域,這些區域對應的影像可能是無意義或低品質的。
變分自動編碼器(Variational Autoencoder, VAE)
為瞭解決傳統自動編碼器在影像生成中的問題,我們需要對潛在空間進行組織或正則化,使得相近的潛在向量對應相似的影像,並且確保整個潛在空間都能生成有意義的輸出。這就引出了變分自動編碼器的概念。
變分自動編碼器透過對潛在空間施加正則化,確保了生成的影像具有連續性和一致性。這種方法克服了傳統自動編碼器的侷限性,為影像生成任務提供了更有效的解決方案。
變分自編碼器(VAE):生成模型的革新
在深度學習的領域中,自編碼器(Autoencoder)一直是學習資料潛在表示(latent representation)的重要工具。然而,傳統的自編碼器在生成新樣本時卻存在著明顯的侷限性。變分自編碼器(Variational Autoencoder, VAE)的出現,正是為瞭解決這些問題而設計的。
自編碼器的侷限性
首先,回顧傳統自編碼器的運作機制。自編碼器由編碼器(encoder)和解碼器(decoder)兩部分組成。編碼器負責將輸入資料對映到潛在空間中的一個點,而解碼器則試圖從這個潛在點重建出原始輸入。然而,經過長時間訓練後,儘管自編碼器能夠很好地重建輸入資料,但其潛在空間卻並未被有效地組織。這意味著,在潛在空間中隨機取樣得到的點,經過解碼器後往往會生成毫無意義的影像,如圖12-9所示。
圖12-9:潛在空間中的隨機點解碼後的無意義影像
這種現象的原因在於,自編碼器的目標僅僅是最小化重建誤差,而並未對潛在空間的結構做出任何假設或限制。因此,潛在空間中可能會出現孤立的區域,而區域之間的空白處則對應著無意義的影像。
變分自編碼器的優勢
變分自編碼器正是為瞭解決上述問題而提出的。VAE是一種生成模型,它不僅能夠學習資料的潛在表示,還能夠生成新的樣本。與傳統自編碼器不同,VAE的編碼器不再輸出單一的潛在向量,而是輸出描述一個機率分佈的引數,通常是均值和方差。然後,從這個分佈中取樣得到潛在向量,並將其輸入到解碼器中以生成新的樣本。
圖12-11:VAE解決了潛在空間的兩個主要問題
VAE透過引入機率分佈的概念,使得潛在空間變得更加連續和平滑。這意味著,在潛在空間中相鄰的點會對應著相似的影像,從而解決了傳統自編碼器的兩個主要問題:一是相近的潛在點生成完全不同的影像,二是隨機潛在點生成無意義的影像。
VAE的架構與實作
在TensorFlow中實作VAE,其架構與傳統自編碼器相似,但有一些關鍵的不同。以下是一個簡單的實作範例(完整程式碼請參考GitHub上的12b_vae.ipynb):
encoder_inputs = keras.Input(shape=(28, 28, 1))
x = layers.Conv2D(32, 3, activation="relu", strides=2, padding="same")(encoder_inputs)
x = layers.Conv2D(64, 3, activation="relu", strides=2, padding="same")(x)
x = layers.Flatten(name="e_flatten")(x)
z_mean = layers.Dense(latent_dim, name="z_mean")(x) # 均值向量
z_log_var = layers.Dense(latent_dim, name="z_log_var")(x) # 對數方差向量
內容解密:
encoder_inputs定義了VAE編碼器的輸入形狀,用於接收28x28畫素的灰階影像。- 使用兩個卷積層(Conv2D)來提取輸入影像的特徵,並透過
strides=2實作下取樣。 Flatten層將多維特徵圖展平成一維向量,以便輸入到全連線層。z_mean和z_log_var分別代表編碼器輸出的均值向量和對數方差向量,用於描述潛在空間中的機率分佈。
VAE的潛在空間組織
VAE透過強制編碼器輸出一個接近標準正態分佈的機率分佈,使得潛在空間變得更加連續和平滑。這種設計避免了孤立區域的出現,並確保了生成的樣本具有多樣性。
圖12-12:理想與非理想的潛在空間分佈
如圖12-12所示,左側代表了非理想的潛在空間分佈,其中存在著孤立的小區域;而右側則是VAE所追求的理想分佈,即整個潛在空間被平滑、重疊的機率分佈所覆寫。
變分自編碼器(VAE)與影像生成
變分自編碼器(VAE)是一種特殊的自編碼器,不僅能重建輸入資料,還能生成新的資料。與傳統自編碼器不同,VAE 的編碼器輸出不是單一的潛在向量,而是潛在空間中的機率分佈。
編碼器架構
為了實作 VAE,我們需要修改編碼器的架構,使其輸出三個值:z_mean、z_log_var 和 z。其中,z_mean 和 z_log_var 分別代表潛在空間中機率分佈的均值和對數變異數,而 z 則是從該分佈中取樣的結果。
z_log_var = layers.Dense(latent_dim, name="z_log_var")(x)
z = Sampling()(z_mean, z_log_var)
encoder = keras.Model(encoder_inputs, [z_mean, z_log_var, z], name="encoder")
內容解密:
z_log_var層用於計算潛在空間中機率分佈的對數變異數,與z_mean層類別似。Sampling層負責從編碼器輸出的機率分佈中取樣,生成潛在向量z。- 編碼器的輸出現在包含三個部分:
z_mean、z_log_var和z,這使得我們能夠使用 Functional API 建立模型。
取樣層實作
取樣層的核心是從正態分佈中取樣,並利用重引數化技巧(reparameterization trick)使取樣過程可微分。
class Sampling(tf.keras.layers.Layer):
def call(self, inputs):
z_mean, z_log_var = inputs
batch = tf.shape(input=z_mean)[0]
dim = tf.shape(input=z_mean)[1]
epsilon = tf.random.normal(shape=(batch, dim))
return z_mean + tf.math.exp(x=0.5 * z_log_var) * epsilon
內容解密:
Sampling層接收z_mean和z_log_var作為輸入。- 使用
tf.random.normal生成標準正態分佈的噪聲epsilon。 - 透過重引數化技巧,將
epsilon與z_mean和z_log_var結合,生成潛在向量z。
解碼器與損失函式
VAE 的解碼器與傳統自編碼器相同,將潛在向量 z 重建為原始輸入。
z_mean, z_log_var, z = encoder(encoder_inputs)
decoder_output = decoder(z)
vae = keras.Model(encoder_inputs, decoder_output, name="vae")
損失函式包含兩個部分:重建損失和 KL 散度。
def kl_divergence(z_mean, z_log_var):
kl_loss = -0.5 * (1 + z_log_var - tf.square(z_mean) - tf.exp(z_log_var))
return tf.reduce_mean(tf.reduce_sum(kl_loss, axis=1))
def reconstruction_loss(real, reconstruction):
return tf.reduce_mean(
tf.reduce_sum(
keras.losses.binary_crossentropy(real, reconstruction),
axis=(1, 2)
)
)
內容解密:
kl_divergence函式計算編碼器輸出的機率分佈與標準正態分佈之間的 KL 散度。reconstruction_loss函式計算重建損失,使用二元交叉熵作為衡量指標。- VAE 的總損失是重建損失與 KL 散度的總和。
訓練與結果
訓練 VAE 需要將 MNIST 數字影像作為輸入和標籤,並使用 Adam 最佳化器進行最佳化。
vae.compile(optimizer=keras.optimizers.Adam(), loss=reconstruction_loss, metrics=["mse"])
history = vae.fit(mnist_digits, mnist_digits, epochs=30, batch_size=128)
訓練完成後,VAE 能夠生成與輸入影像相似的新影像。與傳統自編碼器相比,VAE 生成的影像更具多樣性和真實性。
生成對抗網路(GAN)與影像生成
生成對抗網路(GAN)是一種強大的影像生成模型,透過兩個神經網路之間的對抗訓練,不斷提高生成影像的品質。
GAN 架構
GAN 由生成器(Generator)和鑑別器(Discriminator)兩個部分組成。生成器負責生成假影像,而鑑別器則嘗試區分真實影像和假影像。
GAN 訓練過程
GAN 的訓練過程是交替進行的:先訓練鑑別器,使其能夠區分真實影像和假影像;然後訓練生成器,使其生成的假影像能夠欺騙鑑別器。