Tuesday, January 19, 2021

使用 LSTM Autoencoder 進行異常檢測

簡介
上回提到,因為 LSTM 能記住事件的先後次序,所以它特別適合用來處理一些,具有時間順序的數據 (Time Series Data) 。上一期,我們實現了如何使用監督學習 (Supervised Learning) 做未來預測。而這一次,我們會用 LSTM 實現另一種常用的系統:非監督式異常檢測 (Unsupervised Anomaly Detection)。

所謂異常檢測 (Anomaly Detection),其實是指:當我們訓練系統的時候,我們可以透過提供大量正常數據,繼而讓系統自己找出這些數據的規律。然後,當系統突然收到一些與既定規律相違背的數據,它就能自動把這些數據判別為異常。

這種模型,特別適合用來做入侵偵測系統 (Intrusion Detection System):因為黑客入侵等事件並不尋常, 而且入侵模式千變萬化。所以,我們只能大約知道正常的網絡流量會是怎樣。相反,對於黑客進行攻擊的情況,我們所知的並不多。故此,系統設計者能只提供正常情況的數據,以及極少量的入侵數據(這種數據,在 ML 世界中,也稱為非平衡數據 (Unbalanced Data)),然後就讓系統自行找出正常情況的規律。

是次實作,我們會用另一個較簡單的例子:心跳規律來做示範。我們只需要提供正常的心跳數據,讓系統自行訓練,持之以恆,它就能辨認出何謂正常的心跳。訓練完成後,只要系統發現一些奇怪的心跳規律, 它就會回報異常。這次的實作,是改自 Curiousily - Time Series Anomaly Detection using LSTM Autoencoders with PyTorch in Python 的範例。我改進了以下兩點:

  • 使用 DataLoader 來進行 batch training
  • 簡化代碼,使其更易閱讀及改裝

---

實作模型:LSTM Autoencoder
Autoencoder,顧名思義, 就是一種自動編碼 (Encode) 及解碼 (Decode) 的轉碼器。

簡單來講,你可以把它想像成一個有損壓縮 (Lossy Compression) 的工具:首先,它會把收到的訊號,編譯 (Encode) 成一個非常細小的編碼 (Embedding Code,下圖紅色部分,又稱為 Bottleneck Layer)。然後, 當解碼器 (Decoder) 收到這一條小編碼,就會試圖把它解譯成原本的訊號。若果轉譯來回的數據十分相似,就代表它帶來的損失很少,也代表這對 Encoder 和 Decoder 的效能很強。相反,若果它在經過來回轉譯後,輸出與原本大相逕庭,就代表這對 Encoder 和 Decoder 失真的情況嚴重,十分垃圾,並不可靠。

Information Theory 告訴我們,由於數據儲存密度有限,若果壓縮要做到可靠,只有兩個方法:一是加大 Embedding Code,讓它存放更多資料,另一方法,則是按照數據的規律,度身訂造 Encoder 和 Decoder,以達至極致的壓縮比率。 由於我們的系統會固定 Embedding Code 的大小 (Embedding Code 的大小,通常固定在原數據十分之一左右) ,所以系統不能使用第一個方法保存資料。也就正說:系統會被迫用第二個方法達成極致壓縮。

正因如此,隨著訓練時間越長,系統將會越來越依靠訓練數據(也就是正常數據)中的一些固定規律去運作。最後,它會變成一個專為正常數據而設的壓縮器。換句話說,這個壓縮器,能在你放入正常數據的情況下,完美地進行極致壓縮。但如果你放入一些異常數據,這個壓縮器就會突然失控,結果變得強差人意。而我們這次,正正就是利用這一種特性,來判斷數據是否異常。

---

演示代碼

https://github.com/cmcvista/MLHelloWorld/blob/main/LSTMAutoencoder/HeartbeatAutoencoder.ipynb

---

幾個重要變數
是次實作,有幾個變數,需要仔細解釋:

  • 一條時間順序的資料 (data_seq_len) 的長度為 140,特徵 (data_n_features) 為 1。
    這是指一個數據長度為 140, 而維度只有 1( y 座標為 1 )。
  • Embedding code 的長度為 (data_embedding_dim) 為 64,
    也代表它的 Tensor Dimension 是 64*1。
  • 整個系統的大小為 [140, 128, 64, 64, 128, 140]
    (格式:[Input, Hidden, Embedding, Embedding, Hidden, Output] )
  • 訓練 100 次對 LSTM 來講,其實並不足夠,但由於只作示範,就懶得執行太久。

---

Losses 計算方法
損失 (Losses) 的定義,是指解碼後的結果,跟原數據的差異大小。在這次實作中,為了與原例子一樣,我使用了兩組數據之間的 L1Loss 的總和去定義。不過,其實用其他方法(例如 Mean Square Error (MSE) Loss)也能達至相同效果。

---

Threshold 的定義方法
這次因為懶惰,我只用肉眼判斷 Threshold 的值。這個 Threshold 是指:只要任何數據的 total loss 比它小,就應把它當成正常數據。事實上,隨著訓練次數越多,系統會對正常數據的規律更熟悉,輸出會更接近完美,所以它的 total loss 必定會越來越小。故此,訓練越多,Threshold 其實也應該變得越小,以達至更準確的辨識率。

 ---

小結
運用 LSTM Autoencoder 做異常偵測,能達至頗高的成功率,而且原理也不難懂。
不過,由於變數不少,LSTM 訓練時間也頗長, 所以部署時,應先考慮其他方法。

總而言之,這次的實驗也算成功,至少加上 batch support 後,仍能達至預期結果。

---

參考:

[1] Time Series Anomaly Detection using LSTM Autoencoders with PyTorch in Python - https://curiousily.com/posts/time-series-anomaly-detection-using-lstm-autoencoder-with-pytorch-in-python/

[2] 【深度學習】一個簡單又神奇的結構:自編碼機 AutoEncoder - https://jason-chen-1992.weebly.com/home/-autoencoder