TadaoYamaokaの日記

山岡忠夫Homeで公開しているプログラムの開発ネタを中心に書いていきます。

AlphaZero方式における入力の正規化

前回AlphaZero方式で訓練データを作成する際のデータの格納方式をSQLiteに決めたので、テストのためにfloodgateの棋譜から訓練データの作成して、学習を行ってみた。

floodgateの棋譜から訓練データ作成

cshogiを使って2018年分のfloodgateのCSAファイルから訓練データを作成した。
AlphaZeroの論文の通り履歴局面は8手まで含めた。
棋譜には指し手の確率分布はないため、唯一の指し手の確率を1として生成した。
局面の繰り返し数は1までとした(速度の都合による。AlphaZeroでは4まで)。

検証データとして2019年分の棋譜から同様にデータを作成した。

訓練

AlphaZeroのモデルをスケールダウンしたResNet10ブロック、192フィルタのモデルで訓練を行った。

入力特徴量

入力特徴量は、局面の繰り返し数が1までである点以外はAlphaZeroと同じとした。

  • P1 piece 14
  • P2 piece 14
  • Repetitions 1
  • P1 prisoner count 7
  • P2 prisoner count 7
  • Colour 1
  • Total move count 1
正解データ

policyの出力は確率分布を正解として与える。floodgateの棋譜から作成したデータでは指し手の確率が1で、他が0の分布になる。
Kerasでは、categorical_crossentropyは確率分布を入力にできる。

valueは負けが-1、引き分け0、勝ちを1として正解を与える。

訓練結果

バッチサイズ256で1000ステップ学習した結果、以下のようになった。

100/100 [==============================] - 8s 82ms/step - loss: 11.4482 - policy_loss: 9.5888 - value_loss: 0.9822 - policy_categorical_accuracy: 0.0102 - value_binary_accuracy: 0.4967
1000/1000 [==============================] - 137s 137ms/step - loss: 8.9705 - policy_loss: 7.0944 - value_loss: 0.9828 - policy_categorical_accuracy: 0.0141 - value_binary_accuracy: 0.5013 - val_loss: 11.4482 - val_policy_loss: 9.5888 - val_value_loss: 0.9822 - val_policy_categorical_accuracy: 0.0102 - val_value_binary_accuracy: 0.4967

※上段が検証データの結果、下段が訓練データの結果

policyの正解率が1%、valueが49.6%となり、ほとんど学習できていない。

データの作成やネットワーク定義などに誤りがないか確認したが特に間違っていない。
そこで、入力特徴の局面の手数(Total move count)を整数のまま入力していたので、上限が1になるように正規化を行ってみた。
一般的に機械学習では、入力のスケールをそろえて正規化を行うことが重要である。

AlphaZeroの論文には特に記載されていなかったが、論文を読んだとき整数のまま入力していないのではないかと考察したことがある。

正規化

手数(Total move count)は、floodgateの棋譜では256が上限のため、256で割ることにした。
持ち駒の数も歩以外は4枚までなので4で割った。2枚しかない駒は2で割って、歩は8とかで割った方よいかもしれないが、一旦4とした。

正規化後の訓練結果
100/100 [==============================] - 8s 83ms/step - loss: 5.2102 - policy_loss: 3.5036 - value_loss: 0.8290 - policy_categorical_accuracy: 0.2384 - value_binary_accuracy: 0.6488
1000/1000 [==============================] - 137s 137ms/step - loss: 6.2560 - policy_loss: 4.4754 - value_loss: 0.8948 - policy_categorical_accuracy: 0.1724 - value_binary_accuracy: 0.5981 - val_loss: 5.2102 - val_policy_loss: 3.5036 - val_value_loss: 0.8290 - val_policy_categorical_accuracy: 0.2384 - val_value_binary_accuracy: 0.6488

policyの正解率が23.8%、vlaueの正解率が64.8%に改善した。
うまく学習できなかったのは、正規化されていないことがが原因だった。

正規化のために割る数は、floodgateの手数は256が上限だが、自己対局ではAlphaZeroでは512が上限となっているので、調整が必要と思われる。
tanhで、上限を1にしても良さそうである。
tanh(x/100)は以下のようなグラフになる。
f:id:TadaoYamaoka:20190227001923p:plain

試しにtanhでも学習してみた。

100/100 [==============================] - 8s 82ms/step - loss: 5.3477 - policy_loss: 3.5811 - value_loss: 0.8875 - policy_categorical_accuracy: 0.2261 - value_binary_accuracy: 0.6177
1000/1000 [==============================] - 137s 137ms/step - loss: 6.3605 - policy_loss: 4.5126 - value_loss: 0.9604 - policy_categorical_accuracy: 0.1700 - value_binary_accuracy: 0.5321 - val_loss: 5.3477 - val_policy_loss: 3.5811 - val_value_loss: 0.8875 - val_policy_categorical_accuracy: 0.2261 - val_value_binary_accuracy: 0.6177

policyの正解率が22.6%、vlaueの正解率が61.7%となった。
256で割った方が良いが、自己対局の場合は手数が増えるためtanhの方がよいかもしれない。

LeelaChessZeroではどうしているかソースを調べてみたが、手数の入力は未実装だった。
時間があるときにELF Open GoとかLeelaZeroも調べてみたい。

余談

SQLiteのデータをハードディスクに保存すると、作成後再起動するまではキャッシュに乗っているためランダムアクセスしても早かったが、一度PCを再起動すると悲惨なほどランダムアクセスが遅くなった。
保存先をSSDに変えると解決した。アクセス速度が10倍ほど改善した。
SQLiteでランダムアクセスする場合はSSDが必須と言える。