CNNによる数字当てモデルの実装コードです。コピペで動くようにしてあります。
まずは、必要なファイルをインポートします。再現性保証のための関数もいれてあります。
# 必要なファイルのインポート
import tensorflow
from tensorflow.keras.datasets import mnist
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Flatten, Dropout, Conv2D, MaxPooling2D
from tensorflow.python.client import device_lib
from keras.models import model_from_json
from tensorflow.keras import optimizers
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
import os
import random
# 学習の再現性を保証する関数
def reproduct(s):
os.environ['PYTHONHASHSEED'] = '0'
os.environ['TF_DETERMINISTIC_OPS'] = '1'
os.environ['TF_CUDNN_DETERMINISTIC'] = '1'
tensorflow.random.set_seed(s)
np.random.seed(s)
random.seed(s)
続いて、データの前処理を行います。
# 警告を非表示に
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
# GPUが動いているかチェック
# CPUの表示のみならCPUで、GPUが表示されるとGPUで学習されます。
print(device_lib.list_local_devices())
# mnistデータセットのダウンロード
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 教師データを、メイン教師データとバリデーションデータに分割
# メイン教師データとは、勾配法に適用させるデータで、バリデーションデータとは過学習を監視するためのデータです。
# また、テストデータは、汎化誤差を測定するためのデータとなります。
# random_stateを使用しないと再現性がなくなるので注意しましょう。
x_main_train, x_valid, y_main_train, y_valid = train_test_split(x_train, y_train, test_size=0.20, random_state=0)
# 特定の画像の表示
imgid = 10
plt.imshow(x_main_train[imgid, :, :], "gray")
plt.title("number = " + str(y_main_train[imgid]))
plt.show()
plt.close()
# データ数*行サイズ*列サイズ*チャンネル数の4次元配列に変換
# kerasはこの形状にしなければ動かないので注意しましょう。
x_main_train_cnn = x_main_train.reshape(x_main_train.shape[0], x_main_train.shape[1], x_main_train.shape[2], 1)
x_valid_cnn = x_valid.reshape(x_valid.shape[0], x_valid.shape[1], x_valid.shape[2], 1)
x_test_cnn = x_test.reshape(x_test.shape[0], x_test.shape[1], x_test.shape[2], 1)
# ピクセルのレンジを0~255から0~1に変換
# この処理を入れないと、学習がうまくいかないことが多いので、必須となります。
x_main_train_cnn = x_main_train_cnn/255
x_valid_cnn = x_valid_cnn/255
x_test_cnn = x_test_cnn/255
# 正解値をone hot ベクトルに変換
# 例えば、正解値が3であれば、[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]と変換する処理です。
# 0~9を当てる10択問題なので、depthに10を指定します。
y_main_train = tensorflow.one_hot(y_main_train, depth=10)
y_valid = tensorflow.one_hot(y_valid, depth=10)
y_test = tensorflow.one_hot(y_test, depth=10)
CNNの定義と、学習を行います。
# モデルの定義
reproduct(s=15) # 学習の再現性を保証。乱数の出目が悪いならば、sを別の整数に
model = Sequential()
model.add(Conv2D(filters=10, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1)))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Conv2D(filters=10, kernel_size=(3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(30, activation='relu'))
model.add(Dense(20, activation='relu'))
model.add(Dense(10, activation='softmax'))
# モデルの構造を見る
model.summary()
# モデルのコンパイル
# 10**(-3)は学習係数で、10のマイナス3乗を意味します。
model.compile(loss="categorical_crossentropy", optimizer=optimizers.RMSprop(learning_rate=10**(-3)), metrics=['accuracy'])
# 学習開始
# メイン教師データとバリデーションデータを与えます。
# epochは、バリデーションデータの正答率(val_accuracy)が最大となる値が良いです。
# バリデーションデータは勾配法の計算に利用されていないためです。
# メイン教師データの正答率(accuracy)に、大きな意味はありません。
# メイン教師データの正答率(accuracy)を高くしすぎると、バリデーションデータの正答率(val_accuracy)が下がります。
# この現象を、過学習と呼びます。したがって、val_accuracyを眺めるようにしましょう。
history = model.fit(x_main_train, y_main_train, batch_size=256, epochs=5, verbose=1, validation_data=(x_valid, y_valid))
学習過程を確認します。
# 学習過程の表示
# 今回はaccurcyのみにしましたが、lossも同様の方法でみることができます。
acc_main_train = history.history["accuracy"]
acc_valid = history.history["val_accuracy"]
plt.plot(acc_main_train, label="main train data")
plt.plot(acc_valid, label="validation data")
plt.legend()
plt.grid()
plt.ylabel("Accuracy")
plt.xlabel("epoch")
plt.show()
plt.close()
正答率を確認します。
# モデルの精度評価
score_main_train = model.evaluate(x_main_train, y_main_train, verbose=0) # メイン教師データ
score_valid = model.evaluate(x_valid, y_valid, verbose=0) # バリデーションデータ
score_test = model.evaluate(x_test, y_test, verbose=0) # テストデータ
print("メイン教師データの正答率", score_main_train[1])
print("バリデーションデータの正答率", score_valid[1])
print("テストデータの正答率", score_test[1])
特定の一枚を推定させます。
# 特定の1枚の推定
imgid = 15 #15枚目の画像を対象に
est = model.predict(x_main_train[imgid:imgid+1])
est = np.argmax(est)
print("推定値: ", est)
学習済みモデルを保存します。
# 学習済みモデルの保存
# jsonファイルがレイヤー構造などの保存で、h5ファイルが学習によって得られたパラメータです。
# いいモデルに出会えたら、きちんと保存しておきましょう。
model_json_str = model.to_json()
open("network.json", 'w').write(model_json_str)
model.save_weights("weights.h5")
保存されたモデルをロードし、推定を行います。
# 保存済みモデルのロード
# 学習済みのモデルで、推定を行います。
# これができると、良いモデルをそのまま使えるので、とても便利です。
# 継続して学習させたい場合は、コンパイルしてからfitさせればokです。
loadmod = model_from_json(open("network.json").read())
loadmod.load_weights("weights.h5")
est = loadmod.predict(x_main_train[imgid:imgid+1])
est = np.argmax(est)
print("推定値: ", est)