前言html
這周工做太忙,原本想更把 Attention tranlsation 寫出來,但一直抽不出時間,等後面有時間再來寫。咱們這周來看一個簡單的自編碼器實戰代碼,關於自編碼器的理論介紹我就不詳細介紹了,網上一搜一大把。最簡單的自編碼器就是經過一個 encoder 和 decoder 來對輸入進行復現,例如咱們將一個圖片輸入到一個網絡中,自編碼器的 encoder 對圖片進行壓縮,獲得壓縮後的信息,進而 decoder 再將這個信息進行解碼從而復現原圖。git
做圖工具:OmniGrafflegithub
自編碼器其實是經過去最小化 target 和 input 的差異來進行優化,即讓輸出層儘量地去復現原來的信息。因爲自編碼器的基礎形式比較簡單,對於它的一些變體也很是之多,包括 DAE,SDAE,VAE 等等,若是感興趣的小夥伴能夠去網上搜一下其餘相關信息。網絡
本篇文章將實現兩個 Demo,第一部分即實現一個簡單的 input-hidden-output 結的自編碼器,第二部分將在第一部分的基礎上實現卷積自編碼器來對圖片進行降噪。dom
工具說明ide
TensorFlow1.0函數
jupyter notebook工具
數據:MNIST 手寫數據集學習
完整代碼地址:NELSONZHAO/zhihu測試
第一部分
首先咱們將實現一個如上圖結構的最簡單的 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 數據集來進行實驗,關於數據導入的步驟再也不贅述,請下載代碼查看。在開始以前,咱們先經過一張圖片來看一下咱們的整個模型結構:
做圖工具:OmniGraffle
咱們經過向模型輸入一個帶有噪聲的圖片,在輸出端給模型沒有噪聲的圖片,讓模型經過卷積自編碼器去學習降噪的過程。
輸入層
這裏的輸入層和咱們上一部分的輸入層已經不一樣,由於這裏咱們要使用卷積操做,所以,輸入層應該是一個 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 也有了一個初步的認識。
完整的代碼已經放在個人 GitHub(NELSONZHAO/zhihu) 上,其中包含了六個文件:
BasicAE,基礎版本的 AutoEncoder(包含 jupyter notebook 和 html 兩個文件)
EasyDAE,基礎版本的降噪 AutoEncoder(包含 jupyter notebook 和 html 兩個文件)
ConvDAE,卷積降噪 AutoEncoder(包含 jupyter notebook 和 html 兩個文件)
若是以爲不錯,能夠給個人 GitHub 點個 star 就更好啦!