Convolutional Autoencoder

by allenlu2007

Reference: http://ithelp.ithome.com.tw/articles/10188326

前文討論 convolutional autoencoder 的 denoise 效果很好。具體是因為 multi-layer or convolution 架構的關係 (translation invariance)?如果用 multi-layer fully connected (RBM) autoencoder 是否也有很好的 denoise 效果?

本文仔細討論 multi-layer convolutional autoencoder.  以下摘自 reference.

Introduction

讓我們仔細來看一下之前所實作的 Autoencoder 的網路結構,不管它的 encoder 還是 decoder 都是 fully connected 的結構.那就會有一個問題是如果網路的結構換成 convolutional 的樣子,是不是同樣可以 work 呢?答案是可以的,也就是今天要來看的 convolutional autoencoder

在 CNN 中,主要有兩個部分一個是 convolutional layer,另一個是 max pooling layer.在 autoencoder 的 encoder 以及 decoder,fully connected 的結構都是相對應的,例如 encoder 中第一層是 784 維降到 300 維,則相對的在 decoder 中的最後一層就要是 300 維升到 784 維.因此如果在 encoder 的部分有 convolutional layer,則在 decoder 的部分就要有一個 deconvolutional layer;在 encoder 的部分有 max pooling layer,則在 decoder 的部分就要有一個 max unpooling layer

Deconvolution layer 是用來對應 convolution layer.

max unpooling layer 或是 upsampling layer 是用來對應 max pooling/down sampling layer.

 

Deconvolution

那在 encoder 中的 deconvolution 要怎麼做呢,以下有一個簡單的 gif 例子,而在 tensorflow 的實現上已經有了一個 tf.nn.conv2d_transpose 來讓我們直接使用.

這裡會建立一個包含兩層 encoder 以及兩層 decoder 的 convolutional autoencoder 來試試看它重建輸入的能力如何,而這裡的 strides 我們會設定成 2,也就是說對一個 mnist 輸入影像 28 * 28 維,經過 convolutional layer 之後會變成 14 * 14 維.達到維度降低的效果.以下是各層輸出的維度比較.

  • x 維度: 28 * 28,channel: 1
  • encoder layer1 維度: 14 * 14,channel: 16
  • encoder_layer2 維度: 7 * 7,channel: 32
  • decoder_layer1 維度: 14 * 14,channel: 16
  • decoder_layer2 維度: 28 * 28,channel: 1
  • x recontruct = decoder_layer2

(tf.nn.conv2d_transpose 的參數跟 tf.nn.conv2d 很像,只是要多一個 output_shape)

Build convolution and deconvolution function

def conv2d(x, W): return tf.nn.conv2d(x, W, strides=[1, 2, 2, 1], padding = 'SAME') def deconv2d(x, W, output_shape): return tf.nn.conv2d_transpose(x, W, output_shape, strides = [1, 2, 2, 1], padding = 'SAME') 
 

Max Unpooling

那在 Max Unpooling 要如何實現呢?最簡單的想法是怎麼來就怎麼回去,encoder 在做 max pooling 的時候記下取 max 的索引值,而在 unpooling 的時候依據索引回填數值,其他沒有記錄到的地方則為零.

使用 tf.nn.max_pool_with_argmax 這個函數,它除了會回傳 pooling 的結果外也會回傳對應原本的索引值 (argmax),如下.

The indices in argmax are flattened, so that a maximum value at position [b, y, x, c] becomes flattened index ((b * height + y) * width + x) * channels + c.

理論上在做 unpooling 的時就會用到這裡產生的對應表.不過目前 tensorflow 中沒有 unpooling 這個 op (可以參考 issue).因此以下展示了兩種方法作 unpooling 也都不會用到 argmax.

  1. 使用 Github Issue 討論中的方法,也就是放大兩倍後在固定的地方填值 (ex. 左上角)

  2. 借用影像的 upsample 函數 tf.image.resize_nearest_neighbor 來做等比例放大,也就不會補 0.

註:

  • 在 encoder 中都是一個 convolutional layer 接一個 max pooling,因此 convolutional layer 的 strides 就調回為 1,只讓 max pooling 做降維.
  • 經過測試以後 encoder 使用 relu;decoder 使用 sigmoid,才有比較好的還原效果,都使用 relu 會訓練失敗.

Build helper functions

def conv2d(x, W):return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding = 'SAME') def deconv2d(x, W, output_shape):return tf.nn.conv2d_transpose(x, W, output_shape, strides = [1, 1, 1, 1], padding = 'SAME') def max_unpool_2x2(x, output_shape): out = tf.concat_v2([x, tf.zeros_like(x)], 3) out = tf.concat_v2([out, tf.zeros_like(out)], 2) out_size = output_shape return tf.reshape(out, out_size) def max_pool_2x2(x): _, argmax = tf.nn.max_pool_with_argmax(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding = 'SAME') pool = tf.nn.max_pool(x, ksize = [1, 2, 2, 1], strides = [1, 2, 2, 1], padding = 'SAME') return pool, argmax 
 

Plot recontructed images

可以看到重建的影像有成功,但是有點狀的稀疏情形,因為在經過 unpooling 的時候使採取補 0 的動作.

改用 resize_nearest_neighbor 好很多。但仍然比不上  https://zhuanlan.zhihu.com/p/27902193

的結果。後續有空再研究。

 

今日心得

這裡實現了 convolutional autoencoder,包含使用了 deconvolution 以及 max unpooling 兩個方法來組成 decoder.其中 deconvolution 使用了官方的 op,而 max unpooling 則使用了兩種非官方的方法,其中用 tf.image.resize_nearest_neighbor 的方法所做的 unpooling 效果較好.

遇到最困難的點會是一開始在 decoder 都使用 relu 作 activation function,但是完全得不出好的重建影像,而後來改用 sigmoid 後才成功.我想是因為 relu 會讓小於 0 的部分都等於 0,失去了影響後面網路的能力.或許在更大且複雜的網路,或是較長的訓練時間,才有可能成功.

=> 參考 https://zhuanlan.zhihu.com/p/27902193  有更詳細的說明。

Convolution -> Max pooling  in encoder  (activation = ReLU)

: resize_nearest_neighbor -> Convolution  (activation = ReLu)

 

Encoder 基本上是一樣的。

Difference in decoder:

Convolution <> convolution_transpose   in decoder

Unpooling 如果用 zero insertion (0-th order interpolation) 會很不好。改用 resize_nearest_neighbor.

ReLu <> sigmoid

 



Advertisements