こんにちは、ヒガシです。
このページではオートエンコーダーを使ってMNISTの画像を生成するということをやってみようと思います。
それではさっそくやっていきましょう!
オートエンコーダー(Auto Encoder)の概略
まずはオートエンコーダーというものがどんなものなのかを説明します。
オートエンコーダーは次元を圧縮していくエンコーダー部分と、圧縮した次元を増加させていくデコーダー部分で構成されます。
そしてエンコーダーとデコーダーのちょうど境目のところに2次元(別に3次元でも、4次元でも良いですが)の中間層が設けれています。
そしてこのニューラルネットワークを、入力と出力がまったく同じデータを使って学習させます。
そうすることによってこの2次元の情報にはデータを説明する情報が凝縮されます。
例えばエンコーダー部分だけを取り出し既知のデータをたくさん入力すれば、入力データがどんなグループに分類できるのかを2次元mapで可視化分析できたり、デコーダー部分だけを取り出せば2次元の情報を適当に与えることで疑似データを大量に生成することが可能です。
今回は後者であるデコーダー部分を使った疑似データの作成をトライしてみます。
MNIST画像を使ったオードエンコーダーの学習
ということでまずはオートエンコーダーのモデルを作成していきます。
今回はサンプルデータとして手書き数字データであるMNISTの画像データを使用します。
画像は(28,28)ピクセルで構成されますので、この画像を1次元の784次元にし、オートエンコーダーの入力および出力データとして使用していきます。
ということで以下がオートエンコーダーモデルの生成プログラムです。
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation ,Conv2D, MaxPooling2D,Dropout, Flatten,BatchNormalization
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.datasets import mnist
import numpy as np
import matplotlib.pyplot as plt
import random
# MNISTデータを読込む
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# MNISTデータを加工する
inout_dim = 784
x_train = x_train.reshape(60000, 784)
x_test = x_test.reshape(10000, 784)
x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
#オートエンコーダーモデルの定義
model = Sequential()
model.add(Dense(128, input_dim = inout_dim))
model.add(Activation('relu'))
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dense(2))
model.add(Activation('tanh'))
model.add(Dense(64))
model.add(Activation('relu'))
model.add(Dense(128))
model.add(Activation('relu'))
model.add(Dense(inout_dim))
model.compile(loss="mean_squared_error", optimizer=Adam(lr=0.001))
model.summary()
history=model.fit(x_train, x_train, batch_size=32, epochs=50, validation_data = (x_test , x_test))
#学習結果の可視化
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('loss')
plt.xlabel('Epoch')
plt.grid()
plt.legend(['train','test'],loc='upper left')
plt.show()
以下が学習結果です。
問題なくLossが減っていますね。
ということでモデルの学習は完了です。
学習済みモデルからデコーダー部分を取り出す
次は先ほど学習させたデータからデコーダー部分を取り出します。
重み、バイアスの取り出し方は以下で詳細説明していますので参考まで。
【AI】ニューラルネットワークから重み、バイアスを取得する方法!
以下が今回のモデルから重み、バイアスを取り出すコードです。
#デコーダー部分の重み、バイアスを取得する
Dec_W1 = model.layers[6].get_weights()[0]
Dec_b1 = model.layers[6].get_weights()[1]
Dec_W2 = model.layers[8].get_weights()[0]
Dec_b2 = model.layers[8].get_weights()[1]
Dec_W3 = model.layers[10].get_weights()[0]
Dec_b3 = model.layers[10].get_weights()[1]
#デコーダー部分の計算を実施⇒画像配列にする関数
def calc_decoder(input_data):
L1P = np.dot(input_data,Dec_W1)+Dec_b1
L1P = np.where(L1P<0,0,L1P)
L2P = np.dot(L1P,Dec_W2)+Dec_b2
L2P = np.where(L2P<0,0,L2P)
L3P = np.dot(L2P,Dec_W3)+Dec_b3
L3P = L3P.reshape((28,28)) * 255
return L3P
これでcalc_decoder関数に適当な2次元配列をぶち込めば画像配列データを作成することができます。
オートエンコーダーを使った画像生成トライアル
それでは先ほどまでに作成したモデルを使って実際に画像生成をトライしてみます。
以下がそのサンプルコードです。
#適当な乱数を設定してデコーダーで画像を生成する
for i in range(10):
input_data = np.array([random.random()*2-1, random.random()*2-1])
L3P = calc_decoder(input_data)
plt.imshow(L3P, cmap="gray")
plt.show()
やっていることは適当な-1~1までの乱数を生成し、先ほど作成したcalc_decoder関数に放り込んでいるだけです。
※全体モデルを作成する際に2次元の中間層のあと活性化関数としてtanhを使っていますのでデコーダーモデルは-1~1の範囲のデータを受け取ることが前提になっています。
以下が出てきた生成画像のサンプルです。
いい感じに生成できてますね。
ちょっと汚いのもあるけどギリギリわかる範囲。
おわりに
ということで今回はオートエンコーダーとMNISTのサンプルデータを使って画像を生成するということをトライアルしてみました。
生成系AIが話題になっている中、すこしはどんなことをやっているかがイメージできたのではないかと思います。
このブログでは、このようなAIスキルを多数紹介しています。
ぜひ他のページもご覧ください。
それではまた!
コメント