こんにちは、ヒガシです。
先日業務中にKerasで学習させたモデルをnumpyの行列計算で表した際に、ほぼ値は一致するんですが、微妙に結果が異なる事象に遭遇しました。
いろいろ調べてみると、GPUで計算する際の計算順序が毎度変わるらしく、その結果出力も微妙に変化してしまうとのこと。
詳しいことは不明!
ちなみにこのあたりの問題はPytorchでは解消されているが、Tensorflow系は対応できてないとのことです。
ということで実際のところどのくらい結果が変わるのが実際のKerasモデルを使って検証してみましょう。
それではさっそくやっていきます。
KerasでNNモデルを構築する
まずはモデルがないと始まらないので以前構築したMNIST画像を用いたオートエンコーダーモデルを再利用したいと思います。
以下がその構築コードです。
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 = 5, validation_data = (x_test , x_test))
今回はKerasの誤差を確認することが目的なので学習は適用で良いでしょう。
学習済みモデルからnumpy計算用に重み、バイアスを取り出す
次に学習したモデルから重みとバイアスを取得します。
以下がそのコードです。
#エンコーダー部分の重み、バイアスを取得する
Enc_W1 = model.layers[0].get_weights()[0]
Enc_b1 = model.layers[0].get_weights()[1]
Enc_W2 = model.layers[2].get_weights()[0]
Enc_b2 = model.layers[2].get_weights()[1]
Enc_W3 = model.layers[4].get_weights()[0]
Enc_b3 = model.layers[4].get_weights()[1]
#デコーダー部分の重み、バイアスを取得する
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]
あとはこれらを行列計算していくだけですね。
AIモデルの行列計算をnumpyで記述する
次に先ほど取得した重み、バイアスを使ってAIの内部計算をnumpyの行列計算で再現していきます。
以下がそのコードです。
#エンコーダー部分の計算
def calc_encoder(input_data):
L1P = np.dot(input_data,Enc_W1)+Enc_b1
L1P = np.where(L1P<0,0,L1P)
L2P = np.dot(L1P,Enc_W2)+Enc_b2
L2P = np.where(L2P<0,0,L2P)
L3P = np.dot(L2P,Enc_W3)+Enc_b3
L3P = np.tanh(L3P)
return L3P
#デコーダー部分の計算
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
return L3P
別にエンコーダーとデコーダーに分ける意味もないのですが、一応分けておきました。
Kerasの計算とnumpy計算での誤差を確認する
今回のモデルの出力は784次元のデータなのですが、一つ一つ見ていくのも面倒なので、まずは最初の5次元だけを比較してみましょう。
以下がそのサンプルコードです。
#サンプル入力データ
input_data = x_test[0]
#numpyで計算実行⇒結果出力
enc_out = calc_encoder(input_data)
dec_out = calc_decoder(enc_out)
print('numpy', dec_out[:5])
#kerasで計算実行⇒結果出力
keras_out = model.predict(np.array([input_data]))[0]
print('keras', keras_out[:5])
だいたいあってますが、微妙に最後の方の桁が違いますね。
次はnumpy結果÷keras結果で最大、最小、平均誤差がどのくらいか確認してみます。
rate = (dec_out / keras_out -1) * 100
print('numpy / keras(%): max, min, average')
print(max(rate),min(rate),np.average(rate))
最大で0.084& 最小で0.033%、平均だともはやほぼゼロ。
ということで誤差はあるものの、そこまで気にするほどのものではなさそうですね。
おわりに
ということで今回はKerasのPredictとnumpyの行列計算でどのくらい結果に差が出るのかを検証してみました。
あくまでも今回のモデルだとこうなったという話なので、あなたのモデルで確認することをオススメします。
面倒ならPytorchを使いましょう。
このブログでは、このようなAIスキルを多数紹介しています。
ぜひ他のページもご覧ください。
それではまた!
コメント