【エンジニアの道は果てしない】 Pythonの辞書とリストを活用してみる
こんにちは。すうちです。
前回AIの学習・推論の実装例を紹介しましたが、全体の流れ中心でコードの中身はほとんど触れてませんでした。
今回は動物分類の実装でも使ったPythonの辞書やリストについて、知っておくと便利な使い方をいくつか紹介します。
リスト・辞書の活用
リスト・辞書によるデータ定義
下記のように辞書(dictionary {})とリスト(list [])を使ってます。リストも辞書もデータを保存する箱ですが、できることが少し違います。辞書はテキストのワードとペアでデータを保存したり取り出すことができます。
リストは、a=[] 又は a=list()
辞書はb={} 又は b=dict()で作れます。
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)},
]
animal_baseは各動物のデータ(名前、身長・体重等)を辞書で保存し、更にそれらをリスト化したものです。
’Name’と’Plot_c’のキーは文字列で動物の名前や図のプロット色を保存。
身長・体重のキー’H’と'W'は、(最小、最大)範囲を保存してます。
表2はデータ構造のイメージです。
リストのインデックスと辞書のキーを指定すると、下記のようにデータを参照できます。
animal_base[0]['Name'] → ’Wolf’
animal_base[3]['H'], animal_base[3]['W'] → (170, 250), (150, 250)
ちなみに、データの定義はこれが正解という意味ではなく、今回はこの形(管理ルール)に決めたという程度です。
辞書の参照
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)])
前述のanimal_baseから身長体重のデータを取出します。
最初のenumerate()は、リストからデータ(animal)を取出す際、同時にリストのインデックス番号(i)も返します。
例えば、以下のようにiとanimal が更新されます。
(i, animal )=(0, オオカミdata), (1, ハイエナdata ), …(4, シマウマdata )
for 文により動物5種類の身長・体重の最小・最大値を取出します。
animal_base[0]のオオカミの場合、animal[’H’]は(100, 130), animal[’W’]は(25, 45)です。
ここでenumerate()を使うのは、取出した順番で別のリストH[]や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)])
リスト[]の中にfor文などありますが、これはリストの内包表現です。簡単なプログラムを書いてリストを初期化できます。
random.randint(a, b)は、a~b範囲の任意の整数を一つ生成します。
上記は身長体重の範囲を引数にしてサンプル数(num)分データを作ってます。またsorted()で中身のデータを昇順に並べ替えしてます。
ちなみに乱数の生成方法は、他にもあります。
ここで生成した各動物の身長体重データ(サンプル数300*5=1500)をanimal_data_listに保存、最終的にtrainとtestデータに分割します。
リストの分割
Pythonのライブラリtrain_test_split()は、入力X、Yに対して、学習(X_train, y_train)と推論(X_test, y_test)データに分けて出力します。
(X_train, X_test, y_train, y_test) = \
train_test_split( X, y, test_size=0.3, random_state=0, )
ここで入力X=animal_data_list[:, 1:](data[1]とdata[2]の列), 正解ラベルY=animal_data_list[:, 0](data[0]の列)です。
リスト内の表記は[行,列]を示しPythonでは指定範囲(先頭位置:最後尾-1)を参照可能です。
仮にanimal_data_list[299:301,0]の場合、data[0]列の[0,1]が取出せます。
上記[:, x]は行の範囲指定はないので行の全データかつ列の指定範囲が抽出対象です。
リストの結合
# 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]
ここでは後で正解ラベルYと入力Xのペアを一緒に取出すためy_xxxとX_xxxのリストを結合して別のリストにしています。
npは、Pythonで行例データを扱えるライブラリnumpyです。
np.c_[a, b]は行列a, bを列(column)方向で結合します(np.r_[a, b]は行(row)方向に結合可)
やっていることは、図2のイメージです。
リストの参照
#学習(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)
out_labelは再び内包表現で長さ5(=out_nodes)、[0.01, 0.01, 0.01, 0.01, 0.01]のリストです。
dataは[正解ラベル、身長、体重]が入っているので目的のデータを取出します。
data[1:]は前述のスライス表記でリストから[指定位置(先頭) : 指定位置(最後尾-1)]を抽出。
最後尾の指定を省略しているのでdata[1]からリストの終端(data[2])を取り出す(=入力)という意味です。
リストのインデックス取得(最大/最小)
#推論(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)
np.argmax()は、リストのインデックス(最大値がある位置)を返します。
outputsリスト(動物分類の推論確率)が図3[0.92, 0.13, 0.13, 7.5e-6, 4.6e-12]の場合、インデックス0を返します。
ちなみに、最小値のインデックスが知りたい場合は、np.argmin()です。
最後に
Pythonのリスト・辞書活用 まとめです。
・リストと辞書作成例:
a=[] 又は a=list(), b={} 又は b=dict()
・リストの参照例:
一次元a[0], 2次元 a[0,1], 範囲指定a[:, 0:5]
・辞書の参照例:
b['name'], b['H'], b['W']
・リストの内包表現例:
list = [v for n in range(loop)]
・リストの結合例:
列方向 np.c_[a0,a1], 行方向 np.r_[a0,a1]
・リストの分割:
TrainとTestデータ分割 train_test_split()
・リストのインデックス取得:
最大値 np.argmax() 最小値 np.argmin()
Pythonでリストや辞書を使う様になってプログラムを書くことが楽になった感覚があります。
既にやられている方はおなじみの話と思いますが、プログラムにあまり馴染みのない方など、何か参考になれば幸いです。
最後まで読んで頂き、ありがとうございました。