花式解釋AutoEncoder與VAE

什么是自動編碼器

自動編碼器(AutoEncoder)最開始作為一種數(shù)據(jù)的壓縮方法,其特點有:

1)跟數(shù)據(jù)相關(guān)程度很高灾锯,這意味著自動編碼器只能壓縮與訓練數(shù)據(jù)相似的數(shù)據(jù),這個其實比較顯然坪圾,因為使用神經(jīng)網(wǎng)絡提取的特征一般是高度相關(guān)于原始的訓練集穴墅,使用人臉訓練出來的自動編碼器在壓縮自然界動物的圖片是表現(xiàn)就會比較差,因為它只學習到了人臉的特征斋枢,而沒有能夠?qū)W習到自然界圖片的特征;

2)壓縮后數(shù)據(jù)是有損的知给,這是因為在降維的過程中不可避免的要丟失掉信息瓤帚;

到了2012年描姚,人們發(fā)現(xiàn)在卷積網(wǎng)絡中使用自動編碼器做逐層預訓練可以訓練更加深層的網(wǎng)絡,但是很快人們發(fā)現(xiàn)良好的初始化策略要比費勁的逐層預訓練有效地多戈次,2014年出現(xiàn)的Batch Normalization技術(shù)也是的更深的網(wǎng)絡能夠被被有效訓練轩勘,到了15年底,通過殘差(ResNet)我們基本可以訓練任意深度的神經(jīng)網(wǎng)絡怯邪。

所以現(xiàn)在自動編碼器主要應用有兩個方面绊寻,第一是數(shù)據(jù)去噪,第二是進行可視化降維悬秉。然而自動編碼器還有著一個功能就是生成數(shù)據(jù)澄步。

我們之前講過GAN,它與GAN相比有著一些好處和泌,同時也有著一些缺點村缸。我們先來講講其跟GAN相比有著哪些優(yōu)點。

第一點武氓,我們使用GAN來生成圖片有個很不好的缺點就是我們生成圖片使用的隨機高斯噪聲梯皿,這意味著我們并不能生成任意我們指定類型的圖片,也就是說我們沒辦法決定使用哪種隨機噪聲能夠產(chǎn)生我們想要的圖片县恕,除非我們能夠把初始分布全部試一遍东羹。但是使用自動編碼器我們就能夠通過輸出圖片的編碼過程得到這種類型圖片的編碼之后的分布,相當于我們是知道每種圖片對應的噪聲分布忠烛,我們就能夠通過選擇特定的噪聲來生成我們想要生成的圖片属提。

第二點,這既是生成網(wǎng)絡的優(yōu)點同時又有著一定的局限性况木,這就是生成網(wǎng)絡通過對抗過程來區(qū)分“真”的圖片和“假”的圖片垒拢,然而這樣得到的圖片只是盡可能像真的,但是這并不能保證圖片的內(nèi)容是我們想要的火惊,換句話說求类,有可能生成網(wǎng)絡盡可能的去生成一些背景圖案使得其盡可能真,但是里面沒有實際的物體屹耐。

自動編碼器的結(jié)構(gòu)

首先我們給出自動編碼器的一般結(jié)構(gòu)

autoencoder.png

從上面的圖中尸疆,我們能夠看到兩個部分,第一個部分是編碼器(Encoder)惶岭,第二個部分是解碼器(Decoder)寿弱,編碼器和解碼器都可以是任意的模型,通常我們使用神經(jīng)網(wǎng)絡模型作為編碼器和解碼器按灶。輸入的數(shù)據(jù)經(jīng)過神經(jīng)網(wǎng)絡降維到一個編碼(code)症革,接著又通過另外一個神經(jīng)網(wǎng)絡去解碼得到一個與輸入原數(shù)據(jù)一模一樣的生成數(shù)據(jù),然后通過去比較這兩個數(shù)據(jù)鸯旁,最小化他們之間的差異來訓練這個網(wǎng)絡中編碼器和解碼器的參數(shù)噪矛。當這個過程訓練完之后量蕊,我們可以拿出這個解碼器,隨機傳入一個編碼(code)艇挨,希望通過解碼器能夠生成一個和原數(shù)據(jù)差不多的數(shù)據(jù)残炮,上面這種圖這個例子就是希望能夠生成一張差不多的圖片。

decoder.png

這件事情能不能實現(xiàn)呢缩滨?其實是可以的势就,下面我們會用PyTorch來簡單的實現(xiàn)一個自動編碼器。

首先我們構(gòu)建一個簡單的多層感知器來實現(xiàn)一下脉漏。

class autoencoder(nn.Module):
    def __init__(self):
        super(autoencoder, self).__init__()
        self.encoder = nn.Sequential(
            nn.Linear(28*28, 128),
            nn.ReLU(True),
            nn.Linear(128, 64),
            nn.ReLU(True),
            nn.Linear(64, 12),
            nn.ReLU(True),
            nn.Linear(12, 3)
        )
        self.decoder = nn.Sequential(
            nn.Linear(3, 12),
            nn.ReLU(True),
            nn.Linear(12, 64),
            nn.ReLU(True),
            nn.Linear(64, 128),
            nn.ReLU(True),
            nn.Linear(128, 28*28),
            nn.Tanh()
        )

    def forward(self, x):
        x = self.encoder(x)
        x = self.decoder(x)
        return x

這里我們定義了一個簡單的4層網(wǎng)絡作為編碼器苞冯,中間使用ReLU激活函數(shù),最后輸出的維度是3維的鸠删,定義的解碼器抱完,輸入三維的編碼,輸出一個 $28\times 28$ 的圖像數(shù)據(jù)刃泡,特別要注意最后使用的激活函數(shù)是Tanh巧娱,這個激活函數(shù)能夠?qū)⒆詈蟮妮敵鲛D(zhuǎn)換到-1 $\thicksim$ 1之間,這是因為我們輸入的圖片已經(jīng)變換到了-1 $\thicksim$ 1之間了烘贴,這里的輸出必須和其對應禁添。

訓練過程也比較簡單,我們使用最小均方誤差來作為損失函數(shù)桨踪,比較生成的圖片與原始圖片的每個像素點的差異老翘。

同時我們也可以將多層感知器換成卷積神經(jīng)網(wǎng)絡,這樣對圖片的特征提取有著更好的效果锻离。

class autoencoder(nn.Module):
    def __init__(self):
        super(autoencoder, self).__init__()
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 16, 3, stride=3, padding=1),  # b, 16, 10, 10
            nn.ReLU(True),
            nn.MaxPool2d(2, stride=2),  # b, 16, 5, 5
            nn.Conv2d(16, 8, 3, stride=2, padding=1),  # b, 8, 3, 3
            nn.ReLU(True),
            nn.MaxPool2d(2, stride=1)  # b, 8, 2, 2
        )
        self.decoder = nn.Sequential(
            nn.ConvTranspose2d(8, 16, 3, stride=2),  # b, 16, 5, 5
            nn.ReLU(True),
            nn.ConvTranspose2d(16, 8, 5, stride=3, padding=1),  # b, 8, 15, 15
            nn.ReLU(True),
            nn.ConvTranspose2d(8, 1, 2, stride=2, padding=1),  # b, 1, 28, 28
            nn.Tanh()
        )

    def forward(self, x):
        x = self.encoder(x)
        x = self.decoder(x)
        return x

這里使用了nn.ConvTranspose2d()铺峭,這可以看作是卷積的反操作,可以在某種意義上看作是反卷積汽纠。

我們使用卷積網(wǎng)絡得到的最后生成的圖片效果會更好卫键,具體的圖片效果我就不再這里放了,可以在我們的github上看到圖片的展示虱朵。

變分自動編碼器(Variational Autoencoder)

變分編碼器是自動編碼器的升級版本莉炉,其結(jié)構(gòu)跟自動編碼器是類似的,也由編碼器和解碼器構(gòu)成碴犬。

回憶一下我們在自動編碼器中所做的事絮宁,我們需要輸入一張圖片,然后將一張圖片編碼之后得到一個隱含向量服协,這比我們隨機取一個隨機噪聲更好绍昂,因為這包含著原圖片的信息,然后我們隱含向量解碼得到與原圖片對應的照片。

但是這樣我們其實并不能任意生成圖片窘游,因為我們沒有辦法自己去構(gòu)造隱藏向量卖陵,我們需要通過一張圖片輸入編碼我們才知道得到的隱含向量是什么,這時我們就可以通過變分自動編碼器來解決這個問題张峰。

其實原理特別簡單,只需要在編碼過程給它增加一些限制棒旗,迫使其生成的隱含向量能夠粗略的遵循一個標準正態(tài)分布喘批,這就是其與一般的自動編碼器最大的不同。

這樣我們生成一張新圖片就很簡單了铣揉,我們只需要給它一個標準正態(tài)分布的隨機隱含向量饶深,這樣通過解碼器就能夠生成我們想要的圖片,而不需要給它一張原始圖片先編碼逛拱。

在實際情況中敌厘,我們需要在模型的準確率上與隱含向量服從標準正態(tài)分布之間做一個權(quán)衡,所謂模型的準確率就是指解碼器生成的圖片與原圖片的相似程度朽合。我們可以讓網(wǎng)絡自己來做這個決定俱两,非常簡單,我們只需要將這兩者都做一個loss曹步,然后在將他們求和作為總的loss宪彩,這樣網(wǎng)絡就能夠自己選擇如何才能夠使得這個總的loss下降。另外我們要衡量兩種分布的相似程度讲婚,如何看過之前一片GAN的數(shù)學推導尿孔,你就知道會有一個東西叫KL divergence來衡量兩種分布的相似程度,這里我們就是用KL divergence來表示隱含向量與標準正態(tài)分布之間差異的loss筹麸,另外一個loss仍然使用生成圖片與原圖片的均方誤差來表示活合。

我們可以給出KL divergence 的公式

$$
D_{KL} (P || Q) = \int_{-\infty}^{\infty} p(x) \log \frac{p(x)}{q(x)} dx
$$

這里變分編碼器使用了一個技巧“重新參數(shù)化”來解決KL divergence的計算問題。

vae.png

這時不再是每次產(chǎn)生一個隱含向量物赶,而是生成兩個向量白指,一個表示均值,一個表示標準差块差,然后通過這兩個統(tǒng)計量來合成隱含向量侵续,這也非常簡單,用一個標準正態(tài)分布先乘上標準差再加上均值就行了憨闰,這里我們默認編碼之后的隱含向量是服從一個正態(tài)分布的状蜗。這個時候我們是想讓均值盡可能接近0,標準差盡可能接近1鹉动。而論文里面有詳細的推導如何得到這個loss的計算公式轧坎,有興趣的同學可以去看看推導

下面是PyTorch的實現(xiàn)

reconstruction_function = nn.BCELoss(size_average=False)  # mse loss

def loss_function(recon_x, x, mu, logvar):
    """
    recon_x: generating images
    x: origin images
    mu: latent mean
    logvar: latent log variance
    """
    BCE = reconstruction_function(recon_x, x)
    # loss = 0.5 * sum(1 + log(sigma^2) - mu^2 - sigma^2)
    KLD_element = mu.pow(2).add_(logvar.exp()).mul_(-1).add_(1).add_(logvar)
    KLD = torch.sum(KLD_element).mul_(-0.5)
    # KL divergence
    return BCE + KLD

另外變分編碼器除了可以讓我們隨機生成隱含變量,還能夠提高網(wǎng)絡的泛化能力泽示。

最后是VAE的代碼實現(xiàn)

class VAE(nn.Module):
    def __init__(self):
        super(VAE, self).__init__()

        self.fc1 = nn.Linear(784, 400)
        self.fc21 = nn.Linear(400, 20)
        self.fc22 = nn.Linear(400, 20)
        self.fc3 = nn.Linear(20, 400)
        self.fc4 = nn.Linear(400, 784)

    def encode(self, x):
        h1 = F.relu(self.fc1(x))
        return self.fc21(h1), self.fc22(h1)

    def reparametrize(self, mu, logvar):
        std = logvar.mul(0.5).exp_()
        if torch.cuda.is_available():
            eps = torch.cuda.FloatTensor(std.size()).normal_()
        else:
            eps = torch.FloatTensor(std.size()).normal_()
        eps = Variable(eps)
        return eps.mul(std).add_(mu)

    def decode(self, z):
        h3 = F.relu(self.fc3(z))
        return F.sigmoid(self.fc4(h3))

    def forward(self, x):
        mu, logvar = self.encode(x)
        z = self.reparametrize(mu, logvar)
        return self.decode(z), mu, logvar

VAE的結(jié)果比普通的自動編碼器要好很多缸血,下面是結(jié)果

image_0.png

image_80.png

VAE的缺點也很明顯蜜氨,他是直接計算生成圖片和原始圖片的均方誤差而不是像GAN那樣去對抗來學習,這就使得生成的圖片會有點模糊∩有海現(xiàn)在已經(jīng)有一些工作是將VAE和GAN結(jié)合起來飒炎,使用VAE的結(jié)構(gòu),但是使用對抗網(wǎng)絡來進行訓練笆豁,具體可以參考一下這篇論文郎汪。

參考內(nèi)容: kvfrans blog


本文代碼已經(jīng)上傳到了github

歡迎查看我的知乎專欄,深度煉丹

歡迎訪問我的博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闯狱,一起剝皮案震驚了整個濱河市煞赢,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哄孤,老刑警劉巖照筑,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瘦陈,居然都是意外死亡凝危,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門双饥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來媒抠,“玉大人,你說我怎么就攤上這事咏花∨可” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵昏翰,是天一觀的道長苍匆。 經(jīng)常有香客問我,道長棚菊,這世上最難降的妖魔是什么浸踩? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮统求,結(jié)果婚禮上检碗,老公的妹妹穿的比我還像新娘。我一直安慰自己码邻,他們只是感情好折剃,可當我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著像屋,像睡著了一般怕犁。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天奏甫,我揣著相機與錄音戈轿,去河邊找鬼。 笑死阵子,一個胖子當著我的面吹牛思杯,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播挠进,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼智蝠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奈梳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤解虱,失蹤者是張志新(化名)和其女友劉穎攘须,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體殴泰,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡于宙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了悍汛。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捞魁。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖离咐,靈堂內(nèi)的尸體忽然破棺而出谱俭,到底是詐尸還是另有隱情,我是刑警寧澤宵蛀,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布昆著,位于F島的核電站,受9級特大地震影響术陶,放射性物質(zhì)發(fā)生泄漏凑懂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一梧宫、第九天 我趴在偏房一處隱蔽的房頂上張望接谨。 院中可真熱鬧,春花似錦塘匣、人聲如沸脓豪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽跑揉。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間历谍,已是汗流浹背现拒。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留望侈,地道東北人印蔬。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像脱衙,于是被迫代替她去往敵國和親侥猬。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容