AI
2024/08/22
小山 佳祐

【機械学習】異常検知_autoencoder

AI

今回は機械学習手法の一つであるAutoencoder、およびその異常検知への応用について解説していきます。

はじめに

機械学習手法の一つであるAutoencoder、およびその異常検知への応用について解説していきます。


 Autoencoderとはニューラルネットワークの一種で教師なし学習に分類されます。ニューラルネットワークでは入力層から出力層までデータが流れていきますが、オートエンコーダにおいてトレーニングデータをそのまま出力するように学習します。そこで入力層と出力層との差分を確認することで異常検知に応用させたり、本質部分のみを抜き出すような特徴からノイズ除去などに応用されたりします。本ブログではAutoencoderの説明、異常検知への応用、プログラムによる実装まで解説します。

Autoencoderとは

 Autoencoderはニューラルネットワークの一種であり、単純なモデルは入力層、中間層、出力層から成り立ちます。Autoencoderの出力層は、入力層と同じデータを出力するように機能します。ここでAutoencoderでは入力層から中間層のことを「エンコーダ」、中間層から出力層のことを「デコーダ」と呼びます。一般的なオートエンコーダにおいて最も特徴的なことは、入力層と出力層のノードの数が同じであり、中間層のノード数が小さくなるということです。これにより入力層と同じ構造を持つ出力を考えることが出来、中間層で重要な特徴のみを抽出することが出来ます。以下、イメージ図です。



Autoencoder

Autoencoder




入力層から中間層までのエンコーダでは入力データから復元に関して重要な情報のみを取り出す次元削減部、中間層から出力層までのデコーダでは抽出データから元のデータを生成するデータ生成部と考えられます。


中間層、出力層では通常のニューラルネットワークと同様に入力層から受け取った各特徴量を重みづけし和を取り、ノードの中で活性化関数を通し計算します。


オートエンコーダの本質は学習データからいかにして適切な中間層を作成するかです。適切に中間層を作成することで学習データの重要な情報を表現することが出来るのです。

オートエンコーダと異常検知の関係

ここまで、オートエンコーダとはニューラルネットワークの一種であり入力層と同じように出力層を生成させるため学習にするということを述べてきました。したがって学習データに正常データのみを与えて学習させたオートエンコーダモデルに対して、異常値が入力したとき、入力層と出力層の値は正常データの差に比べて大きくなると考えられます。これを利用することで異常検知に用いられるのです。

実装

では、入力データに正常データを与え異常検知する流れを実装していきます。


今回はMNISTデータセットを使用して実装します。これは0~9の数値が28*28のピクセルで表現された手書き画像データセットです。

サンプルデータ:



サンプルデータ

サンプルデータ



今回は入力データに0,1,2を与え、(0,1,2を正常データとみなす。)正常データが入力されたとき、8などの異常データが入力されたときの挙動を確認していきたいと思います。


では、まずは準備から行います。必要なライブラリのインストールおよびデータの前処理を行います。



import numpy as np
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model
from tensorflow.keras.datasets import mnist

# MNISTデータセットをロード
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# 数字0, 1, 2のみを選択
train_filter = np.where((y_train == 0) | (y_train == 1) | (y_train == 2))

x_train, y_train = x_train[train_filter], y_train[train_filter]

# データの正規化(0〜1の範囲にスケール)
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.

# 28x28の画像を784次元のベクトルに変換
x_train = x_train.reshape((len(x_train), np.prod(x_train.shape[1:])))
x_test = x_test.reshape((len(x_test), np.prod(x_test.shape[1:])))

# オートエンコーダーの次元を定義
input_dim = 784 # 28*28
encoding_dim = 32 # 圧縮後の次元数


次に、エンコーダ部、デコーダ部を定義,、およびモデルの生成をしていきます。「activation」に「relu」や「sigmoid」がありますがこれが活性化関数になります。どのような活性化関数を使用するかは、モデルを作成する際に選ぶ必要があります。



# エンコーダー
input_img = Input(shape=(input_dim,))
encoded = Dense(encoding_dim, activation='relu')(input_img)

# デコーダー
decoded = Dense(input_dim, activation='sigmoid')(encoded)

# オートエンコーダーモデル
autoencoder = Model(input_img, decoded)


最後にモデルのコンパイル、学習を行います。ここでlossやepochsなどもハイパーパラメータであり事前に与える必要があります。



# モデルのコンパイル
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')

# モデルの訓練
autoencoder.fit(x_train, x_train,
epochs=5,
batch_size=256,
shuffle=True,
validation_data=(x_test, x_test))

# エンコーダーモデル
encoder = Model(input_img, encoded)




では、作成したモデルを使用して画像を再構築していきましょう。


 



decoded_imgs = autoencoder.predict(x_test)

# 再構成画像の表示
n = 10 # 表示する数字の数
plt.figure(figsize=(20, 4))
for i in range(n):
# オリジナル画像
ax = plt.subplot(2, n, i + 1)
plt.imshow(x_test[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)

# 再構成された画像
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(decoded_imgs[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()


結果は以下のようになります。




このように正常データとして用いた0,1,2はよく再構築されているのに対して7,や9といった正常データに用いていないデータを再構築すると上手く再構築できていないことが確認できます。


これをさらに数値で確認してみましょう。


 



# 元の画像と再構成された画像の差を計算
error = np.mean(np.abs(x_test[0] - decoded_imgs[0]))
print(f'Sample {0} - True Label: {y_test[0]} - Reconstruction Error: {error}')

error = np.mean(np.abs(x_test[1] - decoded_imgs[1]))
print(f'Sample {1} - True Label: {y_test[1]} - Reconstruction Error: {error}')

error = np.mean(np.abs(x_test[7] - decoded_imgs[7]))
print(f'Sample {7} - True Label: {y_test[7]} - Reconstruction Error: {error}')


再構築誤差を絶対値誤差として表現しました。結果は



結果

結果



です。


このように、2は誤差が小さいのに対して7や9は誤差が大きいことが確認できます。


ここで閾値を設定することで異常か正常化を分けることが出来るのです。


終わりに

今回はautoencoderについて解説を行いました。このモデルは様々な方向に拡張されており応用系の形も存在します。次回のブログでは他のモデルについても解説していきます。

New call-to-action