DeepLearningによる将棋の学習1

 山岡さんの本を参考にtensorflowを用いてDeepLearningを用いた将棋ソフトを作ってみています。

将棋AIで学ぶディープラーニング

将棋AIで学ぶディープラーニング

 ResidualNetを実装しているところなのですが、実装がまずいのかブロック数を増やすと上手く学習してくれないという事態が起こりました。現在の実装は以下の通りです。山岡さんの本をそのまま模倣しようとしているので、そうなっていない部分があったら指摘していただけると幸いです。

ch = 192
kernel = 3


def ResidualBlock(inputs):
    conv1 = tf.layers.conv2d(inputs=inputs, filters=ch, kernel_size=kernel, padding="same", use_bias=False)
    bn1 = tf.layers.batch_normalization(inputs=conv1, training=True)
    relu1 = tf.nn.relu(bn1)
    conv2 = tf.layers.conv2d(inputs=relu1, filters=ch, kernel_size=kernel, padding="same", use_bias=False)
    bn2 = tf.layers.batch_normalization(inputs=conv2, training=True)
    return tf.nn.relu(inputs + bn2)


def policy_value_func(features):
    # Input Layer
    # featuresのshapeは9×9マスが104チャネル ([-1, 9, 9, 104])
    input_layer = features

    # ResidualBlock
    conv1 = tf.layers.conv2d(inputs=input_layer, filters=ch, kernel_size=3, padding="same", activation=tf.nn.relu)
    res1 = ResidualBlock(conv1)
    res2 = ResidualBlock(res1)
    res3 = ResidualBlock(res2)
    res4 = ResidualBlock(res3)
    res5 = ResidualBlock(res4)
    res6 = ResidualBlock(res5)
    res7 = ResidualBlock(res6)
    res8 = ResidualBlock(res7)
    res9 = ResidualBlock(res8)
    res10 = ResidualBlock(res9)

    # policy_network用
    conv13p = tf.layers.conv2d(inputs=res10, filters=MOVE_DIRECTION_LABEL_NUM, kernel_size=1, padding="same",
                               activation=tf.nn.relu)
    policy = tf.reshape(conv13p, shape=[-1, 9 * 9 * MOVE_DIRECTION_LABEL_NUM])

    # value_network用
    conv13d = tf.layers.conv2d(inputs=res10, filters=MOVE_DIRECTION_LABEL_NUM, kernel_size=1, padding="same",
                               activation=tf.nn.relu)
    # 1次元化する
    flat = tf.reshape(conv13d, [-1, 9 * 9 * MOVE_DIRECTION_LABEL_NUM])

    # 全結合層
    dense = tf.layers.dense(inputs=flat, units=256, activation=tf.nn.relu,)
    # 出力値はスカラ1つ
    value = tf.layers.dense(inputs=dense, units=1)
    # サイズ1のベクトルになっているのでsqueezeで直す
    value = tf.squeeze(value, axis=1)

    # policyとvalueの二つを返す
    return policy, value

 学習はfloodgateの2016年の棋譜を山岡さんの本の通りに学習用3,711,167局面、テスト用414,677局面に分け、early stoppingをpatience = 3でやっています。

 5ブロックで行った場合の結果が以下

epoch Loss Move Accuracy Value Accuracy
1 3.6427 0.3086 0.6702
2 3.2474 0.3481 0.6825
3 3.1120 0.3646 0.6899
4 3.0099 0.3765 0.6882
5 2.9835 0.3807 0.6913
6 2.9768 0.3865 0.6861
7 2.9753 0.3879 0.6853
8 3.0051 0.3903 0.6839
9 2.9959 0.3927 0.6816
10 3.0267 0.3927 0.6815

 10ブロックで行った場合の結果が以下のようになります。

epoch Loss Move Accuracy Value Accuracy
1 5.0812 0.2416 0.6547
2 4.6445 0.2853 0.6804
3 4.4765 0.3042 0.6877
4 4.3976 0.3153 0.6901
5 4.3842 0.3237 0.6883
6 4.3915 0.3265 0.6866
7 4.3952 0.3267 0.6836
8 4.4427 0.3301 0.6837

 5ブロックのときと比べて損失が大きい段階で学習が終了しています。指し手の一致率も6%以上低いです。今冷静に見返すとやはりもっとpatienceを大きくするべきなのでしょうか……。

 上の二つの実験はoptimizerをNesterovありのmomentum

optimizer = tf.train.MomentumOptimizer(learning_rate=0.01, momentum=0.9, use_nesterov=True)

で行っており、普通のSGD

optimizer = tf.train.GradientDescentOptimizer(learning_rate=0.01)

にしたところ5ブロックの場合にやや劣る程度になりました。

epoch Loss Move Accuracy Value Accuracy
1 3.9261 0.2829 0.6452
2 3.4671 0.3243 0.6741
3 3.2671 0.3472 0.6791
4 3.1414 0.3597 0.6859
5 3.0936 0.3697 0.6810
6 3.0394 0.3727 0.6882
7 3.0323 0.3789 0.6845
8 3.0575 0.3811 0.6798
9 3.0550 0.3833 0.6842
10 3.0771 0.3852 0.6813

 表にして最終結果をまとめると以下のようになります。

model Loss Move Accuracy Value Accuracy
5ブロック(Nesterov) 2.9753 0.3879 0.6853
10ブロック(Nesterov) 4.3842 0.3237 0.6883
10ブロック(SGD) 3.0323 0.3789 0.6845

 patienceが小さいと鞍点にハマってしまうかどうかに大きく左右され、運次第になってしまうということでしょうか。それぞれ1回の実験だけではなんとも言えないという気もします。optimizerとの相性の問題だとするとかなり大変なことだなと思えるのですが、何はともあれもっと実験を重ねてみるしかなさそうです。