PyLearnMLCR 06: モデルのセーブとロード

これまで、色々な手法によるモデル構築、良いモデルを選択する方法について学んできました。さて、それを実システムに使用するためには、作成したモデルを保存しなければいけません。ここでは、そのモデルの保存(セーブ)と読み込み(ロード)の方法について学びます。

モデルの構築

以下は、今までとまったく同じダミーデータを用意して、ニューラルネットを1つ構築するコードとなります。 今までと同じなので、X1, X2からYを推定するモデルとなります。

  • X1: 勉強時間
  • X2: 理解傾向
  • Y: 試験得点ラベル(Bad, Good, Middle)
In [17]:
# *** ダミーデータの用意 ***
import numpy as np
np.random.seed(1) # 擬似乱数シード: 毎回同じ乱数を出す

# Class1: 成績上位群
mu = [7, 7]
sigma = [[0.5, 0.05], [0.05, 0.5]]
Dat01 = np.random.multivariate_normal(mu, sigma, 100)
Label01=[]
for i in range(0, len(Dat01)):
    Label01.append("Good")

# Class 2: 成績中位群
mu = [5.5, 4]
sigma = [[0.8, -0.5], [-0.5, 0.8]]
Dat02 = np.random.multivariate_normal(mu, sigma, 100)
Label02=[]
for i in range(0, len(Dat02)):
    Label02.append("Middle")

# Class 3: 成績下位群
mu = [2, 5]
sigma = [[0.3, 0.0], [0.0, 3]]
Dat03 = np.random.multivariate_normal(mu, sigma, 100)
Label03=[]
for i in range(0, len(Dat01)):
    Label03.append("Bad")
    
# *** 教師データセットの作成 ***
X=np.concatenate([Dat01, Dat02, Dat03]) # 問題
Y=np.concatenate([Label01, Label02, Label03]) # 正解ラベル

# *** ニューラルネットの定義 ***
from sklearn.neural_network import MLPClassifier
NN_model = MLPClassifier(hidden_layer_sizes=(10, 10, ), solver="adam", activation="logistic", learning_rate_init=0.1, 
                         random_state=1, max_iter=20, alpha=0.01, tol = pow(10, -5), verbose=True)

# *** ニューラルネットの学習 ***
NN_model.fit(X, Y)
Iteration 1, loss = 1.18240101
Iteration 2, loss = 1.10248268
Iteration 3, loss = 1.15580689
Iteration 4, loss = 1.07420803
Iteration 5, loss = 0.98933469
Iteration 6, loss = 0.95641855
Iteration 7, loss = 0.83841133
Iteration 8, loss = 0.72845417
Iteration 9, loss = 0.62671981
Iteration 10, loss = 0.54522681
Iteration 11, loss = 0.49304744
Iteration 12, loss = 0.43331103
Iteration 13, loss = 0.39100287
Iteration 14, loss = 0.33986862
Iteration 15, loss = 0.30508362
Iteration 16, loss = 0.28566503
Iteration 17, loss = 0.24985790
Iteration 18, loss = 0.24350305
Iteration 19, loss = 0.22906806
Iteration 20, loss = 0.21419798
/usr/local/lib/python3.7/site-packages/sklearn/neural_network/multilayer_perceptron.py:562: ConvergenceWarning: Stochastic Optimizer: Maximum iterations (20) reached and the optimization hasn't converged yet.
  % self.max_iter, ConvergenceWarning)
Out[17]:
MLPClassifier(activation='logistic', alpha=0.01, batch_size='auto',
       beta_1=0.9, beta_2=0.999, early_stopping=False, epsilon=1e-08,
       hidden_layer_sizes=(10, 10), learning_rate='constant',
       learning_rate_init=0.1, max_iter=20, momentum=0.9,
       n_iter_no_change=10, nesterovs_momentum=True, power_t=0.5,
       random_state=1, shuffle=True, solver='adam', tol=1e-05,
       validation_fraction=0.1, verbose=True, warm_start=False)

hidden_layer_sizes=(5, 4, )ですので、

  • 1層目: 入力層2次元(勉強時間、理解傾向)
  • 2層目: 隠れ層5次元
  • 3層目: 隠れ層4次元
  • 4層目: 出力層3次元(Bad, Good, Middleラベル分3つ)

の4層階層型のニューラルネットです。学習係数は0.1と比較的大きめ、活性化関数はlogisticなのでシグモイド関数です。この設定で、20回だけニューラルを学習させました。「the optimization hasn't converged yet.」という警告が出ているので、まだ精度が収束していないことがわかります。とはいえここでは気にせず、先に進みます。このモデルは、1時間勉強して、理解傾向が5のとき、何点と予測してくれるか調べてみます。

In [18]:
StudyTime = 1
Understand = 5
Res = NN_model.predict([[StudyTime, Understand]])
print(Res)
['Bad']

1時間勉強し、理解傾向が5の場合は、成績は悪いよと予測してくれています。てきとうに作ったモデルですが、そんなに悪くはなさそうですね。

モデルのセーブ

このように学習が終了しましたが、これではpythonを閉じた時点で(メモリを解放した時点で)学習済みモデルが消失してしまいます。これでは実際の現場では使用できません。モデルを保存することが必要です。モデルの保存は、以下のように書けばokです。

In [19]:
import pickle
filename = 'NN_model.sav'
pickle.dump(NN_model, open(filename, 'wb'))

プログラムがあるディレクトリを確認して、NN_model.savファイルがあることを確認してください。それが学習済みモデルとなります。なお、これだといつ保存したモデルなのかわからなくなってしまいます。長く研究を行なっているとき、モデルを改良したりすることがあります。こう言ったときは、ファイル名への時間付与も検討してください。

In [37]:
# ファイル名に時間を付与
from datetime import datetime
filename = 'NN_model_'
filename = filename + datetime.now().strftime('%Y%m%d_%H%M%S') + '.sav'
print(filename)

pickle.dump(NN_model, open(filename, 'wb'))
NN_model_20190211_183605.sav

datetime.now().strftime('%Y%m%d_%H%M%S')は、現在の年、月、日、時間、分、秒を文字列として取得するコードです(時刻取得について詳しく知りたい人は自分で調べてみてください)。ディレクトリを見ると、保存されたファイルがあるはずです。これで時刻付きモデルを保存することができました。これによって、いつ作ったモデルなのか、知ることができます。ノートなどにどういう条件で学習させたかなど、書いておくといいでしょう(化学などの実験みたいですね!)。

モデルのロード

次に、保存したモデルをロードしてみます。

In [41]:
filename = 'NN_model.sav' # ファイル名を指定
Load_model = pickle.load(open(filename, 'rb'))

これでモデルのロードが完了しました。ロードしたモデルで予測結果を得てみます。

In [45]:
StudyTime = 1
Understand = 5

# ロードしたモデルの推定結果
Res_load = Load_model.predict([[StudyTime, Understand]])

# 現在のメモリに格納されているモデルの推定結果
Res_mem = NN_model.predict([[StudyTime, Understand]])

print('ロードモデルの推定結果:', Res_load)
print('メモリにあるモデルの推定結果:', Res_mem)
print('等価判定: ', Res_load == Res_mem)
ロードモデルの推定結果: ['Bad']
メモリにあるモデルの推定結果: ['Bad']
等価判定:  [ True]

ロードしたモデルの予測結果と、現在のメモリに入っているモデルの予測結果が一致したことを確認できました。そのままの状態で保存されていることがわかります。

ところで、複数のモデルを構築したとして、一番最後に保存したモデルを使いたい、ということもあるでしょう。これを実現するには、以下のように記述します。

In [66]:
# .sav拡張子のファイルを取得
import glob as gl
NameList = gl.glob('*.sav')

# 降順ソート
NameList.sort(reverse=True)
print(NameList)

# 最新のファイル名を取得
filename = NameList[0]
print(filename)

# 最新のモデルをロード
Load_model = pickle.load(open(filename, 'rb'))
['NN_model_20190211_183605.sav', 'NN_model_20190211_183603.sav', 'NN_model.sav']
NN_model_20190211_183605.sav

ロードしたモデルのパラメータ

ロードしたモデルの詳細なパラメータなども、printで見ることで確認することが可能です。ニューラルネットは階層的なベクトルの線形写像(を活性化関数に通したもの)によって実現されます。ウェイトパラメータは、入力されたベクトルの次元変換を行う行列、バイアスパラメータは、ウェイトにより次元変換されたベクトルのバイアス成分となります。興味がある人は勉強してみましょう。

In [74]:
print(Load_model)
print('ハイパーパラメータ:', Load_model.get_params())
print('バイアスパラメータ: ', Load_model.intercepts_)
print('ウェイトパラメータ: ', Load_model.coefs_)
MLPClassifier(activation='logistic', alpha=0.01, batch_size='auto',
       beta_1=0.9, beta_2=0.999, early_stopping=False, epsilon=1e-08,
       hidden_layer_sizes=(10, 10), learning_rate='constant',
       learning_rate_init=0.1, max_iter=20, momentum=0.9,
       n_iter_no_change=10, nesterovs_momentum=True, power_t=0.5,
       random_state=1, shuffle=True, solver='adam', tol=1e-05,
       validation_fraction=0.1, verbose=True, warm_start=False)
ハイパーパラメータ: {'activation': 'logistic', 'alpha': 0.01, 'batch_size': 'auto', 'beta_1': 0.9, 'beta_2': 0.999, 'early_stopping': False, 'epsilon': 1e-08, 'hidden_layer_sizes': (10, 10), 'learning_rate': 'constant', 'learning_rate_init': 0.1, 'max_iter': 20, 'momentum': 0.9, 'n_iter_no_change': 10, 'nesterovs_momentum': True, 'power_t': 0.5, 'random_state': 1, 'shuffle': True, 'solver': 'adam', 'tol': 1e-05, 'validation_fraction': 0.1, 'verbose': True, 'warm_start': False}
バイアスパラメータ:  [array([-0.78134736,  0.82635589,  1.79774976, -2.26039895,  2.18870723,
        1.97102966,  1.41469722, -0.70501575, -0.45098175,  2.63456618]), array([-0.60209531, -0.27505733,  0.23615709, -0.88322368, -0.28922582,
       -0.3303324 , -0.22402179, -0.72900194, -0.6493766 ,  0.31246941]), array([-0.34494401,  0.52289148,  0.22312861])]
ウェイトパラメータ:  [array([[ 1.5263123 , -1.24136913, -1.35502931, -0.54782298, -0.85498734,
        -1.50885382, -1.22550758, -0.26309125,  0.66249569,  0.53014738],
       [-0.11930032,  0.22892768,  0.19532551,  1.20078179,  0.33867655,
         0.25135495,  0.20314173, -0.51876547, -1.64318251, -1.22338399]]), array([[-1.92270812,  0.30575091,  0.71874537,  0.80510183, -0.79458081,
        -0.12888184, -1.12926159,  0.87640983, -1.75341675,  0.73729543],
       [ 0.62382122, -0.69404667, -0.92130833, -0.66452661, -0.04222676,
         0.21301615,  0.4120336 , -1.51376499, -0.22771213, -1.47083926],
       [ 2.36123731, -0.96820231, -2.81929874, -1.72652236,  2.82552447,
         2.71503355,  2.01371607, -1.42256556,  2.11307602, -2.44451292],
       [-0.8857772 , -0.23277895,  1.39855305, -1.94911574, -1.30902916,
        -1.23945866,  0.56619667, -2.25559527, -0.43754633,  1.27193219],
       [ 2.56375093, -1.31931021, -2.45807602, -0.68949774,  2.98902646,
         3.02063482,  1.37221704, -0.65022992,  1.41964464, -2.50057065],
       [ 2.38164318, -0.92868403, -1.83852544, -1.80270113,  2.09837663,
         2.63975133,  1.65157334, -1.4827709 ,  1.30482126, -2.19668092],
       [ 2.17687652, -0.89263997, -2.38506778, -1.30037317,  2.64870623,
         2.75307898,  1.75040063, -0.95390544,  1.60421972, -2.32471915],
       [-0.05361532, -0.45118962, -0.8737831 , -0.20893402,  0.24906407,
         0.29097626, -0.47388705, -0.96000581, -0.09865433, -0.89556361],
       [-1.59929753, -1.75671005, -0.25923449,  2.48473248, -0.73486872,
         0.83366656, -2.01319979,  2.37761394, -1.40670842, -1.08597609],
       [-0.33157786, -1.83633604, -2.61553018,  1.99466981,  1.41790066,
         2.12362443, -1.6804813 ,  2.00514346, -0.9020561 , -2.64173213]]), array([[ 1.03017289, -1.42990751, -1.0524936 ],
       [-0.39734884,  0.22196451, -0.57805317],
       [-1.3514577 ,  1.90779694, -0.02435519],
       [-1.39874638, -0.52354294,  2.07671192],
       [ 0.72868842, -1.58616697, -0.07075543],
       [ 0.81533187, -1.73385151,  0.57912315],
       [ 1.03527991, -0.13017919, -0.98712371],
       [-1.11267399, -0.34021242,  2.19168519],
       [ 0.14301228, -0.66282142, -0.78579809],
       [-0.96462212,  1.99655906, -0.16788179]])]

ちなみに、学習時点の評価関数の値も取り出せます。「学習状況を知りたいから、ニューラルの学習状況の図を作ってよ」と教員に言われた場合は、以下のように描けばokです。

In [87]:
import matplotlib.pyplot as plt
LossFunc = Load_model.loss_curve_
plt.plot(LossFunc)
plt.xlabel('Iteration')
plt.ylabel('Evalation')
Out[87]:
Text(0, 0.5, 'Evalation')

Iterationが学習回数、Evaluationがニューラルネットの誤差だと考えてください。学習を進めるごとに、教師データの誤差が減っていることを確認できます。もうちょっと学習を行っても良さそうですね。

おわりに

構築したモデルのセーブとロードの方法について説明しました。研究や分析を行っているとき、1日で作業が完了することはあまりありません。また、モデルを1つ作って終わりということもないです。そのため、きちんと自分の作ったモデルを、時間を記録しながらセーブすることができるようになりましょう!!