變分自動編碼器(Variational Auto Encoders ,VAE)是種隱藏變量模型[1,2]。該模型的思想在於:由模型所生成的數據可以經變量參數化,而這些變量將生成具有給定數據的特徵。因此,這些變量被稱為隱藏變量。
而VAE背後的關鍵點在於:為了從樣本空間中找到能夠生成合適輸出的樣本(就是能輸出儘可能接近我們所規定分布的數據),它並沒有試圖去直接構造一個隱藏空間(隱藏變量所在的空間),而是構造了一個類似於具有編碼器和解碼器兩個部分的網絡:
編碼器部分能夠學習到根據輸入樣本X來形成一個特定分布,從中我們可以對一個隱藏變量進行採樣,而這個隱藏變量極有可能生成X裡面的樣本。換句話說,可以理解為編碼器通過學習一組參數θ1來得到一個能計算出Q(X,θ1)分布樣本的模型,通過抽樣能夠使得隱藏變量z的P(X|z)最大化。
而解碼器部分能夠學習到根據給定的一個隱藏變量z作為輸入,生成一個具有真實數據分布的輸出。換句話說,可以理解為解碼器通過學習一組參數θ2來得到一個能映射到隱藏空間分布的函數f(z,θ2),從而能夠輸出與真實數據具有相同分布的數據集。
為了充分理解VAE背後的數學意義,我們將通過對其理論講解以及與一些傳統方法進行比較來說明。
這篇文章將包含以下內容
如何對隱藏空間進行定義?
如何高效的從隱藏空間中生成數據?
VAE最終的框架是什麼?
通過一些實驗來展示VAE中的一些有趣特徵。
潛在變量模型
隱藏變量模型這一說法是根據:模型所生成的數據是通過對隱藏變量進行參數化而來。而這個名稱具有這樣一個事實:對於能夠生成我們所需數據的模型,我們並不需要知道這個模型在哪設置了隱藏變量。
在一些測試場合,對於一個在高維空間Z中的隱藏向量z,我們能夠很輕鬆的根據它的概率密度函數P(z)進行抽樣。這時,我們能夠得到一些較為確定的函數f(z;θ),其中θ能夠被參數化到Θ空間,具體表示為:f :Z×Θ→X。式中f是一個確定的映射關係,當z具有隨機性而θ是一個固定參數時,f(z;θ) 就是X張成空間中的隨機變量。
在訓練過程中,我們通過優化參數θ來使得根據P(z)抽取出的z具有更高的概率,從而通過f(z;θ)函數得到的數據能更加接近真實樣本X中的數據。總之為了實現這一目的,我們需要去找到這樣的參數θ:
在上式中,為了使得X與z的關係更加直觀,我們使用了總體概率密度進行計算,即採用P(X|z;θ)分布來替換f(z;θ)分布。此外,我們還做了另外一個假設:P(W|z;θ)遵循N(X|f(z; θ),σ*I)類型的高斯分布。(這樣做的目的是:可以認為生成的數據幾乎是X中的數據,但卻又不完全是X中的數據)
定義隱藏空間
正如開始介紹的那樣,隱藏空間是一個假設的模型,該空間中的一些變量能夠影響到我們數據分布中的一些特定的特徵。我們可以想像下,如果我們的數據假設為是由汽車組成的以及數據的分布類比於汽車的空間,那麼這個隱藏變量可以理解為影響汽車顏色、每個部件的位置以及車門的數量的因素。
然而,直接去明確每個隱藏變量是非常困難的,特別是當我們所需處理的數據具有上百個維度時。並且當其中一個隱藏變量與另一個隱藏變量存在一定耦合關係時,人工去定義這些隱藏變量將變得更加困難,換句話說就是對於P(z)的複雜分布很難通過人工去進行定義。
解決方案
為了解決這一問題,我們以反向傳播的方式,採用數學中概率分布的性質和神經網絡的能力來學習出這些樣本所服從的函數。
使得該問題能被更容易處理的數學性質:對於一個d維的任意分布,它都能夠通過選取一組d個服從正態分布的變量並將它們映射到一個足夠複雜的函數中來生成。因此,我們可以假定我們的隱藏變量服從高斯分布,然後構造出一個具體的函數將我們服從高斯分布的隱藏空間映射到一個更為複雜的分布當中,這樣我們就能通過取樣來生成我們需要的數據了。其中,這個具體的函數需要把簡單的隱藏分布映射成一個更為複雜的分布,而這樣的一個複雜分布可以看作是一個隱藏空間,這時就可以採用神經網絡來建立出一些參數,因此這些參數就能夠在訓練中進行微調了。
學會從潛在空間中生成數據
在進入本文最有趣的部分之前,讓我們先回顧一下最終的目標:我們有一個服從正態分布的d維隱藏空間,以及我們所要學習的f(z;θ2)函數能夠將隱藏分布映射到真實數據的分布。換句話說就是:我們先對隱藏變量進行採樣,然後將這個隱藏變量作為生成器的輸入,最後生成的數據樣本能夠儘可能的接近真實的數據。
我們需要解決以下兩件事情
1. 為了使得變量z的概率密度P(X|z)最大化,如何高效的對隱藏空間進行探索?(我們需要在訓練過程中為給定的X找到最為正確的z)
2. 如何使用反向傳播來訓練這整個過程?(我們需要找到一個具體對象來優化f:P(z)映射到P(X))
為我們的X找到正確的隱藏變量z
在絕大多數的實驗中,z和P(X|z)的數值都接近於零,因此我們對P(X)的估計幾乎沒有什麼意義。而VAE的核心思想在於:需要嘗試對可能產生X的z值進行不斷採樣,然後從這些值中計算出P(X)的大小。為了做到這一點,我們首先需要構建一個能夠給出X的值並給出可能產生z的值的X分布的這樣一個新函數Q(z|X),並希望在Q函數下的z值的空間大小比P(z)下的z值的空間大小要小得多。
對於VAE的編碼器與所假定出的Q函數,我們採用神經網絡來對其進行訓練,使得輸入X能映射到輸出Q(z|X)的分布中,從而幫助我們能夠找到一個最好的z來生成實際X中的數據。
使用反向傳播訓練模型
為了更好理解到我們的VAE是如何訓練出來的,首先我們需要定義一個明確的目標,而為了做到這一點,我們又需要做一些數學公式的推導。
讓我們從編碼器開始說起,我們是想讓Q(z|X)的分布無限接近於P(X|z)的分布,為了確定出這兩個分布到底有多接近,我們可以採用兩個分布間的Kullback-Leibler散度D來進行度量:
通過數學推導,我們可以把這個等式寫成更為有趣的形式。對P(z|X)使用貝葉斯定律後,等式如下:
還可以表示成如下等式:
花點時間看看這個公式
對於A部分:就是在等式左邊的這一項,它對於反向傳播來說並不是一個有利的設定(我們並不知道P (X)的表達式是多少),但是我們可以知道如果想要在給定z的情況下最大化log(P(X)),可以通過最小化減號右邊的部分來實現(這樣可以使得Q(z|x)的分布儘可能接近P(z|X)的分布),這也正是我們在一開始時提到的最終目標。
對於B部分:等式右邊的這一項就更加有趣,正如我們了解P(X|z)(這是解碼器部分->生成器)和Q(z|X)(這是我們的編碼器)。所以我們可以得出,為了最大化這一項,我們需要最大化log(P(X|z)),而這也就意味著我們不需要最大化log函數的極大似然概率和最小化Q(z|X)和P(z)之間的KL散度。
為了使得B部分更容易被計算,我們假設Q(z|X)是服從N(z|mu(X,θ1)或sigma(X,θ1))的高斯分布,其中θ1是在神經網絡中需要從數據集中所學到的參數。此外,在我們的公式中還有一個問題還沒被解決:就是如何計算反向傳播中的期望(損失函數)?
損失函數部分的操作
一種思路是採用多次前向傳遞的方式來計算出log(P(X|z))的期望,但是其計算效率較低。從而我們希望通過隨機訓練來解決這一問題,首先我們假定在第n輪迭代中使用的數據Xi能代表整個數據集,因此可以考慮我們從樣本Xi中所獲得的log(P(Xi|zi))和代表log(P(X|z))對Q分布的期望zi。最後,這個解碼器只是一個簡單的生成器模型,而我們希望去重建這個輸入圖像,因此一個簡單的方法是使用輸入圖像和生成圖像之間的均方誤差來作為其期望部分(損失函數),如下式。
VAE的最終框架
正如在一開始所介紹的那樣,我們知道VAE的最終結構由兩個部分的網絡所構成:
1. 編碼器部分能夠學習到根據輸入樣本X來形成一個特定分布,從中我們可以對一個隱藏變量進行採樣,而這個隱藏變量極有可能生成X裡面的樣本。為了使得Q(z|X)服從高斯分布,這部分需要被優化。
2. 解碼器部分能夠學習到根據給定的一個隱藏變量z作為輸入,生成一個具有真實數據分布的輸出。該部分將經過採樣後的z(最初來自正態分布)映射到一個更複雜的隱藏空間去(實際數據的空間),並通過這個複雜的隱藏變量z生成一個個的數據點,這些數據點十分接近我們真實數據的分布。
VAE的詳細架構。左邊的圖和右邊的圖是類似的,只是左邊示例中展示了反向傳播,實際使用圖一般為右邊的示例
VAE實驗分析
現在你已經了解到了VAE背後的數學理論,那麼現在讓我們看看通過VAE我們能夠生成哪些模型,實驗平臺為PyTorch。
PyTorch的全局架構
class VAE(nn.Module):
def __init__(self, latent_dim):
super().__init__()
self.encoder = nn.Sequential(nn.Linear(28 * 28, 256),
nn.ReLU(),
nn.Linear(256, 128))
self.mu = nn.Linear(128, latent_dim)
self.logvar = nn.Linear(128, latent_dim)
self.latent_mapping = nn.Linear(latent_dim, 128)
self.decoder = nn.Sequential(nn.Linear(128, 256),
nn.ReLU(),
nn.Linear(256, 28 * 28))
def encode(self, x):
x = x.view(x.size(0), -1)
encoder = self.encoder(x)
mu, logvar = self.mu(encoder), self.logvar(encoder)
return mu, logvar
def sample_z(self, mu, logvar):
eps = torch.rand_like(mu)
return mu + eps * torch.exp(0.5 * logvar)
def decode(self, z,x):
latent_z = self.latent_mapping(z)
out = self.decoder(latent_z)
reshaped_out = torch.sigmoid(out).view(x.shape[0],1, 28,28)
return reshaped_out
def forward(self, x):
mu, logvar = self.encode(x)
z = self.sample_z(mu, logvar)
output = self.decode(z,x)
return output
訓練模型
下圖展示了我們在訓練期過程中所得到的結果。為了演示,VAE已經在MNIST數據集[3]上經過了訓練,每10輪展示一次,我們繪製了輸入X和所生成的數據,而這些輸出的數據又作為VAE的輸入。
訓練樣本(輸入在上,輸出在下)——第1輪
訓練樣本(輸入在上,輸出在下)——第10輪
訓練樣本(輸入在上,輸出在下)——第20輪
訓練樣本(輸入在上,輸出在下)——第30輪
訓練樣本(輸入在上,輸出在下)——第40輪
訓練樣本(輸入在上,輸出在下)——第50輪
隱藏空間
其中關於VAE有一個有趣的事情:在訓練中學習到的隱藏空間有很好的連續性。為了能夠在二維空間中輕鬆地可視化我們的數據點,我們考慮通過二維可視化隱藏空間的特性來實現。
在訓練過程中,我們能夠發現在2維隱藏空間中MNIST數據集被重新劃分了,從中我們可以看到相似的數字被分在了一起(綠色的表示3且都被分在了一起,並且非常靠近數字8,說明這兩個數字非常相似)。
2維圖中可視化隱藏空間
一個更好地從視覺上去理解隱藏空間連續性的方法是:去觀察從隱藏空間中所生成的圖像。我們從下圖中可看出,數字在隱藏空間內進行移動時被平滑地轉換為了與之相似的數字。
二維隱藏空間中抽樣出的數字
結論
VAE是一個令人驚嘆的工具,它依靠神經網絡的幫助能夠解決一些具有挑戰性的問題:生成模型。與傳統方法相比,VAEs解決了兩個主要的問題: 1. 如何在隱藏空間中抽取最相關的隱藏變量來給到輸出。2.如何將隱藏空間中的數據分布映射到真實的數據分布中去。但是,VAE也存在著一些缺點:由於使用的是均方根誤差,它使得生成器是收斂到了平均最優,導致生成的圖像有一點模糊。
而生成對抗網絡(GANs)通過使用鑑別器而不是均方根誤差作為損失能解決這一問題,從而使得生成的圖像更為真實。但是,由於GAN的隱藏空間難以控制,使得它不具有像VAEs那樣的連續性,但這在某些應用場合中卻是必要的。
參考文獻:
[1] Doersch, C., 2016. Tutorial on variational autoencoders. arXiv preprint arXiv:1606.05908.
[2] Kingma, D.P. and Welling, M., 2019. An introduction to variational autoencoders. arXiv preprint arXiv:1906.02691.
[3] MNIST dataset,
作者: Emrick Sinitambirivoutin
Deephub翻譯組:李愛(Li Ai)