【エンジニアの道は果てしない】AIで学習・推論してみる(動物分類の実装)
こんにちは。すうちです。
前回AIの学習について、概念や考え方を紹介しました。
AIの学習は、y=ax+b(Y=WX+b)のa,b(W:重み b:バイアス)を目的の判定(例:5種類の動物分類)になるように最適値を調整する話でした。
今回はAIを使った学習・推論について、実際のプログラム(実装)とあわせて書きたいと思います。
結論
はじめに
データは、前回と同じく動物図鑑/動物の大きさの比較を参考にしました。
本来この目的にAIを使う必要はないですが、今回は上記分布をあえてAIで分類(学習と推論)する前提で進めます。コードはPythonです。以下実装の流れです。
1. データ準備
データ作成
データは5種類の動物の身長体重(最小最大範囲)からランダムに生成して昇順に並び替えます。また動物1種のデータサンプル数が少ないので300に増やします(合計1500)。
animal_base = \
[
{'Name':'Wolf', 'Plot_c':'red', 'H':(100,130), 'W':(25,45)},
{'Name':'Hiena', 'Plot_c':'green', 'H':(120,180), 'W':(55,85)},
{'Name':'Bear', 'Plot_c':'brown', 'H':(140,160), 'W':(85,145)},
{'Name':'Lion', 'Plot_c':'blue', 'H':(170,250), 'W':(150,250)},
{'Name':'Zebra', 'Plot_c':'gray', 'H':(200,240), 'W':(250,300)},
]
for i, animal in enumerate(animal_base):
# label_keys(0: Wolf, 1: Hiena, 2: Bear, 3: Lion, 4: Zebra)
# Nameからラベル値に変換
Y = Name_to_Label(animal['Name'], label_keys)
# Dictionaryから身長体重範囲を取出し
H_min, H_max = animal['H']
W_min, W_max = animal['W']
# サンプル数(num)分、指定範囲のランダム値をリスト化、昇順に並べ替え
H[i] = sorted([random.randint(H_min, H_max) for n in range(num)])
W[i] = sorted([random.randint(W_min, W_max) for n in range(num)])
散布図はプロットサイズ(=1)、サンプル数(=300)に変えましたが分布は同じ傾向に見えます。
学習と推論(テスト)データ
準備したデータは学習用と推論用(テスト)に分けたいので、Pythonのライブラリ(sklearn)を使用して学習:推論=7:3に分割します。
(X_train, X_test, y_train, y_test) = train_test_split(
X, y, test_size=0.3, random_state=0,
)
train_test_splitは入力X(身長・体重のリスト)、正解データy(正解ラベル値:0~4のリスト)、テストサイズ(=0.3)を設定します。これにより学習(train)と推論(test)データをそれぞれ1050、450個に分けることができます。
データの正規化(ノーマライズ)
データのばらつき範囲が大きいと学習時に重み(W)の最適化がうまく進まないことがあります。そのため入力範囲を一定に保つ様にデータを正規化(0〜1範囲に調整)します。
図2の分布見るとデータ範囲は0〜350に収まっているので、身長体重の値を最大値350で割って補正します。
# AIに設定する学習と推論データを準備
X_train_norm = (X_train/350)*0.99+0.01
X_test_norm = (X_test/350)*0.99+0.01
train_data_list = np.c_[y_train, X_train_norm]
test_data_list = np.c_[y_test, X_test_norm]
ちなみに正規化しない場合、推論精度(正解率)は30%程度でした。
2. AIのモデル
今回のモデルは以下を参考にしています(後半に書籍リンクあり)。
入出力にそれぞれ隠れ層を配置したニューラルネットワークでオリジナルはMNIST(手書き文字の画像サンプル)を判定するモデルです。層伝搬の活性化関数fはシグモイド関数です。
モデルの中身は公開されているので、バックプロパゲーション(誤差逆伝播)などの計算も確認できます。
入出力設定
データは身長と体重の値がペア(2個)なので入力2、最終出力は5種類の動物分類なので5です。隠れ層のノード数や学習率は仮置き。学習の過程で別途最適値を設定します。
# Parameters
input_nodes, hidden_nodes, output_nodes, learning_rate = 2, hid_nodes, 5, ir
# Model
m = network_model('NN', input_nodes, hidden_nodes, output_nodes, learning_rate)
3. 学習
#学習(train)のループ処理
for e in range(epochs):
for cnt, data in enumerate(training_data_list):
out_label = [0.01 for n in range(out_nodes)]
max_index = int(data[0])
out_label[max_index] = 0.99
inputs = data[1:]
targets = out_label
outputs = nn.train(inputs, targets)
学習データのリストから正解ラベルと入力データ(身長、体重)を取出します。最終出力は5個なのでサイズ5のリストを用意し正解ラベル(リストのインデックス)の位置を0.99にします(他は0.01)。学習は1050個のデータに対してエポック数分くり返します。
4. 推論(テスト)
推論は入力データがどのラベルに該当するかを確率で返します。出力(リスト)にある一番高い確率のラベルをAIの判定結果とします。
#推論(test)のループ処理
for cnt, data in enumerate(test_data_list):
inputs = data[1:]
targets = data[0]
outputs = nn.query(inputs)
out[cnt] = int(np.argmax(outputs))
tar[cnt] = int(targets)
推論結果
今回は設定値を頑張って調整しなくても、テストデータに対して99%の精度(正解率)で判定できました。
Loop:target keys-> ep:100, hid:100, ir:0.06
NN Training has completed!!
NN Eval has completed!!
accuracy:99.00[%] 495/500
最後に
流れからわかるようにデータを準備した後はモデルに学習と推論をお任せしている感じです。これだけ見ると意外と簡単そうだなと思われるでしょうか。。。
今はこの他にも様々なモデルがあり、その気になってデータさえ用意すればAIを試すことができます(実は事前の加工や質の良いデータを多く集める方が時間かかったり大変だったりします)。
今回は入力データが小さいかつ図2の分布は境界が明確なので(元々AIを使う程でないとは言え)簡単すぎたのかもしれません。
データ分布やモデルの設定を変えた場合の変化については、また別の機会に書きたいと思います。
補足
以下、AIの勉強始めた頃に読んで参考になった本です。
AIの理論や基礎を簡単な数学を交えつつ丁寧に説明されています。今回使用したモデル(ニューラルネットワーク)も上記を参考にさせて頂きました。
こちらも基礎から応用まで実際のプログラムも交え詳しく説明されてます。特に誤差逆伝播をグラフで説明する例はとてもわかりやすかったです。
何か参考になれば幸いです。
最後まで読んで頂き、ありがとうございました。