引言
前面三篇文章介紹了變分推斷(variational inference),這篇文章將要介紹變分自編碼器,但是在介紹變分自編碼器前,我們先來了解一下傳統的自編碼器。
自編碼器
自編碼器(autoencoder)屬於無監督學習模型(unsupervised learning),更嚴格地說屬於自監督學習(self-supervised learning)。在自監督學習中,預測的目標來自輸入數據本身,不需要人工標定——有監督學習(supervised learning),例如圖像分類任務,需要人工標注圖像中的物體是貓還是狗或者是其他東西。自編碼器簡單地說,就是實現將$x$轉換為$y$,再將$y$轉換回$x$的模型,如$(圖1)$所示。
自編碼器主要由編碼器(encoder)和解碼器(decoder)兩個模塊組成。編碼器的任務是將輸入數據編碼成某種表征,而解碼器的任務是將編碼器的輸出作為輸入,將其解碼成目標數據。不過Encoder-decoder的結構不僅用於自編碼器,在普通的分類和回歸任務上都有運用。例如,機器翻譯中,編碼器將原語言編碼成隱表征,然后解碼器再將隱表征翻譯到目標語言,也就是一個由$x$到$y$再到$z$的過程。
(圖1,來自https://blog.keras.io/building-autoencoders-in-keras.html)
自編碼器的預測目標是輸入本身,模型訓練的目標是減小輸出與輸入的差異,可以表示為:
\begin{equation} \argmin_{E,D} E[L(x, D(E(x)))] \nonumber \end{equation}
其中$ \argmin_{E,D}$表示取編碼器$E$和解碼器$D$,使重構的$x$與原$x$差異盡可能小,也就是最小化重構誤差$L$。這樣的模型並不能直接用於我們常見的分類或回歸這樣的任務,因為這些任務的輸出與輸入是不同的。自編碼器更多的是用來對數據進行降噪(denoising)——如$(圖2)$所示,或者實現數據降維(dimension reduction)、數據壓縮(compression)——例如將10維的向量轉換為2維的向量——或者是作為其他模型的一個模塊,例如通過對輸入進行特定處理,從而讓模型學習某些特定的數據表征(latent representation)。
(圖2,來自論文《autoencoders》)
自編碼器的實現
實現自編碼器的方法有幾種,大體上可以分為欠完備自編碼器(undercomplete autoencoder)和正則化自編碼器(regularited autoencoder)。
欠完備自編碼器
欠完備自編碼器,是通過在輸入層和輸出層之間插入一個維度較低的瓶頸層(bottleneck)——例如輸入層和輸出層的維度都是700,而瓶頸層的維度是30——或者采用維度逐層遞減的編碼器,和維度逐層遞增的解碼器組成的模型。插入瓶頸層的自編碼器又稱為香草自編碼器(vanilla autoencoder)——香草通常表示最簡單的方法。
原始的自編碼器是多層感知機(MLP),內部采用的是全連接層,在處理圖像數據的時候,將全連接層替換為卷積層通常能得到更好的結果,而在處理序列型數據的時候,我們則可以采用RNN等序列模型。另外,當編碼器和解碼器都是線性模型,例如我們把神經網絡中的非線性的激活函數去掉,此時自編碼器就是線性自編碼器。如果在訓練線性自編碼器時采用平方誤差損失函數(squared error cost function),那么編碼器輸出的隱表征與主成分分析(Principal Component Analysis,PCA)相似(趕時間,暫時不推導了,請參考論文$[1]$)。
正則化自編碼器
正則化的方法有很多,例如可以使用L1正則化對神經網絡的激活函數的輸出進行處理。假設多多層感知機有三層,我們只對之間那層進行約束,那么自編碼器的目標函數為:
\begin{align} &\argmin_{E,D} E[L(x, D(E(x)))] + \lambda \sum_i {|\bar{a}_i|} \nonumber \\ & \bar{a}_i = \frac{1}{n} \sum_j^n {a_i(x_j)} \end{align}
其中$\lambda$是超參數,由人設定,$a_i$是中間層第$i$個激活函數的輸出,它是對$n$個數據計算后求平均的結果,$|·|$是L1正則化。
L1正則化方法需要設置超參數$\lambda$,如果采用KL散度來對激活函數進行約束,則可以避免設置超參數——要設其實也可以。在這種方法中,我們將激活函數的輸出看成伯努利分布(Bernoulli distribution)——樣本為1或0,為1的概率為$p$,$0<p<1$,這和sigmoid函數的取值范圍一致。這樣,自編碼器的目標函數就變為:
\begin{align} &\argmin_{E,D} E[L(x, D(E(x)))] + \sum_i {KL(p \parallel \hat{p}_i)} \label{1} \\ & \hat{p}_i = \frac{1}{n} \sum_j^n{a_i(x_j)} \nonumber \end{align}
其中$\hat{p}_i$對應第$i$個激活函數對$n$個數據的平均值,$p$為是一個較小的值(例如0.05),這樣就可以使神經元變得稀疏(值趨近於0),所以這種方法與L1正則化是相似的。
另一種正則化的模型是CAE(Contractive AutoEncoder)。這是由Bengio等人提出來的,它的目標是降低模型對數據波動的敏感度。在GAN專題的介紹WGAN的篇章,有一個概念叫Lipschitz約束:
\begin{equation} \frac{\parallel f(x_1) - f(x_2) \parallel}{\parallel x_1 - x_2 \parallel} \leq k \nonumber \end{equation}
其中$k$是某個值,等號左邊要小於等於它。使模型滿足這種約束的一種方法是梯度懲罰(gradient penalty),因為上面的式子可以認為是在計算梯度的絕對值。梯度,也就是一階導,從向量和矩陣的角度講,就是雅可比矩陣(Jacobian matrix)。CAE模型也是要對Jacobian矩陣進行約束,從而使模型更加魯棒(robust),不會對數據太敏感。CAE的目標函數如下:
\begin{equation} \argmin_{E,D} E[L(x, D(E(x)))] + \lambda \parallel J_E(x) \parallel_2^2 \nonumber \end{equation}
其中$\parallel J_E(x) \parallel_2^2$是計算雅可比矩陣的Frobenius范數,$J_E(x)$是Jacobian矩陣,它的基本形式如下:
\begin{bmatrix} \frac{\partial h_1}{\partial x_1} & \frac{\partial h_1}{\partial x_2} & \dotsb & \frac{\partial h_1}{\partial x_n} \\ \frac{\partial h_2}{\partial x_1} & \frac{\partial h_2}{\partial x_2} & dotsb & \frac{\partial h_2}{\partial x_n} \\ \vdots & \vdots & \ddots & \vdots \\ \frac{\partial h_n}{\partial x_1} & \frac{\partial h_n}{\partial x_2} & dotsb & \frac{\partial h_n}{\partial x_n} \end{bmatrix}
而Frobenius范數是矩陣所有元素$J_{i,j}=\triangledown_{x_i} h_j(x_i)$的平方和再取平方根$\sqrt{\sum_{i,j}{J_{i,j}^2}}$,$h_j$是第$j$個神經元$h = a(Wx + b)$。
變分自編碼器
式$(\ref{1})$向我們展示了自編碼器目標函數的一種構造方法,其中KL散度作為正則項(regularizer)去控制模型內部的神經元。我們可以將$(\ref{1})$中的KL散度用另一個KL散度替換:
\begin{equation} \argmin_{E,D} E[L(x, D(E(x)))] - \sum_i {KL(q(z|x) \parallel p(z|x))} \label{2} \end{equation}
其中$q(z|x)$是變分分布,$p(z|x)$是真實的后驗分布,$z$是隱變量。就像前面文章說的,我們可以把分類任務中的標簽看作隱變量,所以$p(z|x)$是通過$x$推斷標簽$z$。另外,式中的變分分布$q(z|x)$也可以是$q(z)$的形式,也就是不以$x$作為條件,但我們這里以$q(z|x)$作為編碼器。
(圖3,來自https://towardsdatascience.com/intuitively-understanding-variational-autoencoders-1bfe67eb5daf)
式$(\ref{2})$並不是變分自編碼器的目標函數,因為就像前面的文章中提到的,后驗$p(z|x)$是難以計算的,要得到變分自編碼器的目標函數,我們要用$ELBO$替換了這里的KL散度。$ELBO$的數學表示如下:
\begin{align} ELBO(q) &= E_q [\log{p(x|z)} ] - KL(q(z|x) \parallel p(z)) \label{3} \\ &= E_q [\log{p(z,x)} ]- E_q [\log{q(z)} ] \label{4} \end{align}
其中$p(x|z)$是似然,可以看作是解碼器,$p(z)$是隱變量$z$的先驗(prior)。優化式$(\ref{3})$,可以看作是求最大似然(maximum likelihood),同時最小化KL散度。(提一點,僅僅從理論上看,因為似然可以看作的解碼器,變分自編碼器並不需要一個真正的解碼器模塊存在,也就是說式$(\ref{2})$前面的重構誤差可以不要,所以一些文章認為變分自編碼器不是真正意義上的自編碼器。)
為了求解$ELBO$,我們需要對$z$進行采樣。假設$z \sim q(z|x)=N(\mu,\sigma^2)$服從某個高斯分布,也就是說我們把高斯分布作為$q(z|x)$的族,優化$ELBO$就是求高斯分布的參數$\mu$和$\sigma^2$,使$ELBO$最大。得到參數$\mu$和$\sigma^2$后,我們就可以從高斯分布中采樣$z$。具體方法是通過$z=g(\epsilon, x)=\mu + \sigma \epsilon$。這里$\epsilon$是一個隨機噪音,服從高斯分布$N(0,I)$,也就是說向量$\epsilon$中的元素的值在[0,1]區間。這樣設置$\epsilon$,采樣得到的$z$能夠服從$N(\mu,\sigma^2)$分布。這個采樣過程如$(圖3)$所示。我們用某個函數$g(\epsilon,x)$代替$q(z|x)$,用$\mu$和$\sigma$代替$z$。這是一種重參數化(reparameterization)的方法——重參數化在GAN專題中介紹參數歸一化(weight normalization)時也有提到。
經過重參數化,並且采用stochastic的策略,我們就將$ELBO$變為:
\begin{align} &ELBO= \frac{1}{M} \sum_{m=1}^M {\log{p(x_i, z_{i,m})}} - \log{q(z_{i,m},x_i)} \nonumber \\ &z_{i,m} = g(\epsilon_{i,m}, x_i) \nonumber \end{align}
因為式$(\ref{3})$中的KL散度是可以求解析解的,所以也可以采用下面的式子:
\begin{align} &ELBO = \frac{1}{M} \sum_{m=1}^M {\log{p(x_i| z_{i,m})}} -KL(q(z|x) \parallel p(z)) \nonumber \\ &z_{i,m} = g(\epsilon_{i,m}, x_i) \nonumber \end{align}
其中$M$是批數據的數據量,它可以是較小的值,甚至是1。
進階實例
為了能夠更深入地理解自編碼器,這里我們對一篇論文中的模型進行介紹。這篇論文是$[2]$。在這篇論文中,作者介紹了自編碼器的一種特殊形式,ALAE,它結合了自編碼器和GAN兩種模型,(如圖4所示)。
相比於傳統的自編碼器,編碼器與解碼器的目標是將輸入的$x$轉換成隱藏層$h$(論文中的$w$),然后再將$h$轉化為$x$,這里的自編碼器則是實現數據從$h$的隱分布(latent distribution)到$x$對應的真實數據分布,最后再輸出服從$h$的分布的數據的過程。
但是在$h \to x \to h$這樣的編碼解碼模式下,服從隱分布的數據$h$來自哪呢?作者團隊采用了GAN的生成器的方法,預先設定一個分布,然后通過生成器$F$將此分布的數據$z$映射到$h$的隱分布,這樣就得到了$h$,剩下的就是將$h$轉換為$x$再轉化為$h$。
有了$h$,現在的問題是,如何保證模型生成的$x$服從真實的$x$的分布呢?為了解決這個問題,作者采用了GAN中的對抗機制(adversary)。來自真實數據的$x$和生成的$x$一樣,都會經過解碼器$E$轉換成$h$,生成的$h$會作為判別器$D$的輸入。判別器$D$的作用是盡可能的分辨$h$是來自真實的$x$還是生成的$x$,而判別器$D$之前的模型相對於GAN的生成器,目標是使兩種$h$的分布盡可能一致,使判別器$D$難以分辨。通過這樣的對抗機制,模型就能保證生成的$x$的分布接近真實的分布。
為了保證經過編碼轉換為$x$后,再經過解碼能返回原先的$h$,模型在優化的時候,需要減小輸入的$h$與解碼得到的$h$的差異。另外,為了讓模型更具有普遍性,編碼器$G$的輸入除了$h$,還有噪音 。
在應用預測的時候,模型的模塊會重新組織,與傳統的自編碼器一致(如圖4中的Inference)。真實數據,例如圖像,會經過上面的解碼器$E$(在這里作為編碼器),生成隱藏層$h$,然后$h$再經過上面的編碼器$G$(這里作為解碼器)輸出最終的結果,也就是圖像或其他真實數據。
對於論文中,模型在訓練和在預測的時候采用不同的組織方式,原因是這樣能夠更有效地運用正則化處理。以傳統的自編碼器的訓練方式,進行正則化處理,數據經過編碼器和解碼器輸出的結果質量會變差。例如在圖像處理中,輸出的圖像會變得模糊。但如果是對隱藏分布進行正則化處理,就不會有這樣的限制。選擇任何方式,只會對隱藏分布產生影響,都不會對真實數據的分布產生太大的影響。
除了基礎的ALAE模型,作者還借鑒了StyleGAN——在GAN專題的最后一篇中有介紹——設計了StyleALAE。在StyleALAE中,作者將$H$作為中間隱藏空間(intermediate latent space)。數據$z$在$F$中的各層通過實例標准化層(IN,Instance Normalization layer)轉化為對應的實例均值和標准差,作為這一層的風格(style)。這些風格信息在經過AdaIN(Adaptive Instance Normalization layer)生成$h$,然后與噪音一起,經過卷積層轉化為$x$。
(圖4,來自論文《adversarial latent autoencoders》)
[1] Plaut, E. (2018). "from_principal_subspaces_to_principal_components_with_linear_autoencoders".
[2] Pidhorskyi, S., Adjeroh, D., A., Doretto, G. (2020). "Adversarial Latent Autoencoder".