異常検知
次の例は辞書学習を用いた画像の異常検知についてです。
異常検知は、画像が正常なのか、異常なのかを判定するという問題で、パターンマッチングを用いる古典的な方法からディープラーニングを使った最新の手法などさまざまなものがありますが、今回は辞書学習を用いた手法を紹介します。
まず、辞書学習を用いた異常検知についての基本的なアイデアを説明します。学習データとして、何枚かの正常画像を用意しておき、これらから辞書とスパース表現を求めます。このとき、辞書の表現能力を小さくしておくと、学習画像と似た特徴を持つ画像であれば、得られた辞書を組み合わせるだけで再構成できるが、それ以外の画像は辞書を組み合わせるだけでは表現できないという状況が作り出せます。
そこで、新しい画像に対して得られた辞書のスパース表現を求め、元の画像をうまく再構成できれば正常、そうでないなら異常と判定することで、辞書学習を用いた異常検知を実現することができます。
それでは、実際に辞書学習で異常検知ができるか試してみましょう。今回は、以下の2枚の木の板の画像を用意しました。
![左:木の板の正常画像、右:木の板の異常画像](http://cz-cdn.shoeisha.jp/static/images/article/12433/12433_003.png)
このように、異常画像には6箇所に傷や穴などがあります。正常画像と異常画像で木目のパターンが多少異なるため、単純な差分を用いた異常検知は難しいことがわかります。そこで、木目のパターンをうまく学習することで、異常検知ができるか試してみます。
まずは、正常画像のみを用いて辞書学習を行います。このとき、得られた辞書は木の木目のパターンのみだけ再構成できるが、それ以外は再構成できないという状況を作り出したいので、辞書のサイズや辞書を組み合わせる数を小さめに設定しておきます。この一連の流れは以下のようになります。
# 正常画像を読み込み ok_img = np.asarray(Image.open("img/wood-ok.jpg").convert('L')) # 正常画像は再構成できるようにしたいが、それ以外は再構成できないように、表現力を小さく設定する patch_size = (16, 16) n_components = 10 transform_n_nonzero_coefs = 3 # 学習用データの用意 scl = StandardScaler() patches = extract_simple_patches_2d(ok_img, patch_size) patches = patches.reshape(-1, np.prod(patch_size)).astype(np.float64) Y = scl.fit_transform(patches) # 辞書学習 ksvd = KSVD(n_components=n_components, transform_n_nonzero_coefs=transform_n_nonzero_coefs, max_iter=max_iter) X = ksvd.fit_transform(Y) D = ksvd.components_
こうして得られた辞書\(D\)とスパースコード\(X\)を用いて、正常画像に対する再構成画像を求めると、以下のようになり、正常画像は問題なく再構成できることがわかりました。
![正常画像に対する再構成結果](http://cz-cdn.shoeisha.jp/static/images/article/12433/12433_004.png)
では、次に、異常画像に対して、得られた辞書を用いて再構成してみましょう。
# 異常画像を読み込み ng_img = np.asarray(Image.open("img/wood-ng.jpg").convert('L')) # 異常画像に対するスパースコードを求める patches = extract_simple_patches_2d(ng_img, patch_size) patches = patches.reshape(-1, np.prod(patch_size)).astype(np.float64) Y = scl.transform(patches) X = ksvd.transform(Y)
すると、再構成画像は次のようになりました。
![異常画像に対する再構成結果](http://cz-cdn.shoeisha.jp/static/images/article/12433/12433_005.png)
これを見ると、異常画像中の正常な部分に関しては、問題なく再構成ができているのに対し、異常箇所は傷が消えていたり、再構成がうまくできていなかったりすることがわかります。実際、これらの絶対誤差のヒストグラムを描くと以下のようになり、10を閾値として二値化を行うと、以下のような画像を得ることができます。
![異常画像に対する再構成誤差のヒストグラム](http://cz-cdn.shoeisha.jp/static/images/article/12433/12433_006.png)
![異常箇所の抽出](http://cz-cdn.shoeisha.jp/static/images/article/12433/12433_007.png)
このように、再構成誤差が極端に大きい部分のみ抽出することで、異常箇所をあぶり出すことができました。正常画像の方は、得られた辞書でうまく表現できているのに対して、異常画像では異常箇所のみが、うまく再構成できていないということがこの結果からわかります。
一般的に、機械学習で異常検知を行うとき、正常データと異常データの両方を用いて学習を行う必要がありますが、工業製品などでは、正常データは大量に集まるが、十分な数の異常データは集めることが難しいという場合がほとんどです。
しかし、辞書学習を用いると、正常データのみから何が正常かを定めることができ、正常な状態から逸脱しているものを異常と判定することができるようになるため、大量の異常データを学習のために用意する必要がなくなります。実は、株式会社HACARUSの外観検査SPECTROでは、この辞書学習ベースの異常検知アルゴリズムが採用されています。