TensorFlowを使ってジャンケンデータを学習する

f:id:hanamichi_sukusuku:20210202204005p:plain

実行結果

f:id:hanamichi_sukusuku:20210202204032p:plain

hanamichi-sukusuku.hatenablog.com

このプログラムではTensorFlowを用いて上記で作成したジャンケンの勝敗データを学習させるプログラムである。

 

tensorflowとは

機械学習を行うライブラリーで、機械学習ディープラーニングが実践できるが、それだけでなく、汎用的な仕組みを提供している。今回はtensorflowの使い方に注目してプログラムを見ていく。

 

kerasとは

Pythonの深層学習(ディープラーニング)のライブラリである。

深層学習を行うときに、イチから全部作っていたら大変なので、使いやすいものを用意してある。

 

ニューラルネットワークとは

ニューラルネットワークとは、人の神経を模したネットワーク構造のことで、コンピューターに学習能力を持たせることにより、様々な問題を解決するためのアプローチができる。人間の脳の中には多数の神経細胞ニューロン)が存在し、一つのニューロンは他の複数のニューロンから信号を受け取り、他のニューロンに対して信号を渡す。脳はこの信号の流れによって様々な情報を伝達している。この仕組みをコンピューターで再現したのがニューラルネットワークである。

 

ディープラーニングとは

ディープラーニングとは機械学習の一分野で、精度が良いことが特徴である。そして、もう少し具体的にいうとニューラルネットワークを3層以上重ねたものを、一般的にディープ・ニューラル・ネットワーク(DNN)とよび、このDNNを使った機械学習ディープラーニングという。ディープラーニングでは、大量のデータを学習することで、各ニューロン間の接続の重み付けのパラメーターを繰り返し調整する。この重みづけというのは単なる多数決で結果が決まるのではなく、例えばテレビを購入するかどうかを決定するときに、テレビが壊れているや資金が少ないなど要因によってテレビを購入する必要があるかどうかはそれぞれなので、テレビが壊れているならその部分の必要性(重み付け)は高いと設定し、資金が少ないならその必要性は低いと設定することで要因によってパラメーターの重み付けを変化させ最適な結果を出力するために重み付けが必要なのである。

 

 

 

保存したジャンケンデータを読み込む

with open("janken-data.pkl", "rb") as fp:
  data = pickle.load(fp)
(x_train, y_train), (x_test,y_test) = data

作成したジャンケンの勝敗データを読み込み、学習用、テスト用に分ける。

janken-data.pklには

[[x_train,y_train],[x_test, y_test]]という形でそれぞれ~_trainが学習用データ、~_testがテスト用データとして格納されている。

 

モデルの構築

model = tf.keras.models.Sequential([
tf.keras.layers.Dense(30, activation='relu', input_dim=2),
tf.keras.layers.Dense(3, activation='softmax')
])
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])

ここでは単純なニューラルネットワークのモデルを構築している。TensorFlowでは学習を行う前にどんなモデルを利用して、ニューラルネットワークを利用するのかを最初に指定し、モデルを構築(コンパイル)する必要がある。そのため、変数modelにネットワークの構造を指定し、compileメソッドで構築する。

 

kerasにおけるモデルには大きく分けて3種類のモデルがある。

  • Sequential(積層型)モデル コンパクトで簡単な書き方
  • Functional(関数型)API 複雑なモデルも定義できる柔軟な書き方
  • Subclassing(サブクラス型)モデル 難易度は少し上がるが、フルカスタマイズが可能

f:id:hanamichi_sukusuku:20210202213800p:plain



 

Sequentialモデルでは、このような階層構造が直列につながっているような状態を表す。

tf.keras.layers.Dense(30, activation='relu', input_dim=2)では上記の画像のような層(レイヤー)をモデルに追加している。

Denseは全結合層のことで上記画像のように前の層の全ユニットの出力が次の層の全ユニットの入力となるのが全結合層である。

第一引数の30は出力数を表し、activation='relu'では活性化関数を表し、前の層からの出力をどのように変換するかという認識でいいと思う。reluは0以下なら0を返し、0より大きい場合はそのまま返すというもの。input_dimは入力層(input Layer)でDenseを追加した場合はinput_dimで入力パラメーターの数を指定する。[身長,体重,年齢]ならinput_dim=3のようになる、今回は2つなのでinput_dim=2。

 

tf.keras.layers.Dense(3, activation='softmax')では

活性化関数は、ユニットごとに個別に入力から出力を決めるのが基本ですが
softmaxは「全ユニットからの出力を、合計が1になるようにする」というもの。

例を挙げると
3ユニットからの出力[1, 2, 2]があった時に
これをsoftmaxに入力すると[0.2, 0.4, 0.4]というように出力してくれる。

だいたい分類モデルの最後の層に加えることが多いと思う。
「入力データからAかBかCか」という分類の問題でどれくらいの割合で
予想されているか出力したいときに手軽にできる。

機械学習は、結果の出力に大きい値やマイナスの値を出すことがある。
これらの値をsoftmax関数に入力すると、出力の合計が1になるように出力してくれる。

softmax関数は分類モデルの最後の層に加えることが多い。
「入力データから、AかBかCか判別」という分類の問題で
どれくらいの割合で予想されているか出力したいときに手軽にできる。

今回の場合でいくと

f:id:hanamichi_sukusuku:20210202220539p:plain

この部分の二次元配列で表現しているところは出力数3、activation='softmax'により合計が1になるように出力されている。

 

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

model.compile()では作成されたモデルがどのように学習するかの指定を行う。

引数はそれぞれ

 

optimizer:最適化関数
loss:損失関数(目的関数)
metrics:評価関数

 

optimizerでは機械学習をする上での最適化アルゴリズムを指定でき、様々なものがあるが今回はadamを指定。

lossは損失関数で、どれだけ正解から外れているかを計算する関数で、この値を小さくする方向に学習が進んでいくので目的関数とも呼ばれている。sparse_categorical_crossentropyでは正解ラベルのインデックスを整数で受け取ることができる。

 

metricsは評価関数でモデルの性能を測るために使われる。正解率を知りたいのでaccracyを指定。

 

学習と評価

model.fit(x_train, y_train, epochs=20)
# テストデータを評価 --- (*4)
model.evaluate(x_test, y_test, verbose=2)

 

作成したモデルをfit()で学習させる。epochsではニューラルネットワークで繰り返し学習を行う回数を指定している。学習経過に表示されるlossとaccuracyを参考に回数を決定する。

 

model.evaluate()ではテストデータを評価しており

f:id:hanamichi_sukusuku:20210202223435p:plain

この部分を表示している。verboseは詳細表示のしかたを指定するもので0だと表示しない、1だとプログレスバーで表示、2だとエポックごとに1行のログを表示する。

accracy: 1.000ということなのでちゃんとジャンケンのルールを学習している。

 

実際に勝負結果を出力してみる

def janken(a, b):
 hands = {'グー':0, 'チョキ':1, 'パー':2}
 results = ['あいこ', '負け', '勝ち']
 x = np.array([[hands[a], hands[b]]])
 r = model.predict(x)
 print(r)
 print(a, b, '→', results[r[0].argmax()])

janken('グー', 'グー')
janken('チョキ', 'パー')
janken('パー', 'チョキ')

predictの結果を見てみると

f:id:hanamichi_sukusuku:20210202220539p:plain

0.74.... ,  0.13....,  0.11....のように予測結果を配列で返している。これは[あいこ, 負け, 勝ち]のそれぞれの値の確率を示すものである。確率が最も高いものが最も勝率の高い答えということになるので、argmaxメソッドを用いることで最も高い値を持つ要素の番号を得ることができる。つまり[0.74.... ,  0.13....,  0.11....]なら0が返される。