您好,登錄后才能下訂單哦!
如何使用變分自編碼器VAE生成動漫人物形象,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
變分自編碼器(VAE)與生成對抗網絡(GAN)經常被相互比較,其中前者在圖像生成上的應用范圍遠窄于后者。VAE 是不是只能在 MNIST 數(shù)據集上生成有意義的輸出?在本文中,作者嘗試使用 VAE 自動生成動漫人物的頭像,并取得了不錯的結果。
以上是通過變分自編碼器生成的動畫圖片樣本。
在圖像生成領域,人們總是喜歡試著將變分自編碼器(VAE)和對抗生成網絡(GAN)相比較。人們的共識是,VAE 更容易被訓練,并且具有顯式的分布假設(高斯分布)用于顯式的表示和觀察,而 GAN 則能夠更好地捕獲觀測值的分布并且對觀測分布沒有任何的假設。結果就是,每個人都相信只有 GAN 能夠創(chuàng)造出清晰而生動的圖片。雖然可能確實是這樣,因為從理論上講,GAN 捕獲到了像素之間的相關性,但是沒有多少人試過用比 28*28 維的 MNIST 數(shù)據更大的圖片作為輸入訓練 VAE 來證明這一點。
在 MNIST 數(shù)據集上有太多變分自編碼器(VAE)的實現(xiàn),但是很少有人在其他的數(shù)據集上做些不一樣的事情。這是因為最原始的變分自編碼器的論文僅僅只用 MNIST 數(shù)據集作為了一個例子嗎?
流言終結者!
現(xiàn)在,讓我們做一個「流言終結者」的實踐來看看 VAE 圖像生成器的效果是多么不盡人意。例如,下面這些圖像。
模糊不清的 VAE 樣例。
我們從尋找一些 GAN 的對比組開始。我在 Google 上搜索了」GAN 應用程序」,并且發(fā)現(xiàn)了一個非常有趣的 Github 代碼倉庫,這個代碼倉庫總結了一些 GAN 應用程序:https://github.com/nashory/gans-awesome-applications
為什么「GAN 應用程序」就可以呢?好吧,很難找到不是圖像生成的 GAN 應用程序,不是嗎?為了讓這個實踐更加令人興奮,我們這次將嘗試用生成模型輸出一些動漫形象!
首先,讓我們看看一個 GAN 模型完成這個任務的效果有多好。下面的兩組圖片來自于兩個做動漫圖片生成的項目,它們被很多人選擇并且以此為基礎開展工作:
1)https://github.com/jayleicn/animeGAN
2)https://github.com/tdrussell/IllustrationGAN
還不錯哦,不是嗎?我喜歡它們的色彩,它們和真實的圖片十分相似。
盡管這些圖片里面有些重影,但它們看上去更好。我猜竅門是放大圖像,僅僅只看人臉。
結果表明,GAN 的優(yōu)異表現(xiàn)令人印象深刻。這讓我倍感壓力。
額... 我們還應該繼續(xù)嗎...
從哪里獲得數(shù)據?
很不幸,在網絡上沒有可以得到的標準動漫形象數(shù)據集。但是這不能阻止像我這樣的人去尋找它。在瀏覽了一些 GitHub 代碼倉庫之后,我得到了一些提示:
一個叫做「Getchu」的日本網站有大量的動漫圖片。
需要一些工具從網上下載圖片,但是你需要自己找到這種工具。(我在這里向你提供一個可能是不合法的)
有很多預訓練好的 U-net/ RCNN 動漫人臉檢測器,比如 lbpcascade_animeface,這樣一來你就可以將人臉提取成 64×64 的圖片。
變分自編碼器 VAE
本文假設你已經閱讀了大量關于變分自編碼器的帖子。如果你沒有的話,我想向你推薦下面這幾篇文章:
Intuitively Understanding Variational Autoencoders (https://towardsdatascience.com/intuitively-understanding-variational-autoencoders-1bfe67eb5daf)
Tutorial—What is a variational autoencoder? (https://jaan.io/what-is-variational-autoencoder-vae-tutorial/)
Introducing Variational Autoencoders (in Prose and Code) (http://blog.fastforwardlabs.com/2016/08/12/introducing-variational-autoencoders-in-prose-and.html)
在 TensorFlow 中對比兩大生成模型:VAE 與 GAN
所以,在你知道了什么是 VAE 以及如何實現(xiàn)它之后,現(xiàn)在的問題就是「如果知道目標函數(shù)和實現(xiàn)方法就足夠去訓練一個變分自編碼器了嗎?」我認為答案是肯定的,但是它并不想通常說的那么簡單。例如,這個目標函數(shù)來自哪里的問題,以及 KL 散度分量在這里有什么作用。在這篇帖子中,我會試著去解釋 VAE 背后隱藏的奧秘。
變分推斷是一種在概率圖模型(PGM)中推斷復雜分布的技術。直觀地說,如果你不能很容易地捕獲復雜分布的最優(yōu)點,你就可以用一些像高斯分布這樣的簡單分布來近似估計它的上界或者下界。例如,下圖展示了如何使用高斯分布近似估計局部最優(yōu)解。
圖片來自:https://people.duke.edu/~ccc14/sta-663/EMAlgorithm.html
請忽略標題中的 EM(最大期望算法)。這是一個在概率圖模型中經典的優(yōu)化方法,它能夠更新變分下界,但是現(xiàn)在你在深度學習中會使用隨機梯度下降算法(SGD)。
KL 散度是另外一個在概率圖模型中會用到的非常重要的技術。它用來測量兩個分布之間的差異。它不是一個距離度量,因為 KL[Q||P] 不等于 KL[P||Q]。下面的幻燈片展示了這種差異。
圖片來自:https://www.slideshare.net/Sabhaology/variational-inference
顯然,在 Q>0 時,KL[Q||P] 不允許 P=0. 換句話說,當最小化 KL[Q||P] 時,你想用 Q 分布來捕獲 P 分布的一些模式,但是你必然會忽略一些模式的風險。并且,在 P>0 時,KL[P||Q] 不允許 Q=0。換句話說,當最小化 KL[P||Q] 時,你想讓 Q 捕獲整個分布,而且如果有需要的話,完全忽略掉 P 的模式。
到目前為止,我們直觀地明白了兩個事實:
「變分」大致是對上界或者下界的近似。
「KL」衡量兩個分部之間的差異。
現(xiàn)在讓我們回過頭來看看 VAE 的目標函數(shù)是怎么得來的。
這是我對 VAE 的推導。盡管它似乎與你可能在論文中看到的看起來不同,但這是我認為最容易理解的推導。
給定一些圖像作為訓練數(shù)據,我們想要擬合一些能夠盡可能準確地表示訓練數(shù)據的參數(shù)(theta)。正式一點說,我們想要擬合用于最大化觀測值的聯(lián)合概率的模型。因此,你會得到左邊的表達式。
「z」從何而來?
z 是創(chuàng)造觀測值(圖像)的潛在表示。直觀地說,我們假設一些神秘的畫家在數(shù)據集中創(chuàng)作這些圖像(x),我們將它們稱作 Z。并且,我們發(fā)現(xiàn) Z 是不確定的,有時 1 號畫家創(chuàng)作了圖片,有時候是 2 號畫家創(chuàng)作了圖片。我們僅僅知道所有的藝術家都對他們所畫的圖片有著特別的偏愛。
大于等于號是怎么來的?
Jensen 不等式如下所示。注意: log 是凹函數(shù),所以在我們的例子中,不等式反過來的。
圖片來自 Youtube:https://www.youtube.com/watch?v=10xgmpG_uTs
為什么在最后一行取近似?
我們不能對無窮的可能的 z 做幾分,所以我們使用數(shù)值逼近,這意味著我們從分布中進行抽樣來對期望取近似。
什么是 P(x|z) 分布?
在變分自編碼器中,我們假設它是高斯函數(shù)。這就是為什么在優(yōu)化 VAE 時,要做均方誤差(MSE)。
f 函數(shù)是解碼器!哦!在范數(shù)之后應該有平方符號。
@staticmethod
def _gaussian_log_likelihood(targets, mean, std):
se = 0.5 * tf.reduce_sum(tf.square(targets - mean)) / (2*tf.square(std)) + tf.log(std)
return se
@staticmethod
def _bernoulli_log_likelihood(targets, outputs, eps=1e-8):
log_like = -tf.reduce_sum(targets * tf.log(outputs + eps)
+ (1. - targets) * tf.log((1. - outputs) + eps))
return log_like
P(x|z) 的假設: 高斯和伯努利分布。代碼顯示了負的對數(shù)似然,因為我們總是希望最小化錯誤,而不是在深度學習中顯式地最大化似然。
你在 Github 中看到如此多的 softmax 函數(shù)的原因是,對于像 MNIST 這樣的二進制圖像,我們假設分布是伯努利分布。
什么是 P(z|x) 分布?
這是高斯分布。這就是為什么你看到 KL 散度的實現(xiàn)是一個近似的解。不明白嗎?不要擔心,你可以看看這篇里:https://stats.stackexchange.com/questions/318184/kl-loss-with-a-unit-gaussian
@staticmethod
def _kl_diagnormal_stdnormal(mu, log_var):
var = tf.exp(log_var)
kl = 0.5 * tf.reduce_sum(tf.square(mu) + var - 1. - log_var)
return kl
Python 語言編寫的 KL 散度近似形式的表達式
這個等式怎么能成為一個自編碼器呢?
等式中有兩類參數(shù)。參數(shù) theta 是用來對分布 P(x|z) 建模的,它將 z 解碼為圖像 x。變體的 theta 是用來對分布 Q(z|x) 建模的,它將 x 編碼成潛在的表示 z。
自制的變分自編碼器的示意圖。綠色和藍色的部分是可微的,琥珀色的部分代表不可微的白噪聲。每個人都用著名的貓的圖片,所以這里我使用了狗。我不知道我從哪里得到的這張可愛的狗狗圖片。如果你知道,請告訴我,這樣我可以正確地引用原始網站。
相應的 TensorFlow 計算圖譜:
def _build_graph(self):
with tf.variable_scope('vae'):
self.x = tf.placeholder(tf.float32, shape=[None, self._observation_dim])
with tf.variable_scope('encoder'):
encoded = self._encode(self.x, self._latent_dim)
with tf.variable_scope('latent'):
self.mean = encoded[:, :self._latent_dim]
logvar = encoded[:, self._latent_dim:]
stddev = tf.sqrt(tf.exp(logvar))
epsilon = tf.random_normal([self._batch_size, self._latent_dim])
# Reparameterization Trick
self.z = self.mean + stddev * epsilon
with tf.variable_scope('decoder'):
decoded = self._decode(self.z, self._observation_dim)
self.obs_mean = decoded
if self._observation_distribution == 'Gaussian':
obs_epsilon = tf.random_normal([self._batch_size,
self._observation_dim])
self.sample = self.obs_mean + self._observation_std * obs_epsilon
else:
self.sample = Bernoulli(probs=self.obs_mean).sample()
VAE 目標函數(shù)的兩個組成部分的意義
最小化 KL 項:將 P(z|x) 看作 N(0,1)(標準正態(tài)分布)。我們希望通過從標準正態(tài)分布中抽樣來生成圖像。因此,我們最好讓潛在的分布盡可能地接近標準正態(tài)分布。
最大限度地減小重構損失:創(chuàng)造盡可能生動/真實的圖像。最小化真實的圖像和生成的圖像之間的誤差。
很容易看到,為了使 VAE 很好的工作,平衡這兩個部分是十分關鍵的。
如果我們完全忽略 KL 項,變分自編碼器將收斂到標準的自編碼器,它將刪除目標函數(shù)中的隨機部分。因此,VAE 不能生成新的圖像,只能記住并且展示訓練數(shù)據(或者創(chuàng)造純粹的噪聲,因為在那個潛在的位置沒有編碼的圖像?。┤绻阕銐蛐疫\的話,理想的結果是實現(xiàn)了核主成分分析!
如果我們完全忽略了重構項,那么潛在的分布會退化成標準的正態(tài)分布。所以無論輸入是什么,你總是得到類似的輸出。
一個 GAN 退化的案例。VAE 的情況也相同。圖片來自:http://yusuke-ujitoko.hatenablog.com/entry/2017/05/30/011900
現(xiàn)在我們明白了:
我們希望 VAE 生成合理的圖像,但是我們不想讓它顯示訓練數(shù)據。
我們想從標準正態(tài)分布中取樣,但是我們不想一次又一次地看到同樣的圖像。我們希望模型能產生差別非常大的圖像。
那么,我們如何平衡它們呢?我們將觀測值的標準差設置成一個超參數(shù)。
with tf.variable_scope('loss'):
with tf.variable_scope('kl-divergence'):
kl = self._kl_diagnormal_stdnormal(self.mean, logvar)
if self._observation_distribution == 'Gaussian':
with tf.variable_scope('gaussian'):
# self._observation_std is hyper parameter
reconst = self._gaussian_log_likelihood(self.x,
self.obs_mean,
self._observation_std)
else:
with tf.variable_scope('bernoulli'):
reconst = self._bernoulli_log_likelihood(self.x, self.obs_mean)
self._loss = (kl + reconst) / self._batch_size
我看到人們經常將 KL 項設定為一個像 0.001×KL + Reconstruction_Loss 這樣的值,這是不正確的!順便問一下,這就是很多人只在 MNIST 數(shù)據集上做 VAE 的原因嗎?
還有什么值得注意的呢?模型的復雜程度是支撐損失函數(shù)的關鍵因素。如果解碼器太復雜,那么即使是較弱的損失也不能阻止它過擬合。結果是,潛在的分布被忽略了。如果解碼器太簡單了,模型就不能合理地解碼潛在的表示,最終只能捕獲一些粗略的輪廓,就像我們之前所展示的圖像那樣。
最后,如果我們上面做的事情都是正確的,是時候看看 VAE 的力量了。
成功了!
好吧,我承認,小圖片是沒有說服力的。
稍微放大一點...
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業(yè)資訊頻道,感謝您對億速云的支持。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。