網頁

2019年10月10日 星期四

Variational Autoencoder: Intuition and Implementation

目前對於數據生成的神經網路架構存在兩種並駕齊驅的生成模型:生成對抗網絡(GAN)和變異自編碼器(VAE)。 這兩種模型的訓練方式相當不同。 GAN來自於game theory,其目標是找到discriminator網絡與generator 網絡之間的Nash Equilibrium。 另一方面,VAE來自於bayesian inference,也就是建立數據的基本機率分佈模型,以便可以從該分佈中採樣新數據。

在本文中,我們將直觀地研究VAE模型及其在Keras中的實現。



VAE: Formulation and Intuition
假設我們要生成一個數據。 做到這一點的最好方法是首先決定我們要生成哪種數據,然後實際生成數據。 例如,假設我們要生成動物。 首先,我們想像一下動物:它必須有四隻腿,並且必須能夠游泳。 有了這些標準,我們便可以通過從動物界取樣來實際產生動物。 瞧,我們得了鴨嘴獸!

從上面的故事來看,我們的想像力類似於潛在變量(latent variable)。 首先確定生成模型中的潛在變量通常很有用,因為潛在變量可以描述我們的數據。 沒有潛在變量,就好像我們只是盲目生成數據一樣。 這就是GAN和VAE之間的區別:VAE使用潛在變量,因此它是一種表達模型。

但我們該如何建模呢? 讓我們談談概率分佈吧。

讓我們定義一些概念:
1. X:建立動物模型的數據
2. z:潛在變量又稱之為我們的想像力
3. P(X):動物界數據的概率分佈
4. P(z):潛在變量的概率分佈,也就是我們的想像力的來源
5. P(X|z):在給定潛在變量其生成數據的分佈,也就是將想像力變成真正的動物

我們的目標是對數據建立模型,因此我們想找到P(X)。使用概率定律,我們可以找到與
z的關係如下:

P(X)=P(X|z)P(z)dz
也就是說,從聯合概率分佈P(X,z)我們將z轉換成X
z
現在只要我們知道P(X,z),等效於P(X|z)P(z)

VAE的想法是利用P(z|X)推斷P(z)。 我們希望潛在變量是跟我們的數據有關。 例如,我們只想限制動物界的想像力,所以我們不應該想像根,葉,輪胎,玻璃,GPU,冰箱,門墊等……因為這些東西不太可能與動物界的事物有關。

但是問題是,我們必須推斷出這種分佈P(z|X),因為我們尚不知道。 在VAE中,顧名思義,我們使用一種稱為Variational Inference(VI)的方法來推斷P(z|X)。 VI是bayesian inference中一種流行的方法選擇,另一種是MCMC方法。 VI的主要想法是藉由優化問題來推論,進而建立真實分佈模型。

使用較簡單的分佈,例如Gaussian,並使用KL divergence metric最小化這兩個分佈之間的差異,來建立真實分佈P(z|X),而且易於評估由潛在變量預測的概率分佈Q與真實分佈P的差異有多大


好吧,現在假設我們要用Q(z|X)推斷P(z|X),KL divergence公式如下:


回想一下上面的符號,我們沒有使用三件事,即P(X)P(X|z)、P(z)。但是,根據Bayes’ rule,我們可以使它出現在等式中:


因為zP(X)不相關,所以我們將兩者分別移到等式兩邊。


如果我們仔細看等式的右邊,我們會注意到它可以被重寫為另一個KL散度。 因此,讓我們首先重新排列符號。


就是這樣,VAE目標函數:


在這一點上,我們有什麼? 讓我們列舉一下:
1. Q(z|X)為投影真實數據X到潛在變量
2. z為潛在變量
3. P(X|z)為從已知潛在變量生成數據

我們可能會對這種結構感到熟悉。 猜猜看,它的結構與自動編碼器中的結構相同! 換句話說,Q(z|X)是encoder net,z是encoded representation,並且P(X|z)decoder net! 這也難怪這個模型的名字叫變異自編碼器!

VAE: Dissecting the Objective
事實證明,VAE目標函數具有很好的解釋。 也就是說,我們要在DKL[Q(z|X)P(z|X)]的誤差情況下,用logP(X)描述來建立我們的數據模型,換句話說,VAE試圖找到logP(X)的下限,也就是足夠好就行了,因為實際上很難找到完美的分佈。

然後可以透過最大化從潛在變量到數據P(X|z)的映射來找到該模型,並最小化我們的簡單分配Q(z|X)和真正的潛在分佈P(z)之間的差異。

我們可能已經知道,最大化E[logP(X|z)]是最大似然估計(maximum likelihood estimation)。 我們基本上總是在discriminative監督模型中看到它,例如Logistic Regression,SVM或Linear Regression。 換句話說,給定輸入z和一個輸出X情況下,在某些模型參數中我們想最大化條件分佈P(X|z)。 所以我們可以藉由任何帶有輸入z和輸出X的分類器來實現它,然後通過使用log loss或regression loss來優化目標函數。

關於什麼是DKL[Q(z|X)P(z)]? 在這裡,P(z)是潛在變量分佈。 我們可能之後要採樣P(z),所以最簡單的選擇是N(0,1)。因此,我們要使Q(z|X)盡可能接近N(0,1)以便我們就可以輕鬆地對其進行採樣

P(z)=N(0,1)也增加了另一個好處。假設我們也想要Q(z|X)用參數μ(X)Σ(X)為高斯分佈,即給定X的均值和方差。然後,可以以封閉形式計算這兩個分佈之間的KL divergence!

上面的k是我們高斯的維度。 tr(X)是跡函數(trace function),即矩陣X對角線的總和。對角矩陣的行列式可以計算為其對角線的乘積。 因此,實際上,我們可以將Σ(X)當作對角矩陣來實現為向量:


但實際上,最好對logΣ(X)建立Σ(X)模型,因為與計算log相比,exponent在數值上更穩定。 因此,我們最終的KL divergence項為:


Implementation in Keras
首先,我們實現encoder net Q(z|X),該網絡接受輸入X並輸出兩種參數:μ(X)),即Gaussian參數。

from tensorflow.examples.tutorials.mnist import input_data
from keras.layers import Input, Dense, Lambda
from keras.models import Model
from keras.objectives import binary_crossentropy
from keras.callbacks import LearningRateScheduler

import numpy as np
import matplotlib.pyplot as plt
import keras.backend as K
import tensorflow as tf


m = 50
n_z = 2
n_epoch = 10


# Q(z|X) -- encoder
inputs = Input(shape=(784,))
h_q = Dense(512, activation='relu')(inputs)
mu = Dense(n_z, activation='linear')(h_q)
log_sigma = Dense(n_z, activation='linear')(h_q)
也就是說,我們的Q(z|X)是具有一個隱藏層的神經網絡。 在此實現中,我們的潛在變量是二維的,因此我們可以輕鬆地對其進行可視化。 但是在實際中,多維的潛在變量應該更好。

但是,我們現在面臨一個問題。 我們如何從encoder 輸出中獲得z? 顯然,我們可以從Gaussian採樣z,其中參數是encoder 的輸出。 因為採樣操作沒有梯度,所以不會使用梯度下降來訓練VAE!

但是,有一個技巧稱為reparameterization 技巧,它使網絡具有差異性。 reparameterization 技巧基本上將不可微分的操作轉移到網絡之外,即使我們仍然涉及不可微分的事物,至少它是在網絡之外,因此仍可以對網絡進行訓練。

reparameterization 技巧如下。 回想一下,如果我們有xN(μ,Σ),然後將其標準化以使μ=0,Σ=1,則可以通過還原標準化過程將其還原為原始分佈。 因此,我們有以下等式:
考慮到這一點,我們可以擴展它。 如果我們從標準常態分佈中採樣,則在知道均值和方差的情況下,可以將其轉換為所需的任何高斯分佈。 因此,我們可以通過以下方式實現z的採樣操作:
這裡ϵN(0,1)。

現在,在反向傳播過程中,我們不再關心採樣過程,因為它現在不在網絡中,也就是說,它不再依賴於網絡中的任何內容,因此梯度不會通過z

def sample_z(args):
    mu, log_sigma = args
    eps = K.random_normal(shape=(m, n_z), mean=0., std=1.)
    return mu + K.exp(log_sigma / 2) * eps


# Sample z ~ Q(z|X)
z = Lambda(sample_z)([mu, log_sigma])

現在我們創建decoder net P(X|z):

# P(X|z) -- decoder
decoder_hidden = Dense(512, activation='relu')
decoder_out = Dense(784, activation='sigmoid')

h_p = decoder_hidden(z)
outputs = decoder_out(h_p)

最後,根據該模型,我們可以做三件事:重構輸入,將輸入編碼為潛在變量,並從潛在變量生成數據。 因此,我們有三種Keras模型:

# Overall VAE model, for reconstruction and training
vae = Model(inputs, outputs)

# Encoder model, to encode input into latent variable
# We use the mean as the output as it is the center point, the representative of the gaussian
encoder = Model(inputs, mu)

# Generator model, generate new data given latent variable z
d_in = Input(shape=(n_z,))
d_h = decoder_hidden(d_in)
d_out = decoder_out(d_h)
decoder = Model(d_in, d_out)

然後,我們需要將loss轉換為Keras代碼:

def vae_loss(y_true, y_pred):
    """ Calculate loss = reconstruction loss + KL loss for each data in minibatch """
    # E[log P(X|z)]
    recon = K.sum(K.binary_crossentropy(y_pred, y_true), axis=1)
    # D_KL(Q(z|X) || P(z|X)); calculate in closed form as both dist. are Gaussian
    kl = 0.5 * K.sum(K.exp(log_sigma) + K.square(mu) - 1. - log_sigma, axis=1)

    return recon + kl

然後訓練它:

vae.compile(optimizer='adam', loss=vae_loss)
vae.fit(X_train, X_train, batch_size=m, nb_epoch=n_epoch)

Implementation on MNIST Data
在訓練了VAE模型之後,我們可以可視化潛在變量空間Q(z|X)

Q(z \vert X)
我們可以看到,在潛在空間中,數據的representation具有相同特徵,例如 相同的標籤彼此靠近。 請注意,在訓練階段,從不提供數據任何資訊。

我們還可以將數據遍歷整個VAE網絡中來查看數據重構:

Reconstruction
最後,我們可以通過第一個樣本z生成新樣本,並將其輸入到我們的decoder net中:

Generation
如果仔細查看重建和生成的數據,我們會注意到其中一些數據是模棱兩可的。 例如,數字5看起來像3或8。這是因為我們的潛在變量空間是連續分佈(即N(0,1)),因此在clusters的邊緣一定會有一些平順轉換(smooth transition)。 而且,如果它們有點相似則數字cluster 彼此接近。 這也是為什麼在潛在空間中5接近3。

Conclusion
在這篇文章中,我們直觀地研究了變異自編碼器(VAE)其背後公式,及在Keras中的實現。

我們還看到了當今兩種最流行的生成模型VAE和GAN之間的區別。

有關VAE的更多數學知識,請務必參閱Kingma等人(2014年)的原始論文。Carl Doersch撰寫的關於VAE的教程也很棒。 查看下面的參考部分。


此外,VAE優點是可以通過編碼解碼的步驟,直接比較重建圖片和原始圖片的差異,但是GAN做不到。但缺點是VAE的一個劣勢就是沒有使用對抗網路,所以會更趨向於產生模糊的圖片。也有一些結合VAE和GAN的工作:使用基本的VAE框架,但是用對抗網路去訓練解碼器。

參考:https://arxiv.org/pdf/1512.09300.pdf 和http://blog.otoro.net/2016/04/01/generating-large-images-from-latent-vectors/




沒有留言:

張貼留言