畫一張一樣照片來做異常檢測

用 Keras 實作 GANomaly

Abby Yeh
Taiwan AI Academy
7 min readJul 30, 2019

--

Photo by Lianhao Qu on Unsplash

Autoencoder 通常用來降低高維度的資料,讓資料被投射在一個比較容易被分開的空間。以圖片為例, Autoencoder 會將高維度的資料投射到低維,在讓模型利用這些低維的資料,畫出一張和原本的一樣的圖片。這樣就能夠確保,雖然圖片降到低維還是保有應該要有的資訊。

然而,跟 SVD 這些使用統計學來降為的方法不同,Autoencoder 只有在與訓練資料相似的資料上表現的很好。GANomaly 卻把這個本來應該是缺點的特性拿來做異常偵測。

GANomaly 只用正常的資料訓練 Autoencoder,所以我們可以預期這個 Autoencoder 只有在測試資料是正常時,才在重畫出一張相似的圖片。當畫出來的資料不像原本的圖時,我們就判斷他為異常資料。

— Keras實作 —

模型

首先,我們要來用 Kears 建 GANomaly 的模型。

上圖為 GANomaly 的架構,他有三個子網路。第一個子網路是 autoencoder,將圖片壓縮到較低的維度後再重建回原本的圖片。為了計算loss 方便,我們將 generator encoder 的部分取出來,所以會有 encoder,和完整的 autoencoder (genrator) 兩個 model。

ganerator’s encoder

encoder 還要注意 encode 完的向量大小不要太大,不然很容易無論是無是訓練資料都可以很完美的複製回去,就沒辦法分類了。所以在最後一層,我們過 global average pooling,讓維度快速縮小。

接著是完整的 generator,先過 encoder 再接上 decoder 的網路。

complete generator

然後,是第二個 encoder,負責把 generator 建出來的圖片在 encode 一次,結構和 generator 中的 encoder 相同。這個子網路用來最小化正常圖片和 generate 出來的圖片的 laten vector 差,因為判斷時是用這兩個向量的距離判斷的,所以我們希望正常照片(訓練資料)的差越小越好。

encoder

最後一個是 feature extractor,將圖片過 convolution layers 得到圖片的特徵。

feature_extractor

損失函數

在更新 generator 的時候,GANomaly 用了 3 個損失函數。第一個是 Adversarial Loss,希望 decode 出來的圖片可以騙過 discriminator。不過並不是讓 discriminator 把 decode 出來的圖片盼成真實圖片,而是希望 discriminator 萃取出來的特徵跟原本的圖片的特徵越像越好,也就是過了 feature extractor 的 L2 loss。

用 Keras custom layer 把 loss 刻出來就會像下面的程式,這個 layer 會需要兩個 input,一個是訓練資料 (x[0]),另一個則是過完 generator 的結果 (x[1]):

第二個是 Content Loss ,原始照片和產生的照片的 L1 loss。雖然用 Adversarial Loss 就可以讓 generator 產生以假亂真的正常圖片,但是為了避免和原本的照片不同,所以還是要加上 Content Loss。

寫成 layer 一樣需要兩個 input:

第三個是 Encoder Loss ,為原本的圖片和產生的圖片的 latent vaector 的 L2 loss。在判斷是否為正常照片的時候,是用原本的圖片和產生的圖片的 latent vectors 的距離來判斷,所以在更新 autoencoder 的時候,同時也要最小化兩個向量的距離。

寫成 layer 如下:

最後,我們把這些 loss 接起來, compile 完就可以訓練了:

要注意的是,這個用來訓練的 model 本身的輸出就是算好的 loss,所以我們要自己定義一個損失函數直接回傳預測出來的值。

判斷器

discriminator 的目的是為了讓 autoencoder decode 出來圖片更像真實圖片。這部分和一般的 GAN 相同,將 feature extractor 的部分接上只有一個 node 的 dense layer,再過 sigmoid 就輸出 real/fake 的機率就可以了!損失函數也和 GAN 相同,用 binary_crossentropy,將 Genertor 產生的圖片判為假照片,訓練圖片判為真照片。

訓練

因為我們不需要任何 label,所以我們可以自己宣告一個 data generator,y的部分只要回傳三個長度跟訓練資料一樣長的資料就好。

跟 GAN 一樣,訓練的時候要用 train_on_batch,輪流訓練 discriminator 和 generator。

在每個迴圈裡,先呼叫 data generator 的 __next__ 拿到 x 和 y。接著訓練 discriminator。先用 generator 預測圖片得到假的圖片,並把這些圖片與訓練資料連再一起,讓 discriminator 判斷訓練資料為 0, generator的圖片為 1。再來用上述的 loss 訓練 generator。

評估

訓練完我們就可以來判斷新的照片使否為正常資料了!每一筆測試資料都先算出他的分數:

也就是原圖和產生的圖 encode 完的 L1 距離。接著再把所有分數做 MinMaxScale 到 [0, 1],當作是異常的機率就可以了!

但是在實作上發現,兩邊都用 generator 的 encoder 效果才會好,推測可能是 encode loss 導致即使 decode 完結果完全不像, encode 的結果還是很像。

所以下面的程式碼是用 generator 的 encoder 代替論文中外部的 encoder。

— 結果—

這是用 mnist 資料集的結果,訓練的時候的時候只看過 1 ,測試的時候有 1000 張 1 或 7 的照片 (分別為藍色和粉紅色)。從上圖可以看到,雖然無法直接用大於 0.5 來判斷,但效果其實很不錯。

如果畫出過 autoencoder 前後的圖片 (左邊是過 autoencoder 前,右邊是decode 完的結果),可以看到因為模型只看過的 1 圖片,所以不是 1 的圖片 decode 完還是很像 1,就可以用 decode 前後來分辨是否正常。

GANomaly 真的是一個簡單又實用的模型呢!只要有正常的資料的照片,就可以自己學會辨識異常資料。

— 完整程式—

https://github.com/leafinity/keras_ganomaly/blob/master/ganomaly.ipynb

— 參考資料—

[1] GANomaly: Semi-Supervised Anomaly Detection via Adversarial Training

--

--