#背景
全結合層でオートエンコーダし、画像の異常検知を行う。
loss関数にSSIMを使用すれば構造的類似性の観点からより精度の良いオートエンコーダ結果が得られることから、MNISTデータを用いて検証する。
また、異常度の出し方を画像の輝度差分ではなくSSIMで出すことでどういう値になるかを確認する。
本ページはコードの記録のみとする。検証結果は別紙参照のこと。
#やったこと
MNIST数字データで1のみを学習データに使用して全結合層オートエンコーダモデルを構築。1と9の混じったテストデータで9を異常と検知するか確認する。
異常度を算出し、ヒートマップで異常位置を可視化する。
#必要ライブラリ
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import tensorflow as tf
from tensorflow.keras.datasets import mnist
import tensorflow.keras
#MNIST読み込み
(x_train, y_train), (x_test, y_test) = mnist.load_data()
#学習データを1のみにする
x1=[]
for i in range(len(x_train)):
if y_train[i] == 1:
x1.append(x_train[i])
x_train = np.array(x1)
#テストデータを1と9にする
x2, y = [], []
for i in range(len(x_test)):
if y_test[i] == 1 or y_test[i] == 9:
x2.append(x_test[i])
y.append(y_test[i])
x_test = np.array(x2)
y = np.array(y)
#形状確認
print("x_train.shape;", x_train.shape)#学習データ1
print("x_test.shape;", x_test.shape)#テストデータ1 or 9
print("y.shape:", y.shape)#テストデータのラベル
#スプリットし検証用データ生成
from sklearn.model_selection import train_test_split
train_data, val_data = train_test_split(x_train, test_size=0.2, random_state=0)
print("train_data.shape;", train_data.shape)#学習データ1
print("val_data.shape;", val_data.shape)#検証データ1
print("x_test.shape;", x_test.shape)#テストデータ1 or 9
#SSIMは4次元で受け付けるので4次元へ変更
train_data = np.expand_dims(train_data, 3)
val_data = np.expand_dims(val_data, 3)
x_test = np.expand_dims(x_test, 3)
print("train_data.shape;", train_data.shape)#学習データ1
print("val_data.shape;", val_data.shape)#検証データ1
print("x_test.shape;", x_test.shape)#テストデータ1 or 9
#float32へ変更
train_data = train_data.astype("float32")
val_data = val_data.astype("float32")
x_test = x_test.astype("float32")
#SSIM関数定義
def ssim_loss(y_true, y_pred):
return 1-tf.reduce_mean(tf.image.ssim(y_true, y_pred,
max_val = 1.0,filter_size=11,
filter_sigma=1.5, k1=0.01, k2=0.03 ))
#学習モデル
from tensorflow.keras.layers import Input, Dense, Flatten, Reshape
from tensorflow.keras.models import Model
INPUT_SHAPE = (train_data[0].shape[0], train_data[0].shape[1], 1)
FLATTENED_SHAPE = INPUT_SHAPE[0]*INPUT_SHAPE[1]
input_img = Input(shape=(INPUT_SHAPE))
encoded = Flatten()(input_img)
encoded = Dense(256, activation="relu")(encoded)
encoded = Dense(64, activation="relu")(encoded)
decoded = Dense(256, activation="relu")(encoded)
decoded = Dense(FLATTENED_SHAPE, activation="relu")(decoded)
decoded = Reshape(INPUT_SHAPE)(decoded)
autoencoder = Model(inputs = input_img, outputs=decoded)
#コンパイル
autoencoder.compile(optimizer="adam", loss=ssim_loss)
autoencoder.summary()
#学習
fit_record = autoencoder.fit(train_data, train_data,
epochs=50,
batch_size=64,
shuffle=True,
validation_data=(val_data, val_data))
#可視化グラフ
def plot_loss_accuracy_graph(fit_record):
plt.plot(fit_record.history["loss"], "-D", color="blue", label="train_loss", linewidth=2)
plt.plot(fit_record.history["val_loss"], "-D", color="black", label="val_loss", linewidth=2)
plt.title("LOSS")
plt.xlabel("Epochs")
plt.ylabel("Loss")
plt.legend(loc="upper right")
plt.show()
#グラフ化
plot_loss_accuracy_graph(fit_record)
#予測
pred = autoencoder.predict(x_test)
#可視化(異常度は画像の輝度差分)
scores = []
n=10
plt.figure(figsize=(18,9))
for i in range(n):
#テスト画像を表示
ax = plt.subplot(3, 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(3, n, i+1+n)
plt.imshow(pred[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
#入出力の差分を計算
diff_img = x_test[i].reshape(28, 28) - pred[i].reshape(28, 28)
#入出力の差分数値(異常度)を計算
diff = np.sum(np.abs(x_test[i].reshape(28, 28) - pred[i].reshape(28, 28)))
scores.append(diff)
#差分画像と差分数値(異常度)を表示
ax = plt.subplot(3, n, i+1+n*2)
plt.imshow(diff_img, cmap="jet")
#plt.gray()
ax.get_xaxis().set_visible(True)
ax.get_yaxis().set_visible(True)
ax.set_xlabel("score= "+str(diff))
plt.savefig("result.png")
plt.show()
plt.close()
#可視化(異常度はssimから算出)_ 画像にするため2次元にreshape必要
n = 6
plt.figure(figsize=(20, 14), dpi=100)
plt.subplots_adjust( wspace=0.1, hspace=0.07)
plt_a=1
for i in range(n):
# 検証データ vs 検証データ
ax = plt.subplot(3, n, plt_a)
plt.imshow(x_test[i].reshape(28,28))
ax.get_xaxis().set_visible(True)
ax.get_yaxis().set_visible(False)
value_a = ssim_loss(x_test[i], x_test[i]) #同一のものの類似度
ax.set_title("Original Image")
label = 'SSIM Loss value: {:.3f}'
ax.set_xlabel(label.format(value_a) )
# 再構築データ(予測) vs 検証データ
ax = plt.subplot(3, n, plt_a + n )
plt.imshow(pred[i].reshape(28,28))
ax.get_xaxis().set_visible(True)
ax.get_yaxis().set_visible(False)
value_a = ssim_loss(pred[i], x_test[i])
ax.set_title("Reconstructed Image")
label = 'SSIM Loss value: {:.2}'
ax.set_xlabel(label.format(value_a) )
plt_a+=1
plt.show()
学習結果はlossとval lossで同じ傾向であり、良好。
異常個所が明確に色付けされたヒートマップが得られ、異常度も1と9でしっかり層別できた。
今後は製造物に対してssimを検討する予定。