前言
這周工作太忙,本來想更把 attention tranlsation 寫出來,但一直抽不出時間,等後面有時間再來寫。我們這周來看一個簡單的自編碼器實戰代碼,關于自編碼器的理論介紹我就不詳細介紹了,網上一搜一大把。最簡單的自編碼器就是通過一個 encoder 和 decoder 來對輸入進行複現,例如我們将一個圖檔輸入到一個網絡中,自編碼器的 encoder 對圖檔進行壓縮,得到壓縮後的資訊,進而 decoder 再将這個資訊進行解碼進而複現原圖。
作圖工具:omnigraffle
自編碼器實際上是通過去最小化 target 和 input 的差别來進行優化,即讓輸出層盡可能地去複現原來的資訊。由于自編碼器的基礎形式比較簡單,對于它的一些變體也非常之多,包括 dae,sdae,vae 等等,如果感興趣的小夥伴可以去網上搜一下其他相關資訊。
本篇文章将實作兩個 demo,第一部分即實作一個簡單的 input-hidden-output 結的自編碼器,第二部分将在第一部分的基礎上實作卷積自編碼器來對圖檔進行降噪。
工具說明
tensorflow1.0
jupyter notebook
資料:mnist 手寫資料集
第一部分
首先我們将實作一個如上圖結構的最簡單的 autoencoder。
加載資料
在這裡,我們使用 mnist 手寫資料集來進行實驗。首先我們需要導入資料,tensorflow 已經封裝了這個實驗資料集,是以我們使用起來也非常簡單。
如果想讓資料顯示灰階圖像,使用代碼 plt.imshow(img.reshape((28,28)), cmap='greys_r') 即可。
通過 input_data 就可以加載我們的資料集。如果小夥伴本地已經有了 mnist 資料集(四個壓縮包),可以把這四個壓縮包放在目錄 mnist_data 下,這樣 tensorflow 就會直接 extract 資料,而不用再重新下載下傳。我們可以通過 imshow 來随便檢視一個圖像。由于我們加載進來的資料已經被處理成一個 784 次元的向量,是以重新顯示的時候需要 reshape 一下。
構模組化型
我們把資料加載進來以後就可以進行最簡單的模組化。在這之前,我們首先來擷取一下 input 資料的大小,我們加載進來的圖檔是 28x28 的像素塊,tensorflow 已經幫我們處理成了 784 次元的向量。同時我們還需要指定一下 hidden layer 的大小。
在這裡我指定了 64,hidden_units 越小,意味着資訊損失的越多,小夥伴們也可以嘗試一下其他的大小來看看結果。
autoencoder 中包含了 input,hidden 和 output 三層。
在隐層,我們采用了 relu 作為激活函數。
至此,一個簡單的 autoencoder 就構造完成,接下來我們可以啟動 tensorflow 的 graph 來進行訓練。
訓練結果可視化
經過上面的步驟,我們構造了一個簡單的 autoencoder,下面我們将對結果進行可視化看一下它的表現。
這裡,我挑選了測試資料集中的 5 個樣本來進行可視化,同樣的,如果想觀察灰階圖像,指定 cmap 參數為'greys_r'即可。上面一行為 test 資料集中原始圖檔,第二行是經過 autoencoder 複現以後的圖檔,可以很明顯的看到像素資訊的損失。
同樣,我們也可以把隐層壓縮的資料拿出來可視化,結果如下:
這五張圖分别對應了 test 中五張圖檔的隐層壓縮後的圖像。
通過上面一個簡單的例子,我們了解了 autoencoder 的基本工作原理,下面我們将更進一步改進我們的模型,将隐層轉換為卷積層來進行圖像降噪。
上面過程中省略了一部分代碼,完整代碼請去我的 github 上檢視。
第二部分
在了解了上面 autoencoder 工作原理的基礎上,我們在這一部分将對 autoencoder 加入多個卷積層來進行圖檔的降噪處理。
同樣的我們還是使用 mnist 資料集來進行實驗,關于資料導入的步驟不再贅述,請下載下傳代碼檢視。在開始之前,我們先通過一張圖檔來看一下我們的整個模型結構:
我們通過向模型輸入一個帶有噪聲的圖檔,在輸出端給模型沒有噪聲的圖檔,讓模型通過卷積自編碼器去學習降噪的過程。
輸入層
這裡的輸入層和我們上一部分的輸入層已經不同,因為這裡我們要使用卷積操作,是以,輸入層應該是一個 height x width x depth 的一個圖像,一般的圖像 depth 是 rgb 格式三層,這裡我們的 mnist 資料集的 depth 隻有 1。
encoder 卷積層
encoder 卷積層設定了三層卷積加池化層,對圖像進行處理。
第一層卷積中,我們使用了 64 個大小為 3 x 3 的濾波器(filter),strides 預設為 1,padding 設定為 same 後我們的 height 和 width 不會被改變,是以經過第一層卷積以後,我們得到的資料從最初的 28 x 28 x 1 變為 28 x 28 x 64。
緊接着對卷積結果進行最大池化操作(max pooling),這裡我設定了 size 和 stride 都是 2 x 2,池化操作不改變卷積結果的深度,是以池化以後的大小為 14 x 14 x 64。
對于其他卷積層不再贅述。所有卷積層的激活函數都是用了 relu。
經過三層的卷積和池化操作以後,我們得到的 conv3 實際上就相當于上一部分中 autoencoder 的隐層,這一層的資料已經被壓縮為 4 x 4 x 32 的大小。
至此,我們就完成了 encoder 端的卷積操作,資料次元從開始的 28 x 28 x 1 變成了 4 x 4 x 32。
decoder 卷積層
接下來我們就要開始進行 decoder 端的卷積。在這之前,可能有小夥伴要問了,既然 encoder 中都已經把圖檔卷成了 4 x 4 x 32,我們如果繼續在 decoder 進行卷積的話,那豈不是得到的資料 size 越來越小?是以,在 decoder 端,我們并不是單純進行卷積操作,而是使用了 upsample(中文翻譯可以為上采樣)+ 卷積的組合。
我們知道卷積操作是通過一個濾波器對圖檔中的每個 patch 進行掃描,進而對 patch 中的像素塊權重求和後再進行非線性處理。舉個例子,原圖中我們的 patch 的大小假如是 3 x 3(說的通俗點就是一張圖檔中我們取其中一個 3 x 3 大小的像素塊出來),接着我們使用 3 x 3 的濾波器對這個 patch 進行處理,那麼這個 patch 經過卷積以後就變成了 1 個像素塊。在 deconvolution 中(或者叫 transposed convolution)這一過程是反過來的,1 個像素塊會被擴充成 3 x 3 的像素塊。
但是 deconvolution 有一些弊端,它會導緻圖檔中出現 checkerboard patterns,這是因為在 deconvolution 的過程中,濾波器中會出現很多重疊。為了解決這個問題,有人提出了使用 upsample 加卷積層來進行解決。
關于 upsample 有兩種常見的方式,一種是 nearest neighbor interpolation,另一種是 bilinear interpolation。
本文也會使用 upsample 加卷積的方式來進行 decoder 端的處理。
在 tensorflow 中也封裝了對 upsample 的操作,我們使用 resize_nearest_neighbor 對 encoder 卷積的結果 resize,進而再進行卷積處理。經過三次 upsample 的操作,我們得到了 28 x 28 x 64 的資料大小。最後,我們要将這個結果再進行一次卷積,處理成我們原始圖像的大小。
最後一步定義 loss 和 optimizer。
loss 函數我們使用了交叉熵進行計算,優化函數學習率為 0.001。
構造噪聲資料
通過上面的步驟我們就構造完了整個卷積自編碼器模型。由于我們想通過這個模型對圖檔進行降噪,是以在訓練之前我們還需要在原始資料的基礎上構造一下我們的噪聲資料。
我們通過上面一個簡單的例子來看一下如何加入噪聲,我們擷取一張圖檔的資料 img(大小為 784),在它的基礎上加入噪聲因子乘以随機數的結果,就會改變圖檔上的像素。接着,由于 mnist 資料的每個像素資料都被處理成了 0-1 之間的數,是以我們通過 numpy.clip 對加入噪聲的圖檔進行 clip 操作,保證每個像素資料還是在 0-1 之間。
np.random.randn(*img.shape) 的操作等于 np.random.randn(img.shape[0], img.shape[1])
我們下來來看一下加入噪聲前後的圖像對比。
訓練模型
介紹完模型建構和噪聲處理,我們接下來就可以訓練我們的模型了。
在訓練模型時,我們的輸入已經變成了加入噪聲後的資料,而輸出是我們的原始沒有噪聲的資料,主要要對原始資料進行 reshape 操作,變成與 inputs_相同的格式。由于卷積操作的深度,是以模型訓練時候有些慢,建議使用 gpu 跑。
記得最後關閉 sess。
結果可視化
經過上面漫長的訓練,我們的模型終于訓練好了,接下來我們就通過可視化來看一看模型的效果如何。
可以看到通過卷積自編碼器,我們的降噪效果還是非常好的,最終生成的圖檔看起來非常順滑,噪聲也幾乎看不到了。
有些小夥伴可能就會想,我們也可以用基礎版的 input-hidden-output 結構的 autoencoder 來實作降噪。是以我也實作了一版用最簡單的 input-hidden-output 結構進行降噪訓練的模型(代碼在我的 github)。我們來看看它的結果:
可以看出,跟卷積自編碼器相比,它的降噪效果更差一些,在重塑的圖像中還可以看到一些噪聲的影子。
結尾
至此,我們完成了基礎版本的 autoencoder 模型,還在此基礎上加入卷積層來進行圖檔降噪。相信小夥伴對 antoencoder 也有了一個初步的認識。
basicae,基礎版本的 autoencoder(包含 jupyter notebook 和 html 兩個檔案)
easydae,基礎版本的降噪 autoencoder(包含 jupyter notebook 和 html 兩個檔案)
convdae,卷積降噪 autoencoder(包含 jupyter notebook 和 html 兩個檔案)
如果覺得不錯,可以給我的 github 點個 star 就更好啦!
====================================分割線================================
本文作者:ai研習社