【AI】学習済みのニューラルネットワーク計算を行列計算のみで実行する方法

こんにちは、ヒガシです。今回は久しぶりにAI関連のお話です。

 

AI構築時において、Kerasなんかのライブラリを使ってAIモデルを学習させたとします。

 

その学習済モデル(変数model)を使って実際に推論をする場合、例えばKerasの場合だと

model.predict(入力データ)

とすれば学習済みモデルを使った推論を実施できます。

 

しかしながら実際の運用時においては、KerasライブラリやPythonの実行環境そのものを準備できない場合など、いつもこのやり方で推論できるとは限りません。

 

というわけでこのページでは、Kerasの内部で行われているニューラルネットワーク計算を、Kerasを使わないで実行する方法をご紹介していきます。

※私は普段Kerasしか使わないのでPytorchのことはよくわかりませんが、おそらくほぼ同じやり方で実行できるはずです。

 

それではさっそくやっていきましょう。

 

スポンサーリンク

ニューラルネットワーク内部の計算について

そもそもニューラルネットワークの内部ではどのような計算が行われているのでしょうか?

その答えは行列の積と和です。

 

詳細については以前記載した以下の記事で解説していますので、興味があればあわせてご覧ください。

【人工知能】ディープラーニング内部の計算をエクセルを使って詳細解説!

 

今回は学習済のニューラルネットワークが保有している重みとバイアスを抜き出し、その抜き出したデータを使って行列計算をゴリゴリやっていくことによって、ニューラルネットワークの計算を再現してみます。

 

それでは実際の作業に入っていきましょう。

 

スポンサーリンク

ニューラルネットワークモデルを構築する

そもそもニューラルネットワークのモデルがなければ話になりません。

というわけでまずはニューラルネットワークモデルを構築するところから始めましょう。

 

使用するデータは以下の通りです。

適当な係数を4つ使って

y=1.2*x1-2.1*x2+0.7*x3^2-0.5*x4^2+乱数

という式を作成しました。

AIモデルを構築する際に使用するサンプルデータ

今回はこのx1,x2,x3,x4を入力とし、yを出力するニューラルネットワークを構成⇒学習してみます。

 

そのサンプルコードは以下の通りです。

#ライブラリインポート
import matplotlib.pyplot as plt 
import numpy as np
import pandas as pd
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam

#データ準備
data_file='sample_data.csv'
data=pd.read_csv(data_file).values
Xdata=data[:,:4]
Ydata=data[:,-1]

#ニューラルネットワーク構成⇒学習まで
model = Sequential()
model.add(Dense(20, input_dim=4))
model.add(Dense(10))
model.add(Dense(1))
model.compile(loss="mean_absolute_error", optimizer=Adam(lr=0.001))
model.summary()
validation_split_rate=0.2
history=model.fit(Xdata,Ydata,batch_size=16,epochs=100,validation_split=validation_split_rate)

 

これで変数modelに学習済のニューラルネットワークが構成されました。

 

本題から少しそれますが、念のため以下のコードで精度を確認しておきましょう。

※ここではmodel.predictを使います。

#inputdataをtraining用とvalidation用に分ける
X_trainig=Xdata[:int(len(Xdata)*(1-validation_split_rate))]
X_validation=Xdata[-int(len(Xdata)*(validation_split_rate)):]
Y_trainig=Ydata[:int(len(Ydata)*(1-validation_split_rate))]
Y_validation=Ydata[-int(len(Ydata)*(validation_split_rate)):]
#学習に使っていないデータで推論
Predictdata = model.predict(X_validation)
plt.scatter(Predictdata,Y_validation, color="b")
plt.xlabel('Predict_data')
plt.ylabel('Row_data')
plt.show()

構築したモデルの精度を確認した結果

しっかりと正解データを予測できていますね。

 

というわけで問題なくAIモデルを構築することができました。

 

以降の項目では、先ほどのコード内で実行したmodel.predictの部分をnumpyの行列計算のみで実行し、model.predictで得られた結果と一致するのかを確認していきます

 

スポンサーリンク

ニューラルネットワーク内部の重みとバイアスを取り出す

先ほども簡単に紹介したとおり、ニューラルネットワーク内部で行われているのは行列の積と和です。

 

この行列の積の部分で使われるのが「重み」、行列の和の部分で使われるのが「バイアス」です。

 

行列計算をするにあたっては、まずはこれら「重み」と「バイアス」を取り出す必要があります。

 

そのやり方は以下の通りです。

※先ほどのコードの続きという立ち位置で書いています。今回構築したモデルは3層のニューラルネットワークなので、3層分の重みとバイアスを取り出しています。

#1層目の係数を取得
layer1 = model.layers[0]
L1_weights=layer1.get_weights()[0]
L1_bias=layer1.get_weights()[1]
#2層目の係数を取得
layer2 = model.layers[1]
L2_weights=layer2.get_weights()[0]
L2_bias=layer2.get_weights()[1]
#3層目の係数を取得
layer3 = model.layers[2]
L3_weights=layer3.get_weights()[0]
L3_bias=layer3.get_weights()[1]

 

以下のコードでそれぞれの変数の大きさを確認してみましょう。

print('1層目の重み、バイアス')
print(L1_weights.shape)
print(L1_bias.shape)
print('2層目の重み、バイアス')
print(L2_weights.shape)
print(L2_bias.shape)
print('3層目の重み、バイアス')
print(L3_weights.shape)
print(L3_bias.shape)

 

以下の結果が出力されました。

取り出した重みとバイアスのサイズを確認した結果

今回構成したニューラルネットワークは先ほどのコードでご紹介したとおり、

入力:4

中間層Dense:20,10

出力:1

ですので、しっかりとそれに対応した形で重み、バイアスが取得されていますね。

 

これで事前の準備は完了です。

 

スポンサーリンク

取り出した重みとバイアスで行列計算してみる

それでは事前の準備が非常に長くなってしまいましたが、本題であるニューラルネットワーク内部の計算を行列の積&和のみで表現してみましょう。

※ここも先ほどまでのコードの続きで書いていきます。

 

そのコードは以下の通りです。

今回はnumpyのdotを使って行列の積を計算しています。

#計算結果を入れる箱を準備
Pred_mat=[]
#実際の行列計算
for i in range(len(X_validation)):
    input_data=X_validation[i,:].reshape(1,4)
    L1P=np.dot(input_data,L1_weights)+L1_bias
    L2P=np.dot(L1P,L2_weights)+L2_bias
    L3P=np.dot(L2P,L3_weights)+L3_bias
    Pred_mat.append(L3P)

 

順番にコード追いかけていただければ、各層の重みと入力との積を計算し、バイアスと和をとっていることが確認できると思います

最終的な出力結果はPred_matというリストに格納されています。

 

ニューラルネットワークの計算って複雑そうですが、意外とこんなもんなんですよね。

 

※今回は活性化関数をまったく使っていない単純なモデルでの計算結果です。もし活性化関数が入っているモデルを再現しようと思ったらその部分を追記する必要がありますのでご注意ください。

なお、活性化関数付きモデルで同じことをやる方法は以下で解説していますので、興味があればこちらもご覧ください。

【AI】活性化関数付MLPモデルの計算を行列計算で再現する方法!

 

スポンサーリンク

重みとバイアスで行列計算した結果の確認

それでは重みとバイアスでの計算は終わりましたので、もともとmodel.predictで計算した推論結果と比較してみましょう。

 

そのコードは以下の通りです。

※変数Predictdataには先ほどの項目でmodel.predictにて推論を行った結果が格納されています。

#リストデータを配列に変換
Pred_mat=np.array(Pred_mat)
plt.scatter(Predictdata,Pred_mat, color="b")
plt.xlabel('Keras_Predict')
plt.ylabel('numpy_Predict')
plt.show()

 

実行結果は以下の通りです。

横軸がmodel.predictで推論を行った結果、縦軸が取り出した重みとバイアスを使って行列計算して算出した結果です。

ご覧の通り完璧に一致していますね。

行列計算の結果とmodel.predictで計算した結果の比較

というわけで学習済モデルから取り出した重みとバイアスを使った行列の積&和によって、moderl.predictで算出した結果を再現できることが確認できました。

 

スポンサーリンク

実運用シーンにおいてこの計算を実施する方法

ここまでの計算をおさらいしてみましょう。

①AIモデルの構築⇒学習

②①のモデルから重み、バイアスを取得

③②で取得した情報を使って行列計算し、ニューラルネットワークの計算を再現

上記の流れをkerasがインポートされたpython環境上で実施してきました。

 

もともと今回の手法を解説するモチベーションは、kerasなんかのライブラリや、pythonそのものが使えない環境においてニューラルネットワークの計算を再現したい人を救いたい、というものでした。

 

ですのでこの項目ではkerasやpython自体を準備できない環境でこの計算を実施する方法をご紹介します。

◾️運用先でkerasは準備できないけどpython自体は準備できている場合

この場合、まずはkerasがインポートできている環境下にて先程紹介した重み&バイアスの取得を行います。

取得した重み、バイアスはただの配列データですので、そのまま配列データとして保存することができます。

そしてその保存した配列データを実際のAI運用環境下から読める場所に置き、その環境で配列をロードし直せば先程紹介したような行列計算を実施することができます。

 

配列のロード、セーブについては以下の記事でご紹介していますので参考にしてみてください。

【python】numpy形式のまま配列を保存&既存配列をロードする方法!

 

◾️運用先でpython自体を準備できない場合

この場合も、まずはkerasがインポートできている環境下にて先程紹介した重み&バイアスの取得を行います。

取得した重み、バイアスのデータはcsvファイルなんかに書き出しておきましょう。

実際のAI運用環境から読める場所にそのcsvファイルを置き、その環境からcsvを読み込んでしまえば、後は先程紹介したように行列計算を実施するだけです。

 

スポンサーリンク

おわりに

というわけで今回は、学習済のニューラルネットワークモデルから重みとバイアスを取り出し、その取り出した係数と入力データで行列計算することによって、ニューラルネットワーク内部の計算を再現する方法をご紹介しました。

 

このやり方さえ理解していれば、エクセルだろうが何だろうが、最悪手計算でもニューラルネットワークの計算を実施することができますよね。

 

実務においては常にPythonを使えるとは限りませんので、このようなやり方もぜひ覚えておきましょう。

 

このように、私のブログでは様々なスキルを紹介しています。

過去記事一覧

 

今は仕事中で時間がないかもしれませんが、ぜひ通勤時間中などに他の記事も読んでいただけると嬉しいです。
⇒興味をもった方は【ヒガサラ】で検索してみてください。

確実にスキルアップできるはずです。

 

最後に、この記事が役に立ったという方は、ぜひ応援よろしくお願いします。
↓ 応援ボタン
にほんブログ村 IT技術ブログへ
にほんブログ村

それではまた!

コメント

タイトルとURLをコピーしました