アヤメの種類判別(その5)

実行例は「saka.mokumoku」の「Google Colabotry」環境に保存してある

1.事前準備

 matplotlibを使って可視化する
   「Work Shop」の「3大ライブラリー」のMatplotlibを参照
 
 スライスを使ってデータを特定する
   リストや文字列、タプル等のシーケンス型の一部を
   インデックスを指定して取り出す

   複数の数値をコロン(:)で区切って[ ]で囲う
     a[start : stop : step]
       step:何個ごとに抽出するか(デフォルト=1)
       start:切り取りたい部分の開始(一番左)のインデックス
       stop:切り取りたい部分の終わり(一番右)のインデックス

2.散布図の作成

 x軸にはがく片の長さ、y軸にはがく片の幅をセット、散布図を描く
   がく片の長さとがく片の幅だけで、特徴が現れる

   3つのブロックが50㎝、100㎝で区切られる理由が分からない

import matplotlib.pyplot as plt

x = iris.data
y = iris.target

plt.scatter(x[:50, 0], x[:50, 1], color='r', marker='o', label='setosa')
plt.scatter(x[50:100, 0], x[50:100, 1], color='g', marker='+', label='versicolor')
plt.scatter(x[100:, 0], x[100:, 1], color='b', marker='x', label='virginica')
plt.title("Iris Plants Database")
plt.xlabel("sepal length(cm)")
plt.ylabel("sepal width(cm)")
plt.legend()
plt.show

 scatter命令について
   scatterの第1引数がX軸、第2引数がY軸
   引数で線の色(color='')やマーカーの種類(marker='')、インデックス(index='')なども指定できる

   散布図

3.機械学習

 TensorFlowとKerasをロードする

 乱数のシード値を固定して並べ替える
   引数は実数であれば良い、1に設定する
   TensorFlowの重みの設定が固定される

 idxr = [k for k in range(Ndata)]はリスト内包表記という
   既存のリストから新しいリストを作ること
   分かりやすく分解すると下記のようになる
     idxr = []
     for k in Ndate
       idxr.append(k)

 print文でリスト内に0から149までの数値が格納されていることを確認する

 random.shuffle(idxr)よって先ほどのリストの中身をシャッフルし、
   print(idxr)で中身がランダムに再配置されたか確認する

#
#TensorFlowとKerasをロードする
#
import tensorflow as tf
from tensorflow import keras
print(tf.__version__)
#
#シード値を設定する
#
tf.random.set_seed(1)
#
#データをランダムに並べ替える
#
import random

random.seed(12345)
Ndata = len(iris.data)
print(f"Ndata={Ndata}")
idxr = [k for k in range(Ndata)]
print(idxr)
random.shuffle(idxr)
print(idxr)

 データを分割する
   訓練データと検証データを半分、半分に分割する

 print(f"# of training data = {Ndata_train}")
 print(f"# of validation data = {Ndata-Ndata_train}")
   それぞれ訓練データと検証データの数を出力する

 train_data = iris.data[idxr[:Ndata_train]]の行では訓練データを、
 train_labels = iris.target[idxr[:Ndata_train]]の行では訓練データの教師ラベルを
   スライスを使って、idxrの0~74番目を各iris.datairis.targetに対応させ
   train_datatrain_labelsに代入する

 val_data = iris.data[idxr[Ndata_train:]]の行では検証データを、
 val_labels = iris.target[idxr[Ndata_train:]]の行では検証データの教師ラベルを
   スライスを使って、idxrの75~149番目を各iris.datairis.targetに対応させ
   val_dataval_labelsに代入する

Ndata_train=int(Ndata*0.5)
print(f"# of training data = {Ndata_train}")
print(f"# of validation data = {Ndata-Ndata_train}")

train_data = iris.data[idxr[:Ndata_train]]
train_labels = iris.target[idxr[:Ndata_train]]

val_data = iris.data[idxr[Ndata_train:]]
val_labels = iris.target[idxr[Ndata_train:]]

 ニューラルネットワークを作成する
   KerasのSequentialというクラスを使ってニューラルネットワークを作成

   4次元(がく片の長さ、がく片の幅、花弁の長さ、花弁の幅)のデータから
   3次元(setosa, versicolor, virginica)のラベルへ層が構成されている

   中間層は2層でユニット数(中間層の次元)は10、
     Denseで設定したため全結合層になっている

   modelには、keras.Sequentialから作成したインスタンスが入っており、
     これを使用してデータを学習させることが可能になる

model = keras.Sequential([
    keras.layers.Dense(4, activation='relu'),
    keras.layers.Dense(10, activation='relu'),
    keras.layers.Dense(10, activation='relu'),
    keras.layers.Dense(3, activation='softmax')
])

 機械学習の準備を行う
   modelインスタンスのcompileメソッドを使って学習の設定を行う

   compileには引数が3つ
     optimizer:最適化手法を設定する
       今回はSGDに設定
     loss:損失関数を設定する
       今回はsparse_categorical_crossentropyに設定
     metrics:評価関数を設定する
       accuracyを入れておけば問題ない、評価関数を設定しても良い

model.compile(optimizer='SGD',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

 機械学習を行う
   modelインスタンスのfitメソッドを使うことで学習を行う

   fitの引数に
     訓練データとそれに対応する訓練データのラベルを代入する
     オプションとして他に4つの引数を使っている
       validation_data:検証用データをタプルにして渡す
         (val_data, val_labels)を設定する
       epochs:エポック数を設定、デフォルトは1
         30に設定するしている
       batch_size:バッチサイズを設定、デフォルトは32
         ミニバッチのサイズはNdata_train//10によって7つだが、
         この数字に特に意味はない
       verbose:ログ出力の設定、デフォルトは1
         0だとログが出ない
         正の値だと細かいログが出力される
         負の値だとepoch数のみ表示される

training_history = model.fit(train_data, train_labels,
                             validation_data=(val_data, val_labels),
                             epochs=30,
                             batch_size = Ndata_train//10,
                             verbose=1)

 結果を評価する
   履歴を見る
     fitの返り値をtraining_historyに代入し、中身を見る

   大量に表示されるが履歴はhistoryに格納されている
     出力結果に'accuracy':とあることからdict型のリストである

   辞書のキーに何が入ってるのか確認するためにkeysメソッドを使う
     4つのキーで構成されていることがわかる
       val_accuracy:検証データに対する精度の値
       loss:訓練データに対する損失関数の値
       accuracy:訓練データに対する精度の値
       val_loss:検証データに対する損失関数の値

dir(training_history)

training_history.history

training_history.history.keys()

 精度を確認する
   訓練データに対する精度の値と検証データに対する精度の値を確認する
     訓練データに対する精度の値が79%
     検証データに対する精度の値が73%

print("traininig")
print(training_history.history['accuracy'][-1])
print("validation")
print(training_history.history['val_accuracy'][-1])

 損失関数と精度の履歴について散布図で可視化する
   損失関数の散布図は対数軸にするためにplt.semilogyを使う
   精度の散布図はplt.ylimを使ってy軸の範囲を0~1.1に設定する

# 訓練データに対する損失関数のプロット
y=training_history.history['loss']
x=range(len(y))
plt.semilogy(x,y,label="loss for training")

# 検証データに対する損失関数のプロット
y=training_history.history['val_loss']
x=range(len(y))
plt.semilogy(x,y,label="loss for validation",alpha=0.5)

plt.legend()
plt.xlabel("Steps")
plt.show()

# 訓練データに対する精度のプロット
y=training_history.history['accuracy']
x=range(len(y))
plt.plot(x,y,label="accuracy for training")

# 検証データに対する精度のプロット
y=training_history.history['val_accuracy']
x=range(len(y))
plt.plot(x,y,label="accuracy for validation")

plt.legend()
plt.xlabel("Steps")
plt.ylim(0,1.1)
plt.show()

4.結論

 最終的な訓練データの正答率は79%、検証データの正答率は73%
   アヤメは3種類なので当てずっぽうの33.3%よりは高い精度

 散布図では損失関数が減少すると分類精度は上昇する、負の相関が見られる