# VAE (Variation Autoencoder)
[TOC]
**Github Code** : https://github.com/jason19990305/VAE.git
## Introduction
**Auto-Encoder** 代表這個 **Neural Networ** 有 **Encoder** 和 **Decoder** 兩個過程,Input 的大小會壓縮的很小,變成 **Code**,然後 **Decoder** 透過這個 **Code** 計算出 **Output**,這個 Output 要跟 **Input** 的資料越接近越好,這邊的 **NN** 也可以用 **CNN**,拿 **MNIST** 手寫數字這個資料集來描述,大致流程如下
1. **Conv2d、MaxPooling** : `1x28x28` -> `16x14x14`
2. **Conv2d、MaxPooling** : `16x14x14` -> `32x7x7`
3. **Flatten** : `32x7x7=1568`
4. **Linear** : `1568 -> 10`
這個部分就是 **Encoder**,最後會 **Output** 出維度為 **10** 的 **Vector**
**Decoder** 則負責從向量恢復成原來的樣子,也就是變成原來的手寫數字
1. **Linear** : `10 -> 1568`
2. **Reshape** : `1568 -> 32x7x7`
3. **ConvTranspose2d** : `32x7x7` -> `16x14x14`
4. **ConvTranspose2d** : `16x14x14` -> `1x28x28`
**ConvTranspose2d** :

這樣就可以用來訓練能恢復原本圖片的 **Neural Network**
## VAE

**VAE (Variational Auto-Encoder)** 與 **Auto-Encoder** 的差異,就是將 **Encoder** 的 **Output Code**,變成用 $\mu$ 和 $\sigma$ 來描述,其實也就是用 **Probability Distribution** 來描述一張圖,並透過重參數化技巧(**Reparameterize**),在訓練期間加入雜訊,然後 **Decoder** 一樣用來恢復圖片,**VAE** 對於生成圖片的效果並沒有到很好,生成的圖片通常不構銳利,基本上都是蠻模糊的,但他在 **Encoder** 的部分卻有很多用處,因為訓練時帶入雜訊,所以抗噪能力強,不會死記硬背,能夠較好的學習圖片的相似度,所以他會是一個很 **General** 的降維、提取特徵 **Model**。
## Forward
**Encoder** :
1. **Conv2d、MaxPooling** : `1x28x28` -> `16x14x14`
2. **Conv2d、MaxPooling** : `16x14x14` -> `32x7x7`
3. **Flatten** : `32x7x7=1568`
4. **mu,logvar** : `1568 -> 10`,**10** 是可設定的 **Latent Size**,用一個 **Linear Layer** 分別產生 $\mu$ 和 $\log\sigma^2$
5. **Reparameterize** : $z=\mu + \sigma\epsilon$
**Decoder** :
1. **Linear** : `10 -> 1568`,恢復大小
2. **Reshape** : `1568 -> 32x7x7`
3. **ConvTranspose2d** : `32x7x7` -> `16x14x14`
4. **ConvTranspose2d** : `16x14x14` -> `1x28x28` 恢復原圖
## Reparameterize
**VAE** 的算法,就是要在 **Encoder** 的 **Output** 加入雜訊,雜訊生成的方式會讓 **Encoder** 自己決定,生成雜訊的參數都是 **Encoder** 自己產生,但是還要搭配 **Normal Distribution**, $z\sim\mathcal{N}(\mu,\sigma)$,但是為了讓訓練能正確傳遞 **Gradient** 而產生的,寫法會變成 $z=\mu + \sigma \epsilon$
那為什麼要對 **Code** 加入雜訊? **Encoder** 的輸出就像一個數字如 0~9,而 **Decoder** 就像查表一樣去恢復這個數字
## KL Divergence
**Neural Network** 為了讓訓練效果變好,在 **Training** 的過程中可能會希望雜訊越小越好,然後把 $\sigma$ 調整的非常低,但這樣就失去我們要的效果了,所以我們會在 **Loss Function** 動手腳,用來限制 $\sigma$ 不要太小,**VAE** 用的方法就是讓 **KL Divergence** 計算 **Distribution** $Q(z)$ 和 $\mathcal{N}(0,I)$ 之間的不相似度,目的就是讓 $Q(z)$ 可以越接近 $\mathcal{N}(0,I)$ 越好,因為 **KL Divergence** 用來當作 **Loss**,**Training** 過程會讓不相似度降低,所以 $\sigma$ 會接近 **1**
**KL Divergence** 定義 :
$$
KL(Q||P)=\int Q(z)\cdot\log(\frac{Q(z)}{P(z)})dz
$$
可以用來評估 **Distribution** $Q(z)$ 和 $P(z)$ 之間的不相似度,數值越大代表差距越大,實際計算方式
$$
\int \mathcal{N}(\mu,\sigma^2)\cdot(\frac{\mathcal{N}(\mu,\sigma^2)}{\mathcal{N}(0,I)})
$$

最右邊的顏色深度,代表左邊兩個 Distribution 的差異度,顏色越深代表差越多,所以可以發現重疊的部分深度是較低的,因為機率相似,但 KL Divergence 最後是只吐出一個數值,不會像圖片那樣
**Normal Distribution PDF** : $f(x) = \frac{1}{\sqrt{2\pi}\sigma} e^{-\frac{(x-\mu)^2}{2\sigma^2}}$
帶入並簡化後就變成 :
$$
-0.5\sum(1+\log(\sigma^2)-\mu^2-\sigma^2)
$$
簡化流程可參考 : https://tomohiroliu22.medium.com/%E6%B7%B1%E5%BA%A6%E5%AD%B8%E7%BF%92paper%E7%B3%BB%E5%88%97-04-variational-autoencoder-vae-a7fbc67f0a2
然後恢復圖片的 **Loss** 就直接用 **MSE(Mean Square Error)** 來算圖片間的差異就好,或是可以用 **BCE(Binary Crocc-Entropy)** 來算也沒問題
## Result
訓練完成後要生成圖片時,就直接給 **Latent Size** 的數字,然後直接 **Call Decoder**,不通過 **Encoder** 和 **Reparameterize**,等於直接用無雜訊的 $\mu$ 來生成

VAE 的問題就是生成圖片很模糊,但後來 **Stable-Diffusion** 其實還是有使用,但不會拿來做為生成圖片的 **Decoder**