LoginSignup
1
2

More than 3 years have passed since last update.

VAE(変分オートエンコーダ)の判断根拠の可視化(異常検知)

Last updated at Posted at 2021-06-02

概要

 土木の分野では、エラー取り(異常検知)はよく行われます。時系列データから異常検知を行う際は、画像に変換してからAIを使う場合が多く見受けられます。異常検知においても、判断根拠の可視化は必要だと考えています。

 画像分類において、判断根拠の可視化の方法として、GradCamが知られているが、VAE(変分オートエンコーダ)の場合は、知らなかったので調べてみました。

 論文は、以下を参照しました。
https://arxiv.org/pdf/1911.07389.pdf

 PyTorchを使った実装は、以下があります。私は、tensorflowを使う人なのでわかりませんでした。

日本語の解説として、

があります。

VAEを使った異常検知(One-Class VAE)

 簡単にMNISTを例に異常検知を説明する。手書き数字0の画像を正常とし他の手書き数字1~9の画像を異常とする。

 数字0の画像のみでVAEを学習させ、数字0の画像を再構成させる。学習が上手くいけば、本来の数字0の画像とVAEによって再構成された数字0の画像の誤差は小さいはず。

 そして、手書き数字1~9の画像をVAEに入力すると、数字0以外の画像は再構成されないはずなので、数字0以外の画像とVAEによって再構成された画像の誤差は大きくなり、異常だと見抜くことが出来るだろう。
 
 あとは、適当に誤差の閾値を決め、異常かどうか決めればよい。

Attention map の作成

 VAEが構築できたなら、Attention map の作成する。基本的には、ほぼGradCamと同じである。ただし、GradCamの場合は、勾配を計算する際に予想クラスの出力(1つ)が必要であるが、VAEの場合は、潜在ベクトル(全部)が必要である。

 エンコーダーの最後の畳み込みレイヤーから、特徴量マップ $ A\in \mathbb{R}^{n\times h \times w}$ と潜在ベクトル $z\in \mathbb{R}^{D}$ を取得する。$n,h,w,D$ は、順にチャネル、畳み込み後の高さ・幅、潜在空間の次元である。

 以下の上・下付き添え字 $k,p,q,i$ は、順にチャネル、畳み込み後の高さ・幅、潜在空間の成分を表す。

 特徴量マップと潜在ベクトルから、各チャネルごとに $\alpha_k^i$ を計算する。

\alpha_k^i=\frac{1}{h}\frac{1}{w}\sum_{p=1}^{h}\sum_{q=1}^{w}\left(\frac{\partial z_i}{\partial A_k^{pq}}  \right)

 次に、各潜在ベクトルごとのAttention map $M^{i}$ を計算する。

M^{ipq} = \rm{Relu}\left(\sum_{k=1}^n \alpha_k^i  A_k^{pq} \right)

 最後に、各潜在ベクトルごとのAttention map $M^{i}$ の平均をとり、Attention map $M$ が計算される。

M^{pq} =\frac{1}{D} \sum_{i=1}^D M^{ipq} 

 コードは(たぶん)こんな感じになる。

# batch サイズは1であることが前提
with tf.GradientTape(persistent=True) as tape:
    A,z = エンコーダーからの出力

    dim = tf.shape(z)[1]
    grads = [tape.gradient(z[:,i],A)  for i in range(dim)]

alpha =tf.reduce_mean(grads, axis=(1,2,3))
# チャネル平均
M_i = tf.einsum('ij,klmj->iklm', alpha, A)
M_i= tf.nn.relu(map)
# 潜在空間の次元ごとに平均
M = tf.reduce_mean(M_i, axis=0)

The normal difference distribution

 理想的には、正常データによって学習されて得られた潜在空間は、正規分布に従って生成されているはず。正常データを用い学習されたエンコーダーに、異常データを入力して得られた潜在空間の確率分布は、学習によって得られた正規分布とは異なるはず。正常データと異常データの差のを表す確率分布(normal difference distribution)を使い、異常データのAttention mapを作成することを考える。

 以下の添え字 $x$ は正常データ、添え字 $y$ は異常データを表す。

 最初に、学習されたエンコーダに正常データを代入し、潜在空間のサンプル $z$ を生成させる。多数のサンプル $z$ から、各ベクトルの成分ごとに平均 $\mu_i^x$ と分散 $\sigma_i^x$ を計算する。
 
 次に、学習されたエンコーダに異常データを代入し、平均 $\mu_i^y$ と分散 $\sigma_i^y$ を出力する。正常データの平均 $\mu_i^x$ と分散 $\sigma_i^x$ と異常データの平均 $\mu_i^y$ と分散 $\sigma_i^y$ を使い、以下のnormal difference distribution から、潜在ベクトル $u$ を生成する。

P_{q(z_i|x) - q(z_i|y) }(u_i) = \frac{1}{\sqrt{2\pi\sigma_i^2} }\exp\left\{-\frac{(u_i -\mu_i)^2}{2\sigma_i^2} \right\}  
\mu_i=\mu_i^x - \mu_i^y  \ ,\ \sigma_i^2=(\sigma_i^x)^2 +(\sigma_i^y)^2 

 異常データのAttention mapは、潜在ベクトル $u$ を使い計算する。つまり、上記で説明したAttention map の作成方法で使われる $z$ を $u$ に変えて計算する。

 normal difference distributionに関して、コードは(たぶん)こんな感じになる。

batch = tf.shape(y_mean)[0]
dim = tf.shape(y_mean)[1]
epsilon = tf.keras.backend.random_normal(shape=(batch, dim))

# 通常のVAEで使われる z
z= y_mean + tf.exp(0.5 * y_log_var) * epsilon
# normal difference distributionからサンプリングされる u
u=(x_mean-y_mean) +( tf.sqrt(x_var + tf.exp(y_log_var)) )* epsilon

検証

 MNISTを使い、判断根拠の可視化を行った。手書き数字0の画像を正常データとして学習させ、他の手書き数字1~9の画像を異常データとして入力する。

 通常のVAEの学習は、それなりにできている。もちろん、手書き数字0の画像のみで学習している。以下の可視化では、検証誤差が最小となった重みを使っている。
history_loss_plot.png

 異常かどうかの判断は、入力画像と再構成画像の誤差で判断する。誤差関数は、自乗誤差が使われるかもしれないが、VAEを学習する際にクロスエントロピーを用いたので、クロスエントロピーを使うことにする。

 次に、異常かどうか判断するための閾値を設定する必要がある。MNISTの訓練データ(手書き数字1~9の画像も含む)を使い、再構成誤差の値でヒストグラムを書いてみる。

histogram-test.png

 青色のヒストグラムが、正常データの再構成誤差のヒストグラムであり、オレンジ色のヒストグラムが、異常データの再構成誤差のヒストグラムである。正常データの再構成誤差のヒストグラムの平均は123であり、標準偏差は30ぐらいである。
 
 よって、異常かどうか判断するための閾値を123+2×30とする。つまり、4σ以上のデータ(片側だけど)は、異常とみなす。

 最後に、テストデータを使い異常とみなされた手書き数字1~9の画像を見ていく。3つの画像の順番は、入力画像・再構成画像・Attention map である。

14.png
1.png
18.png
24.png
15.png
22.png
41.png
8.png
9.png

まとめ

 とりあえずMNISTは、それっぽくなる。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2