Autoencoder

德雷克.帕拉維奇尼(Derek Paravicini)是一位英國鋼琴家,雖然他自閉、全盲又患有重度學習障礙,但是,他卻擁有相當神奇的記憶能力,他的腦海中記憶了所有聽過的曲子,隨時可以在琴鍵上彈出來,甚至於加上變奏和即興,外界給他的稱號是「人體iPod」,讚嘆他這種即聽即彈的超強能力。

同樣的,也是在英國倫敦、也是四十左右的年紀,一位名字叫史蒂芬(Stephen Wiltshire)的男子有著「人肉照相機」的稱號,他對於圖像有著「過目不忘」的能力,可以把腦海中的街景畫出來和現實中一模一樣,他最有名的事蹟是搭乘直升機飛過泰晤士河,在短短的數十分鐘航程中憑著記憶力畫出4公尺長和真實景色一模一樣的圖畫。

相關圖片

Autoencoder

其實,我們每個人都擁有類似的能力,想想中國古代的科舉考試和目前深為人所垢病的填鴨式教學,我們所企求的不就是這樣的記憶能力嗎?在目前流行的AI領域,也有某種模型具有相同的功能角色,那就是Autoencoder。

autoencoder_schema.jpg

我們在設計Autoencoder的模型時,會將Encoder的輸出維度設定為小於原圖,Decoder輸出維度則與原圖相片相同,此外,再將原圖與輸出圖的差異作為loss來計算,這使得模型在訓練時,Encoder將被強迫學習圖形中的重要特徵忽略不重要的部份,以便輸出複雜度較小的資訊。接著,Decoder試著將資訊還原並學習將loss降低到與原圖差異最小的程度。因此,如上圖所示,Autoencoder由兩個model組成,Encoder將讀入的圖片資料重新編碼為維度較小、也就是有損且壓縮過的資訊,接著再透過Decoder讀入,重新生成與原圖尺寸一樣、內容接近相同的圖形。

基於此特性,Autoencoder的主要應用範圍:第一是數據去噪,第二是為可視化的降維,不過隨著GAN生成對抗模型的流行,Autoencoder也出現多種變形應用於其中,例如,目前最流行的生成模型之一Variational AutoEncoder(VAE),透過強化Encoder生成數據,可產生訓練樣本中沒有看過的新樣本。

Use Autoencoder

  1. 載入相關模組
from keras.layers import Dense, Input

from keras.layers import Conv2D, Flatten

from keras.layers import Reshape, Conv2DTranspose

from keras.models import Model

from keras.datasets import mnist

from keras.utils import plot_model

from keras import backend as K

import glob, os

import cv2

import random

import numpy as np

import matplotlib.pyplot as plt
  1. 定義load images function
def load_images_from_folder(folder):

    images = []

    for folders in glob.glob(folder+"/*"):

        print("Load {} ...".format(folders))

        for filename in os.listdir(folders):

            print("TEST:", os.path.join(folders,filename))

            img = cv2.imread(os.path.join(folders,filename), 0)

            img = cv2.resize(img, sizeFixed)

            if img is not None:

                images.append(img)

    return np.array(images)
  1. 定義圖片尺寸為 (56,56), 灰階
sizeFixed = (56,56)

channels = 1
  1. 模型的設定
batch_size = 32

CNN filter尺寸

kernel_size = 3

CNN層數及filter數目

latent_dim = 32

CNN層數及filter數目

layer_filters = [32, 64, 64]
  1. 分別載入train dataset及test dataset的path
x_train = load_images_from_folder("datasets_train")

x_test = load_images_from_folder("datasets_test")
  1. 將dataset shape由(圖片數量, height, width)調整為(圖片數量, height, width, channels),例:(26521, 56, 56) 🡪 (26521, 56, 56, 1)。
image_size = x_train.shape[1]

x_train = np.reshape(x_train, [-1, image_size, image_size, 1])

x_test = np.reshape(x_test, [-1, image_size, image_size, 1])

x_train = x_train.astype('float32') / 255

x_test = x_test.astype('float32') / 255
  1. 建構encoder model
inputs = Input(shape=input_shape, name='encoder_input')

x = inputs
  1. 依layer_filters定義的值建立CNN layer
for filters in layer_filters:

    x = Conv2D(filters=filters,

                  kernel_size=kernel_size,

                  activation='relu',

                  strides=2,

                  padding='same')(x)

shape = K.int_shape(x)  # 🡨 此shape將作為下方的12) decoder model的input shape
  1. 輸出維度數目latent_dim = 32,亦即將原圖降維至此數。
x = Flatten()(x)

latent = Dense(latent_dim, name='latent_vector')(x)
  1. 建立Encoder模型
encoder = Model(inputs, latent, name='encoder')

encoder.summary()
  1. Encoder model summary

  1. 建構decoder model
latent_inputs = Input(shape=(latent_dim,), name='decoder_input')
  1. 注意Decoder的輸入shape與Encoder的輸出相同
x = Dense(shape[1] * shape[2] * shape[3])(latent_inputs)

x = Reshape((shape[1], shape[2], shape[3]))(x)
  1. 反捲積Conv2DTranspose,等於是反向的CNN,它與單純的將圖片放大的UpSampling2D不同,Conv2DTranspose透過filters與Conv2D一樣具有學習功能。
for filters in layer_filters[::-1]:

    x = Conv2DTranspose(filters=filters,

                           kernel_size=kernel_size,

                           activation='relu',

                           strides=2,

                           padding='same')(x)
  1. 輸出層,filters=1對應到輸出channel=1
outputs = Conv2DTranspose(filters=1,

                          kernel_size=kernel_size,

                          activation='sigmoid',

                          padding='same',

                          name='decoder_output')(x)

decoder = Model(latent_inputs, outputs, name='decoder')

decoder.summary()
  1. Decoder model summary,圖片經由Conv2DTranspose逐層還原成56×56大小。

  1. Encoder及devoder兩個模型合併為autoencoder模型。
autoencoder = Model(inputs,

              decoder(encoder(inputs)),

              name='autoencoder')

autoencoder.summary()
  1. Autoencoder model summery

  1. 開始訓練。
Loss function用Mean Square Error (MSE) ,Optimizer用Adam

autoencoder.compile(loss='mse', optimizer='adam')

autoencoder.fit(x_train,

                x_train,

                validation_data=(x_test, x_test),

                epochs=1,

                batch_size=batch_size)
  1. 訓練一個epoch即可,valid loss為0.013

  1. 使用test dataset測試
x_decoded = autoencoder.predict(x_test)
  1. 繪出最後八張臉孔並顯示輸出結果。
imgs = np.concatenate([x_test[:8], x_decoded[:8]])

imgs = imgs.reshape((4, 4, image_size, image_size))

imgs = np.vstack([np.hstack(i) for i in imgs])

plt.figure(figsize=(8,8))

plt.axis('off')

plt.title('Input: 1st 2 rows, Decoded: last 2 rows')

plt.imshow(imgs, interpolation='none', cmap='gray')

plt.savefig('input_and_decoded.png')

plt.show()

/var/folders/dk/5y_dt0p16ggfckw19wkrhpkh0000gn/T/com.microsoft.Word/Content.MSO/8A544C94.tmp

Autoencoder實作

網路上最常見的範例是使用MNIST dataset,將28×28的手寫數字圖片encoder為較小的維度之後再decoder回來,圖片前後看得出差異但仍很近似,但實際後者是壓縮過維度少了很多。

basic_ae_32.png

我們另外再從FDDB(Face Detection Data Set and Benchmark)下載人臉圖庫,總共26,521張圖片作為Training dataset,一樣使用上述程式來訓練看看。

Test dataset除了FDDB之外,亦使用了公司人臉刷卡機所搜集的臉孔22張。

Test dataset的輸出結果,下方左側為FDDB test dataset的部份圖片,右側為使用刷臉系統搜集的部份同仁的圖片,可以看出使用西方臉孔為主的FDDB dataset訓練出的model,在還原東方臉孔時會產生較大的差異。

/var/folders/dk/5y_dt0p16ggfckw19wkrhpkh0000gn/T/com.microsoft.Word/Content.MSO/8A544C94.tmp/var/folders/dk/5y_dt0p16ggfckw19wkrhpkh0000gn/T/com.microsoft.Word/Content.MSO/82B17016.tmp

Autoencoder除了用於降維數據、擷取重要特徵、還原圖片之外,它還有一些有趣的應用,例如圖片自動除噪、上色等,此外,目前很流行的 GAP生成對抗模型,也參考了很多Autoencoder的原理。