理解深度學習:神經網絡的雙胞胎兄弟-自編碼器(上)


前言

本篇文章可作為<利用變分自編碼器實現深度換臉(DeepFake)>的知識鋪墊。

自編碼器是什么,自編碼器是個神奇的東西,可以提取數據中的深層次的特征。

例如我們輸入圖像,自編碼器可以將這個圖像上“人臉”的特征進行提取(編碼過程),這個特征就保存為自編碼器的潛變量,例如這張人臉的膚色以及頭發顏色,自編碼器在提取出這些特征之后還可以通過這些特征還原我們的原始數據。這個過程稱作“解碼”。

Screen-Shot-2018-03-16-at-10.24.11-PM
(以下相關圖來自於jeremy)

自編碼器其實也是神經網絡的一種,神經網絡我們都知道,我們設計好網絡層,輸入我們的數據,通過訓練提供的數據進行前向操作提取特征,然后與准備好的標記進行比較,通過特定的損失函數來得到損失,然后進行反向操作實現對目標的分類和標定。

那么自編碼器為什么說和神經網絡很像呢?

什么是自編碼器

自編碼器也有自己的網絡層,在自編碼器中通常稱之為隱藏層$h$,一個自編碼器通常包含兩個部分,一個為$h=f(x)$表示的編碼器,另一個是$s=g(h)$表示的解碼器。也就是$s=g(f(x))$。

當然編碼器不可能輸入$x$輸出$x$,那樣就太沒意思了,編碼器的主要特點是吸收輸入數據的“特點”,然后再釋放出來,可以理解為隨機映射$p_{encoder}(h|x)$和$p_{decoder}(x|h)$。

Screen-Shot-2018-03-06-at-3.17.13-PM

如上圖(來自Jeremyjordan),我們可以看到輸入input layeroutput layer的維數是一樣的,而隱含層的維數較小,所以我們也將自編碼器理解為降維的過程。

但是要注意,自編碼器屬於無監督學習,也就是說,不同於神經網絡,自編碼器不需要任何其他的數據,只需要對輸入的特征進行提取即可,當然我們要添加一些額外的限制條件來“強迫”自編碼器提取我們想要的東西。

Screen-Shot-2018-03-06-at-6.09.05-PM

如果是上面這種網絡的話,全是線性激活層,激活層可以記憶我們的輸入,但也僅僅是記住了而已。在實際應用中,我們需要的隱含層應該可以很好地構建我們輸入數據的信息,學習到我們的輸入數據的一些分布和特點。

欠完備自編碼器

自編碼器我們想要它能夠對輸入的數據進行分析獲取一些特性,而不是簡單的輸入輸出,所以我們通過限制h的維度來實現,強迫自編碼器去尋找訓練數據中最顯著的特征。

為什么叫欠完備,那是因為$h$的維度比輸入$x$小。

Screen-Shot-2018-03-07-at-8.24.37-AM

我們通過對輸出設置一個損失函數(和神經網絡中的損失函數相似):

$$L(x,g(f(x)))$$

然后通過減少損失(這個損失可以是均方誤差等)來使$h$隱含層學習數據中最重要的信息。也就是學習數據中的潛在特性(Latent attributes)。

說白了這個自編碼器和PCA(PCA是一個簡單的機器學習算法,通過尋找矩陣中最大的特征向量來尋找輸入數據的“特點”)很像。都是通過編碼和解碼來得到訓練數據的隱含信息,但是如果這個隱含層是線性層(上圖提到的)或者隱含層的容量很大,那么自編碼器就無法學習到我們輸入數據的有用信息。僅僅起一個復制的作用。

自編碼器和PCA的不同在於自編碼器可以學習到非線性特征,也就是通過非線性編碼和非線性解碼自編碼器可以實現對數據的理解還原操作。對於高維的數據,自編碼器就可以學習到一個復雜的數據表示(流形,manifold)。這些數據表示可以是二維的或者三維的形態(取決於我們自己的設置)。

LinearNonLinear
Image credit

為什么非線性的映射能夠學到更多的東西,當然是因為非線性的表示能力更強,學習到的東西越多,從下圖可以看出,對於二維空間中的一系列點,直線擬合(PCA)遠遠不如曲線擬合(自編碼器)。

Screen-Shot-2018-03-07-at-8.52.21-AM

我們利用Pytorch(v0.4.0)簡單編寫一個程序來觀察一下欠完備自編碼器的效果:

首先我們設計一個基本的網絡層(自編碼器層),這里我們輸入的數據是MNIST手寫數據集,數據集的圖像大小是28*28=784,所以我們設計的欠完備自編碼器將我們的輸入特征進行降維操作(我們輸入的是$28 *28=784$維的數據),然后我們進行三次降維,將784維降到3維這樣我們就可以將其進行可視化了。

下面自編碼器層的設計:

class AutoEncoder(nn.Module):
    def __init__(self):
        super(AutoEncoder, self).__init__()
        # 自編碼器的編碼器構造
        self.encoder = nn.Sequential(
            nn.Linear(28*28, 128),              # 784 => 128
            nn.LeakyReLU(0.1, inplace=True),    # 激活層
            nn.Linear(128, 64),                 # 128 => 64
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(64, 12),                  # 64 => 12
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(12, 3),                   # 最后我們得到3維的特征,之后我們在3維坐標中進行展示
        )
        # 自編碼器的解碼器構造
        self.decoder = nn.Sequential(
            nn.Linear(3, 12),                   # 3 => 12
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(12, 64),                  # 12 => 64
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(64, 128),                 # 64 -> 128
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(128, 28*28),              # 128 => 784
            nn.Sigmoid(),                       # 壓縮值的范圍到0-1便於顯示
        )

    def forward(self, x):
        hidden = self.encoder(x)                # 編碼操作,得到hidden隱含特征
        output = self.decoder(hidden)           # 解碼操作,通過隱含特征還原我們的原始圖
        return hidden, output

我們的優化器為:

optimizer = torch.optim.Adam(autoencoder.parameters(), lr=configure['lr'])

然后建立損失函數就可以進行訓練了。

我們首先讀取我們需要的數字圖像集MNIST,然后將其投入我們設計的自編碼器中進行訓練,我們分別用原始輸入圖像和重構后的圖像進行損失訓練,通過降低損失我們就可以提取到數字數據集中的特征。最后,我們將我們提取的三維特征用三維坐標表示出來:

myplot

顯然可以看到,每種數字(0-9)都有各自的特征簇,它們通常都聚在一起,換個角度再看一下:

myplot1

這樣我們就把一些數字圖從784維降到3維,將其提取到的特征通過三維坐標展示出來了。

我們也可以還原我們的輸入數據(上一行是輸入的圖像,下一行是通過3維特征還原出來的),雖然還原的比較模糊,但是也可以看出我們正確提取了這些數字的特征。

5446099-831328ed34cac900

稀疏自編碼器

稀疏自編碼器是正則編碼器的一種,正則即代表正則化,在之前的自編碼器中我們的隱藏層的維數是小於輸入層的,因此我們可以強迫自編碼器學習數據的“特征”。但是,如果隱含層的維數和輸入層的維數相同或者大於輸入層的維數(也可以說是容量)。那么我們的自編碼器可能就學不到什么有用的東西了。

這個時候應該怎么辦,我們可以學習神經網絡中經常用到的東西,那就是正則化

稀疏編碼器可以簡單表示為:
$$L(x,g(f(x))) + \Omega(h)$$
其中$g(h)$表示解碼器的輸出,$h$是編碼器的輸出$h=f(x)$。

很簡單,我們在損失函數的后面加了一項正則項,也可以稱作懲罰項。結合一些神經網絡的概念,我們可以將其理解為自編碼器前饋網絡中的正則項。

我們一般使用的正則項是L1正則損失,懲罰權重的絕對值,然后使用正則$\lambda$系數來調整。

$$ L(x,\hat x)+ \lambda\sum\limits_i|a_i^{(h)}| $$

另外一個常用的是$KL$散度,KL散度通常用來評價兩個分布的關系,選擇一個分布(通常是Bernoulli分布)然后將我們權重系數的分布與之進行比較,將其作為損失函數的一部分:

$$L(x,\hat x)+\sum\limits_{j}KL(\rho||\hat \rho_j)$$

其中$\rho$是我們選擇要比較的分布,而$\hat \rho$表示我們權重系數的分布。

另外在《深度學習》中,有對稀疏編碼器通過最大似然進行解釋:

TIM截圖20180713200024

TIM截圖20180713200032

通過解釋我們可以知道為什么通過加入正則項可以使隱含層學習到我們想要讓學習到的東西。

還有個小問題,為什么叫稀疏自編碼器,看下面的圖就可以知道,隱含層中有些結點消失了,自然就變稀疏了。稀疏的隱含層與我們之前介紹的欠完備隱含層的區別是:稀疏自編碼器可以使我們的隱含層中相對獨立的結點對特定的輸入特征或者屬性變得更加敏感。 也就是說欠完備的自編碼器的隱含層對輸入數據所有的信息進行觀測利用,而稀疏則不同,它是有選擇性的,只對感興趣的區域進行探測。

Screen-Shot-2018-03-07-at-1.50.55-PM

通過上面的稀疏策略我們可以保證在不犧牲網絡對特征提取能力的同時,減少網絡的容量,這樣我們就可以挑輸入信息中的特定信息去提取。

我們再通過Pytorch編程序來演示一下,我們修改自編碼器的隱含層,將隱含層改成為與輸入數據相同的維數(784),這樣如果進行訓練的話...損失函數的損失值是不會下降的,我們根據稀疏自編碼器的特點加上一個正則項(懲罰項),怎么加呢,其實對權重進行懲罰那就是權重衰減,我們在設計優化器的時候加上一句話就可以了:


# weight_decay為權重衰減系數,我們這里設置為1e-4
# 但這里的懲罰函數為L2
optimizer = torch.optim.Adam(autoencoder.parameters(), lr=configure['lr'], weight_decay=1e-4)

這樣就相當於算上了對權重的正則項,然后是我們的自編碼層,可以看到所有維數都是一樣的。

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

        self.encoder = nn.Sequential(
            nn.Linear(28*28, 28*28),        # 現在所有隱含層的維數是一樣的,沒有縮小也沒有放大
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(28*28, 28*28),
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(28*28, 28*28),
        )
        self.decoder = nn.Sequential(
            nn.Linear(28*28, 28*28),
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(28*28, 28*28),
            nn.LeakyReLU(0.1, inplace=True),
            nn.Linear(28*28, 28*28),
            nn.Sigmoid(),
        )

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

當然我們也可以通過dropout來實現對權重的懲罰,這里就不演示了,我們對上面的自編碼器進行訓練得到的結果,然后對數字5的圖像進行重構:
myplot12

好吧,效果沒有之前那個好,可能是正則化系數(權重衰減系數)需要調一下,但這個簡單的例子也足以說明我們可以通過正則化來使隱含層學習我們想要學習的東西。

未完待續。

參考資料:

撩我吧

  • 如果你與我志同道合於此,老潘很願意與你交流;
  • 如果你喜歡老潘的內容,歡迎關注和支持。
  • 如果你喜歡我的文章,希望點贊👍 收藏 📁 評論 💬 三連一下~

想知道老潘是如何學習踩坑的,想與我交流問題~請關注公眾號「oldpan博客」。
老潘也會整理一些自己的私藏,希望能幫助到大家,點擊神秘傳送門獲取。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM