#はじめに
One Class SVMとは異常検知などに使われる教師なし学習です.通常のSVMでは多クラス分類ができますが,One Class SVMはその名の通り1クラス分類(そのクラスか,それ以外か)となります.One Class SVMについて詳しくは「異常検知のためのOne Class SVM」の記事を参考にしてください.今回はこのOne Class SVMと,機械学習において超有名なデータセットであるMNISTを使って簡単な異常検知(?)をしたいと思います.実験設定はいたって簡単,「1」を正常画像「7」を異常画像とします.MNISTには同じ「1」でも形が様々なものがあるので,どれだけのパターンの「1」を正確に正常と判断できるかどうかも重要となってきますね.
今回の実装に関するソースコードはこちらからどうぞ.
#環境
version | |
---|---|
Python | 3.5.5 |
Keras | 2.1.5 |
TensorFlow | 1.7.0 |
#One Class SVMの実装
One Class SVMを実装するにあたって,scikit-learn(sklearn)を使用しました.このscikit-learnを使うとなんと2行でOne Class SVMの学習が可能となります.便利な世の中ですね.
scikit-learnのOne Class SVMについての公式ドキュメンテーションはこちらになります.
以下が今回のOne Class SVMの実装にあたるコードです.
def execute_OCSVM(x_train_1, x_test_1, x_test_7):
#One Class SVM を実行
clf = svm.OneClassSVM(nu=0.2, kernel="rbf", gamma=0.001)
clf.fit(x_train_1)
#配列の頭から「1」を100個,「7」を20個取り出し予想
test = np.vstack((x_test_1[0:100], x_test_7[0:20]))
pred = clf.predict(test)
return pred
上記のコードにおいて注意しなければいけない点は,nuとgammaです.nuとgammaはハイパーパラメータなのですが,これらの値を変更すると結果に大きな違いが生じます.初期設定ではnu=0.5,gamma=autoとなっています.One Class SVMを用いた異常値検知の記事によると,gamma=1/特徴量の総数とされているので,今回はgamma=0.001としています.nuは異常の割合とされているので,nu=0.2としています(「1」が100枚,「7」が20枚で実験しているため約2割).
#結果
とりあえず画像をそのまま入れてみる
まずは何も考えずに,取得したMNISTの画像をそのままOne Class SVMに入力し,学習・分類をしてみました.結果が以下の図のようになっています.横軸が入力した画像(1~100が「1」、101~120が「7」),縦軸が判定結果(1なら正常,-1なら異常と判定)です.
accuracyは0.90(90%)でした.「7」はすべて異常と判定されていますね.やはり横に長い部分を異常と判定しやすいのでしょう.「1」はところどころ誤って異常と判定してしまっています.実際に異常と判定された「1」のうち,いくつかがこちらです.
湾曲していたり,7に近い形の画像が異常と判定されてしまっています.人が書く「1」にも様々な形があるので難しいのかもしれません.
Autoencoderで特徴量を抽出して入れてみる
簡単なAutoencoderをkerasで実装し,中間層で取得できた特徴量をOne Class SVMに入力すれば精度向上するのでは?と考え実際にやってみました.Autoencoderでは,MNISTの「1」のみの画像で学習を行いました.「1」のみを学習させることで再構築の際に「1」に関係する有効な特徴量が得られることを期待します.結果が以下の図のようになっています.
精度悪化してるやん!!!!!!!!
accuracyが0.84(84%)に下がってしまいました.これは自分の不徳の致すところです.考えが甘かったです.ごめんなさい.MNIST自体簡単な画像ですし,様々な形の「1」を学習することで,どちらかといえば汎用的な特徴量が得られてしまったのではないかと考えます.(実際,Autoencoderに学習されていない他の数字の画像を入力しても,かなりいい精度で再構築できてしまっていたので・・・)実際に得られた中間層の出力画像が以下のようになっています.
#まとめ
今回はOne Class SVMとMNISTを用いて簡単な異常検知(?)を行いました.異常は正確に検知できていましたが,正常を正しく正常とみなすことが難しいという結果となりました.通常の異常検知の現場では,固定されたカメラである部品を同じ角度から撮影し,傷を検知することなどに用いますので,正常画像には全体としてあまり差がない状態となります.今回のような状態はまれですね.
また,Autoencoderを用いて有効な特徴量を得ようと試みましたが,思い通りにいきませんでした.有効な特徴量の取得の仕方は再度検討したいと思います.