論文読みメモ: 深層自己符号化器+混合ガウスモデルによる教師なし異常検知(その4)

以下の論文を読みます。

Bo Zong, Qi Song, Martin Renqiang Min, Wei Cheng, Cristian Lumezanu, Daeki Cho, Haifeng Chen. Deep Autoencoding Gaussian Mixture Model for Unsupervised Anomaly Detection. International Conference on Learning Representations, 2018. https://openreview.net/forum?id=BJJLHbb0-
※ キャラクターに元ネタはないです。お気付きの点がありましたらお手数ですがコメント等にてご指摘ください。
前回:その3 / 次回:まだ
f:id:cookie-box:20180513082851p:plain:w60

前回までのあらすじですが、今回 DAGMM という異常検知向けに多次元データの密度を推定するモデルを開発したので、通信データや医療データの4データセットを用いて異常検知性能を検証します。DAGMM の比較対象となるのは既存の state-of-the-art な4モデル及び DAGMM のフレームワークの各要素の検証となる variant な7モデルの計11モデルです。DAGMM やデータセット、比較対象11モデルについては前回までのメモを参照してください。

f:id:cookie-box:20180513082908p:plain:w60

そうそう、異常検知って、「そのサンプルに対する確率密度が閾値以下なら異常」みたいな判定をすると思うんだけど、複数のモデルを比較検証するときはどんな風に閾値を決めるんだろ。同じ閾値ではフェアな比較ができなさそうだよね。そもそも密度推定によらない異常検知手法もあるし。

f:id:cookie-box:20180513082851p:plain:w60

それも含めて、検証実験のコンフィギュレーションを説明しましょう。今回は2つの実験を行うのですが、1つ目の実験では、各データセットをまず訓練用とテスト用に2分割します。そして、訓練用データから正常データのみを取り出してモデルを訓練します。そのモデルをテストデータに適用するのですが、今回は教師なし学習なので、モデルの出力は各サンプルが「正常か異常か」ではなく「どれくらい異常か」ですね。そこで、今回は次のようにします。「各データセットの異常データ混入割合をa%としたとき、テストデータ異常度の上位a%が『異常』、その他が『正常』と判定されたとする。」 例えば、3つ目の不整脈のデータセットには異常データが15%含まれているので、異常度の上位15%が『異常』判定ということです。こうすれば、通常の分類問題の評価指標がそのまま利用できますね。つまり、精度、感度、F値で各モデルを比較します。

f:id:cookie-box:20180513082908p:plain:w60

なるほど、それならある程度フェアな比較になるのかな。

f:id:cookie-box:20180513082851p:plain:w60

それでその1つ目の実験の結果ですが、論文10ページに数表がありますが、見やすいようにF値のみグラフにしてみました。

f:id:cookie-box:20180524193826p:plain:w700

f:id:cookie-box:20180513082908p:plain:w60

わざわざ!? 甲状腺データと不整脈データの方が通信データよりも異常データを分離する難易度高めなんだね。あと KDDCUP の E2E-AE は何があったし…。

f:id:cookie-box:20180513082851p:plain:w60

さらに2つ目の実験です。1つ目の実験との違いは、訓練時に正常データのみを利用するのではなく、わざと異常データをコンタミします。KDDCUP データセットに対する結果が以下です。こちらもF値のみですがまた勝手にグラフを描いてみました。ratio は異常データのコンタミ率です。0% のF値は上のグラフの該当するF値と同じです。このグラフから提案手法 DAGMM はコンタミにも強いことがわかります。

f:id:cookie-box:20180524201948p:plain:w640

f:id:cookie-box:20180513082908p:plain:w60

あー、OC-SVM は与えられた訓練データを囲む面を求めようとするから、そこに異常データがコンタミしていたら異常検知としての性能がガタ落ちしちゃうって感じなのかな。

f:id:cookie-box:20180513082851p:plain:w60

4.5 節に低次元表現の可視化がありますね。Figure 3. (a) の DAGMM の低次元表現は、他のモデル (b) (c) (d) よりも正常データ(青)と異常データ(赤)をよく分離できていると思いませんか?

f:id:cookie-box:20180513082908p:plain:w60

まあ、(a) が比較的赤と青が混ざっていないようには見えるけど…3次元プロットじゃ他の角度からも見てみないとよくわかんないような。

f:id:cookie-box:20180513082851p:plain:w60

あとこの節で取り上げられているのは、Figure 3. の (c) や (d) は事前学習をせず結合学習する手法であるにもかかわらず、事前学習する (b) に比べて低次元表現にあまり変化がみられないという点ですね。ということからも、非線形な次元削減を最初から regularization して学習するのが肝要だと。しかも、(a) DAGMM の自己符号化器部分の再構築エラーは事前学習した自己符号化器並みに小さいということなので、DAGMM は自己符号化器の学習のフレームワークとしても優秀なのではないかと書いてあるようなないような気がしますね。

f:id:cookie-box:20180513082908p:plain:w60

どっち!? ちゃんと読んで!

f:id:cookie-box:20180513082851p:plain:w60

論文の内容は以上になります。あ、今後の課題とかは特にないです。

f:id:cookie-box:20180513082908p:plain:w60

ないんだ!?

f:id:cookie-box:20180513082851p:plain:w60

実際にベンチマークに用いられたデータセットを見てみましょう。どのデータセットも Web 上で見つかると思いますが、一番サイズが小さいところで不整脈のデータは以下に落ちてました。
UCI Machine Learning Repository: Arrhythmia Data Set
これは一番最後のカラムが不整脈の種類ラベルみたいですね。それで、論文には dimension が 274 とあるのにラベルを除いても 279 カラムあってどういうことだろうかと思ったんですが、11~15カラム目に欠損値があったのでおそらくそこを除いていると思います。なので欠損カラムを取り除いてとりあえず自己符号化器にかけてみました。ニューロン数も活性化関数もバッチサイズもエポック数も全て論文に明記されていますからね。あ、論文には tensorflow で実装したとありますが、さしあたり keras で書きます。

# -*- coding: utf-8 -*-
import numpy as np
import pandas as pd
from keras.models import Sequential
from keras.layers import Dense

if __name__ == '__main__':
  df = pd.read_csv('arrhythmia.data', header=None)
  df = df.drop(df.columns[[10,11,12,13,14]], axis=1)
  y = df.iloc[:,274].values
  x = df.drop(df.columns[[274]], axis=1).values
  print(y.shape)
  print(x.shape)
  
  encoder = Sequential()
  encoder.add(Dense(10, activation='tanh', input_shape=(274,)))
  encoder.add(Dense(2))
  
  decoder = Sequential()
  decoder.add(Dense(10, activation='tanh', input_shape=(2,)))
  decoder.add(Dense(274))
  
  compression_net = Sequential()
  compression_net.add(encoder)
  compression_net.add(decoder)
  compression_net.compile(loss='mean_squared_error', optimizer='RMSProp')
  compression_net.fit(x, x, shuffle=True, epochs=10000, batch_size=128)

これで学習できます。まあ本当は全データを訓練にかけるんじゃなくて、訓練用とテスト用に分割した上で訓練用から異常データを除いて学習しなきゃなんですけど。そもそも、自己符号化器だけ学習しちゃ駄目だし。

f:id:cookie-box:20180513082908p:plain:w60

じゃあもうちょっと頑張ろうよ!?

f:id:cookie-box:20180513082851p:plain:w60

今日はもう遅いので…。ところで、R を書いた直後に Python を書くと混乱しませんか?

f:id:cookie-box:20180513082908p:plain:w60

知らないよ…。

(次回があれば)つづく