理解變分自編碼器(VAE)

概述

變分自編碼器由Diederik P Kingma和Danilo Jimenez Rezende在2013年提出,因為其堅實的理論基礎、應用的多樣性和比MCMC更優異的性能讓它生成模型中得到廣泛的研究和改進,現在已經成為生成模型演算法中的一大主力。如下圖的MNIST的手寫體資料庫圖片的生成和名人圖片就是使用VAE生成的。變分自編碼器理論涉及不少統計學習的內容而目前的實現方法大多是基於深度神經網路,因此這種演算法對初學者造成極大的理解困難。很多工程實踐人員都是拿來別人的代碼之後進行修改以適應自己的需求,因為對原理的不理解造成出現問題時難以修復或都模型訓練時難以達到預期效果而陷入盲目調整參數的境地,因此就想寫一篇通俗的短文介紹一下VAE的原理給初學者提供一點參考。此篇短文會盡量讓VAE所需的預備知識都包含在文中,但是一些非常基礎的數學工具還是認為讀者已經比較好的掌握了的,比如基礎概率論、貝葉斯公式、大學微積分、基礎編碼理論和資訊理論等。本文以圖像識別為例講解VAE,實際上VAE在文本、語音、視頻等領域都可以應用,具體實現採用人工神經網路,實際上VAE也可以用其它的優化技術實現,它是一種很普適的生成模型演算法。

一、關於編碼和壓縮

存儲在計算機中的文件都編碼為01組成的字串,考慮一般我們將文件用winrar,7zip之類的壓縮工具把已經編碼的文件打包壓縮,文件一般都變小了而解壓之後文件並沒有損失。我們原來的文件是比較長的01字串,它可以認為是處在高維的空間中的一個點,而壓縮之後它的緯度降低,變成了更低維空間中的一個點,這說明一般我們的編碼是有冗餘而有被壓縮的空間。再考慮像JPEG這樣的壓縮演算法,我們原始高清數碼相機中的圖片經過JPEG的編碼之後就沒有辦法恢復到原圖的編碼了,這種壓縮方式是有損壓縮,雖然損失了一些信息,但是一般如果我們採用比較低壓縮比的JPEG演算法壓縮圖片,肉眼很難直接觀察出原圖與壓縮之後的圖片之間的差距,但是文件體積卻有巨大的差距,一般壓縮為原圖的1/10大小都看不出明顯的差距。讓我們重新思考這個問題,我們實際生活中視覺看到的圖像也存在巨大的壓縮空間,我們能看到的圖像是整個可編碼空間中佔比非常小的一部分,比如類似電視雪花這樣的圖是不會在真實視覺中看到的,這就說明實際視覺信息可以被壓縮到更低緯度的空間中而基本上不損失信息。如果限制視覺的範圍,比如針對只生活在叢林中的動物或是某一類視頻遊戲,它的視覺將有可能被壓縮到更加低緯度的空間中。

二、神經網路

神經網路的最基本結構如下圖所示:

基本結構包含輸入層,隱藏層和輸出層,隱藏層的數目不一定是1屋,可以是非常多的層,而且結構和連接方式也多種多樣,上圖的結構只是最簡單的結構,關於神經網路結構和優化方法相關內容與本文關係不大,我們只認為它是一個輸入緯度為M,輸出緯度為N的黑匣子,它是一個可以通過一定的優化演算法調整為任意接近一個非病態函數的黑匣子,在本文中我們用它來不斷擬合分布函數(實際上是擬合分布函數中的控制參數)

三、自編碼器?

如果我們把神經網路設計成輸入與輸出維度相同,讓它學習恆等函數x=f(x),但是中間的某個隱藏層的維度比輸入輸出層都小得多,它就成為了一個自編碼器,這個隱藏層稱為瓶頸層(bottleneck)具體如下圖所示:

因為中間某個隱藏層的維度比輸入輸出層的緯度小很多,在神經網路推理計算過程中如果我們把瓶頸層的參數拿出來,通過神經網路向後傳播就產生了輸出值x,這相當於一個低維到高維的解壓縮過程,而輸入層到瓶頸層的高維到低維的過程可以認為是一個壓縮過程。當然我們的討論是理想情況,神經網路基本上不可能實現100%的x=f(x),在壓縮之後又解壓的過程中會產生損失,這個可以類比於有損壓縮。

四、信息熵和KL散度

信息熵是用來描述隨機變數不確定性程度的量,對於連續型隨機變數稱為微分熵。 H(x) = - int p(x) ln^{p(x)} 它基本上可以認為表徵編碼分布 p(x) 所需要的信息編碼長度(單位是nats),如果我們使用另一個用於編碼分布函數 q(x) $的編碼去編碼 p(x),因為 q(x)p(x) 之間可能存在差距,編碼效率不是最高的,因此一定要付出額外的一些冗餘編碼來對分布函數進行編碼。總編碼長度為:

H(p,q) = - int p(x)ln^{q(x)}

$H(P,Q)$稱為交叉熵(cross entropy),那麼多付出的編碼長度就是KL散度(KL divergence):

KL(pmid mid q) = H(p,q)-H(p) = - int p(x) ln frac{q(x)}{p(x)}

KL散度大於等於零並且KL散度一般沒有對稱性, KL(pmid mid q) 
eq KL(q||p) 也就是說用編碼p的最優編碼去編碼q所需要付出的額外編碼一般不等於使用編碼q的最優編碼去編碼p所需要付出的額外編碼。KL散度在機器學習中非常重要,一般我們會要讓一個參數化的分布q去接近一個真實分布p,在此過程中會去優化 KL(q||p) 而使它最小,但是當計算 KL(q||p) 不方便的時候也會去嘗試計算優化 KL(p||q) 而讓它最小化。

在嘗試使用KL散度優化時,不同的優化方向可以經驗性的認為有如下特性, KL(p||q) = - int p(x) ln q(x) -H(p) ,我們優化的是參數化的q,當p的值較大時,相應q的值一般要要求較大,這樣才能讓$KL(p||q)$的值變小,也就是說q會擬合p比較大的值的地方但是可能的副作用是在p比較小的值處q值也比較大,相反當我們優化 KL(q||p) 時, KL(q||p) = - int q(x) ln p(x) -H(q) ,此時需要H(q)的值盡量大但是在p比較小的位置q也要比較小,也就是說這種優化方向趨向於在p比較小的位置q也比較小,副作用是在p比較大的位置q也可能會比較小,具體可參考下圖:

五、變分自編碼器

有了以上的預備知識,我們可以開始闡述變分自編碼器的原理了。在上面的神經網路自編碼器中,對於瓶頸層的編碼特徵我們沒有做任何的限制,如果現在學習到的分布函數為f,那麼我們可以使用一個非線性變換把其變換為 g(f) ,把這個變換放入編碼器(encoder)層,之後在解碼器層加上一個非線性逆變換 g^{-1} ,這時瓶頸層就成了我們已知的分布 p(z) 。實際上這是可行的,一個分布f幾乎總是可以通過變換成為一個目標分布,只要目標分布空間夠通用,維度夠大。我們在實際操作中並不要自己去尋找變換g和逆變換 g^{-1},只要在優化過程中限定瓶頸層的分布是 p(z) 就可以了,讓SGD這樣的演算法自動去尋找需要的神經網路參數。

變分自編碼器的結構如下所示:

我們一般把z的分布函數限定為均值為0,方差為1的標準高維高斯分布,這樣便於我們後期從這個分布中隨機取樣後用解碼器生成新的樣本。編碼器網路 q_{	heta}(zmid x) 接受輸入x(x為圖像數據,對於MNIST手寫識別數據集就是一個28*28的01矩陣,扁平化之後是784維),輸換為低維編碼空間Z中的條件分布參數,這個條件分布 p(zmid x) 我們是不知道的,但是我們可以用一維已經的分布函數去擬合它,如果Z的空間維度為h,假設我們使用的擬合z的條件分布 q_{	heta}(z|x) 是一維相互獨立的高斯分布,那麼實際上編碼器將用於生成這些高斯分布的參數 (mu_{1},sigma_{1}^{2}),(mu_{2},sigma_{2}^{2}),...,(mu_{h},sigma_{h}^{2}),之後我們從條件分布 q_{	heta}(zmid x) 中取樣,就可以使用解碼網路來生成新的樣本(實際上我們也不是直接生成新的樣本,而是生成樣本分布函數中的參數,比如28*28的黑白圖像我們只要生成每個像素對應的伯努利分布的參數 mu_i(i=1,...,784) ,再從這個聯合分布中取樣就可以了。

上面我們使用一組參數化的分布函數去接近後驗分布$p(z|x)$的做法,就是變分編碼的核心內容,這個技巧在其它文獻中也稱為變分貝葉斯推理。我們在上面的神經網路結構中並沒有寫出最終需要的損失函數,沒有損失函數神經網路肯定是沒有辦法訓練的。下面對於損失函數的推理過程就是變分自編碼器的核心原理。我們把用來擬合後驗分布函數$p(z|x)$的一組參數記為 lambda ,這組參數實際上對於每個輸入x是特異的,比如對於輸入 x_1{lambda_1}_{x_1} = ({mu_1}_ {x_1},{sigma_1}_{x_1}^2),{lambda_2}_{x_1} = ({mu_2}_ {x_1},{sigma_2}_{x_1}^2),...

由貝葉斯公式有

p(zmid x) = frac{p(xmid z)p(z)}{p(x)}

注意等式右邊, p(x|z) 稱為似然(likelihood),如果我們知道每個圖像的編碼z,我們是可以直接優化神經網路擬合出 p(x|z) 的,同時我們也假設先驗概率$p(z)$的分布是標準高斯分布,最大的問題來自p(x),p(x)稱為證據分布(evidence), p(x) = int p(x|z)p(z)dz ,p(z)是高維空間的分布密度函數,積分對於高維空間是沒有可操作性的,因此我們必須找其它辦法求$p(z|x)$,這也是變分擬合操作存在的原因。這也也提一個,擬合這個後驗分布函數也可以使用馬爾科夫鏈蒙特卡羅方法(MCMC),不過一般MCMC演算法對於圖像數據,語音數據,視頻數據這類高維數據計算量非常巨大,基本在算力上不可行。

KL(q_{lambda}(zmid x)mid mid p(zmid x)) = E_{q_{lambda}}[ln^{q_{lambda}(zmid x)}]-E_{q_{lambda}}[ln^{p(x,z)}]+ln^{p(x)}

p(x)是數據的分布,與參數無關,因此我們最小化KL散度時與其無關,定義:

ELBO_lambda = E_{q_{lambda}}[ln^{p(x,z)}]-E_{q_{lambda}}[ln^{q_{lambda}(zmid x)}]

那麼

KL(q_{lambda}(zmid x)mid mid p(zmid x))+ELBO_lambda = ln^{p(x)}

因為KL散度不為負,因此 ln^{p(x)} geq ELBO_lambda , ELBO_lambda 就是 ln^{p(x)} 的下界(evidence lower bound),最小化變分後驗概率分布與理論後驗概率分布的KL散度相當於最大化 ELBO_lambda ,對於每一個數據輸入 x_i ,有

ELBO_{lambda_{x_i}} = E_{q_{lambda_{x_i}}}[ln^{p({x_i},z)}]-E_{q_{lambda_{x_i}}}[ln^{q_{lambda_{x_i}(zmid x)}}]

 = E_{q_{lambda_{x_i}}}[ln^{p(x_i mid z)}+ln^{p(z)}] - E_{q_{lambda_{x_i}}}[ln^{q_{lambda_{x_i}(zmid x)}}]

 = E_{q_{lambda_{x_i}}}[ln^{p(x_i mid z)}] - KL(q_{lambda_{x_i}}(z mid x_i)mid mid p(z))

令:

l_i = KL(q_{lambda_{x_i}}(z mid x_i)||p(z))-E_{q_{lambda_{x_i}}}[ln^{p(x_imid z)}]

此為單個輸入值時神經網路的損失函數,這裡面沒有不可計算的變數,可以使用隨機梯度下降(SGD)之類的訓練演算法訓練神經網路,也可以配合批正則化(batch normalization)去降低神經網路訓練過程中的梯度彌散問題,讓訓練結果更加穩定可靠。再重新考慮一下$l_i$的意義,第一項相當於給定數據後的條件後驗概率與先驗概率的距離,最小化此項可以讓z最終的分布接近我們設定的先驗分布,可以認為這是一個正則修正項,否則最終z的分布如是不符合我們設定的先驗分布,我們從先驗分布中隨機取的點就不能被解碼器網路正確解碼,第二項是給定數據下對z條件分布下的對數似然函數,當最小化 l_i 時就相當於最大化這個對數似然函數,相當於我們要讓訓練數據中編碼器編碼過的z,再經過解碼器後要與原始數據差距比較小,減少在壓縮編碼和解碼過程中產生的雜訊。

### 六、參考資料

[1] Tutorial - What is a variational autoencoder? – Jaan Altosaar

[2] Variational Autoencoders Explained

[3] Conditional Variational Autoencoders

[4] Variational Inference: A Review for Statisticians A Review for Statisticians

[5] Tutorial on Variational Autoencoders [1606.05908] Tutorial on Variational Autoencoders

[6] github.com/altosaar/var

[7] Deep Learning (Ian Goodfellow)、 (Yoshua Bengio)

[8] Pattern Recognition and Machine Learning (Christopher M. Bishop)


推薦閱讀:

常見的深度學習模型--卷積神經網路概述
一種分散式環境下的協作機器學習模式
人工智慧——狀態機
DIY發現新行星操作指南 | 谷歌開源了行星發現代碼
邏輯回歸(LogisticRegression)推導與實戰

TAG:人工智慧演算法 |