Keras で AutoEncoder 実行するまでのメモ
python を触り始めて半年、色々学んだ事を記録しておく。
環境はGoogle Collaborators 使用。
実装してみる
データ概要
100点のから生成した を作る。
は平均0,標準偏差3の正規分布とする。
試しに5つ描いてみる。
import numpy as np from matplotlib import pyplot as plt import random fig = plt.figure() ax1 = fig.add_subplot(1, 1, 1) n = 100 x = np.linspace(0, 2*np.pi, n) for i in range(5): a = np.random.normal(0, 3) y = a * np.sin(x) ax1.plot(y)
こんな感じのデータ
学習データ、テストデータ作成
学習データ : 平均0,標準偏差3の正規分布 * sin(x) を200データ
テストデータ: -20~20 の一様分布 * sin(x)を200データ
上記データを作成
train_list = [] test_list = [] for i in range(200): a = np.random.normal(0, 3) b = random.uniform(-20, 20) y_train = a * np.sin(x) y_test = b * np.sin(x) train_list.append(y_train) test_list.append(y_test) x_train = np.vstack(train_list) x_test = np.vstack(test_list) print("x_train.shape=", x_train.shape) print("x_test.shape=", x_test.shape)
x_train.shape= (200, 100)
x_test.shape= (200, 100)
for文の中で、list を append、最後に結合というのよくやる。
正規化
学習データ、テストデータ共に、最大値、最小値が-1~1の範囲を超えている。
この後、非線形関数の活性化関数を使って学習するが、活性化関数では最大、最小で表現できる値が決まっているので、正規化するのが一般的。
今回はtanhを使うので、-1〜1 の範囲に値が収まるようにMinMaxScalerで正規化する。
活性化関数の例:
ReLU:最小値=0, 最大値は幾つでも。
tanh:最小値=-1, 最大値=1
from sklearn.preprocessing import MinMaxScaler fig = plt.figure(figsize=(10, 10)) ax1 = fig.add_subplot(2, 1, 1) ax2 = fig.add_subplot(2, 1, 2) for i in range(len(x_train)): ax1.plot(x_train[i], color="blue") ax1.plot(x_test[i], color="red") scaler = MinMaxScaler(feature_range=(-1, 1)) all_val = np.hstack((x_train, x_test)).reshape(-1, 1) scaler.fit(all_val) x_train = scaler.transform(x_train) x_test = scaler.transform(x_test) for i in range(len(x_train)): ax2.plot(x_train[i], color="blue") ax2.plot(x_test[i], color="red")
学習データ、テストデータ合わせた全部で、-1~1にしたいので、all_valで結合し、fitの型に整形している。
今回、テストデータがある想定なので全部対象にできたが、テストデータが手元にない場合は、正規化の方法をもっと工夫する必要がありそう。
青が学習データ、赤がテストデータ。
上が正規化する前、下が正規化した後。
値の縮尺のみが変わっている。
AutoEncoderで学習
AutoEncoderのモデルを定義。
100 -> 50 -> 100 で次元を50にした後復元している。
損失関数はMSE(最小二乗誤差)。
EarlyStoppingを指定する事で、学習を良い所で止めている。
from keras.layers import Input, Dense from keras.models import Model from keras.callbacks import EarlyStopping input_sin = Input(name="input_sin", shape=(100,)) encoder = Dense(50, name="encoder1", activation='tanh')(input_sin) decoder = Dense(100, name="decoder1", activation='tanh')(encoder) autoenc = Model(inputs=input_sin, outputs=decoder) autoenc.summary() autoenc.compile(loss='mean_squared_error', optimizer='adam') early_stop = EarlyStopping(monitor='val_loss', patience=5, verbose=1, mode='auto') history = autoenc.fit(x_train, x_train, epochs=1000, batch_size=100, shuffle=True, validation_split=0.1, verbose=0, callbacks=[early_stop] ) loss = history.history['loss'] val_loss = history.history['val_loss'] nb_epoch = len(loss) fig = plt.figure() ax1 = fig.add_subplot(1, 1, 1) ax1.plot(range(nb_epoch), loss, label='loss') ax1.plot(range(nb_epoch), val_loss, label='val_loss') ax1.set_xlabel('epoch') ax1.set_ylabel('loss') ax1.legend()
________________________________________________________
Layer (type) Output Shape Param #
=======================================
input_sin (InputLayer) (None, 100) 0
________________________________________________________
encoder1 (Dense) (None, 50) 5050
________________________________________________________
decoder1 (Dense) (None, 100) 5100
=======================================
Total params: 10,150
Trainable params: 10,150
Non-trainable params: 0
________________________________________________________
Epoch 00079: early stopping
79 epoch で学習終了している。
以前ハマったのが、
- Dense の nameは、表示だけのもので、引数指定には使えない。
- Input の shape には、データのセット数は入らない。
x_train.shape= (200, 100)なので、100個のsin(x)データが200セット。
- trainとtestは、データのセット数以外は同じshapeでないとエラー。
上記モデルは、trainだけでなく、test時も同じように使うので。
テストデータで評価
テストデータを入れ、結果を見てみる。
評価としては、損失関数と同じ、MSEの値で、INPUTしたデータがどれだけOUTPUTしたデータと差があるかを確認する。
pre = autoenc.predict(x_test) pre = scaler.inverse_transform(pre) x_test = scaler.inverse_transform(x_test)
基本
- model定義 -> fit()で学習 -> 学習曲線で収束しているか確認 -> predict()でテストデータを入れる
の流れは、AutoEncoderだけでなく、他DNNのモデルや scikit-learnの機械学習なども同様。
わかりやすいように、inverse_transformで正規化を元に戻してから
以下、predictした結果をグラフ化して確認。
from sklearn.metrics import mean_squared_error fig = plt.figure(figsize=(10, 20)) ax1 = fig.add_subplot(4, 1, 1) ax2 = fig.add_subplot(4, 1, 2) ax3 = fig.add_subplot(4, 1, 3) ax4 = fig.add_subplot(4, 1, 4) mse_red = [] mse_blue = [] for i in range(len(pre)): mse_val = mean_squared_error(x_test[i], pre[i]) if np.max(x_test[i]) > 9: ax1.plot(x_test[i], color="red") ax2.plot(pre[i], color="red") ax3.scatter(i, mse_val, color="red") mse_red.append(mse_val) else: ax1.plot(x_test[i], color="blue") ax2.plot(pre[i], color="blue") ax3.scatter(i, mse_val, color="blue") mse_blue.append(mse_val) ax1.set_xlabel('x') ax1.set_ylabel('sin(x)') ax2.set_xlabel('x') ax2.set_ylabel('after autoenc sin(x)') ax3.set_xlabel('file') ax3.set_ylabel('mse value') # ax3.set_ylim(0, 0.5) ax4.hist([mse_blue, mse_red], bins=50, color=["blue", "red"], stacked=True) ax4.set_xlabel('mse value') ax4.set_ylabel('file count')
標準偏差3の範囲のx_trainを学習させたので、にあたる±9の範囲を青で、それ以外の範囲を赤で示している。
x_testは一様分布なのだが、3つ目、4つ目のグラフをみる通り、MSEの値は青の範囲はほとんど0となり、期待通り。
sin波が、学習したデータから外れるほどMSEの値が大きくなっている。