TadaoYamaokaの日記

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

将棋でディープラーニングする その2(ニューラルネットワークの構成)

先日の日記に続き、将棋でのディープラーニングの実装を試す。

今回は、ニューラルネットワークの構成を検討する。

ネットワーク構成

ネットワーク構成は、AlphaGoのネットワーク構成を参考にし、13層の畳み込みニューラルネットワーク(DCNN)とする。
位置について精度が要求されるため、プーリング層は使用しない。

入力特徴

9×9の2値画像を特徴数の枚数分入力する。
特徴は以下の通りとする。

特徴 枚数
自プレイヤーの駒の配置 14
自プレイヤーの持ち駒 38
相手プレイヤーの駒の配置 14
相手プレイヤーの持ち駒 38
空の配置 1

持ち駒は、駒ごとに1枚を割り当て、持ち駒の最大枚数分を割り当てる。
持ち駒がある場合は、すべて1の画像とし、持ち駒がない場合はすべて0の画像とする。

入力特徴数の合計は、105枚となる。

他にも、手番や王手かどうか、2歩の位置なども入力特徴とした方が精度があがると思われるが、一旦最低限の特徴のみで検証を行いたい。

出力

指し手の確率を出力する。
指し手は合法手を絞らず、駒ごとの座標に一つのラベルを割り当て、多クラス分類問題として扱う。
どの駒を移動したかは考慮しない。
ラベル数は、駒の種類が14なので、14×9×9=1134となる。

Chainerでの実装

以上のように設計したネットワーク構成、入力特徴、出力をChainerで実装すると以下のようになる。

import chainer
from chainer import Chain
import chainer.functions as F
import chainer.links as L

import shogi

k = 256
class MyChain(Chain):
    def __init__(self):
        super(MyChain, self).__init__(
            l1=L.Convolution2D(in_channels = None, out_channels = k, ksize = 3, pad = 1),
            l2=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l3=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l4=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l5=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l6=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l7=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l8=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l9=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l10=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l11=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l12=L.Convolution2D(in_channels = k, out_channels = k, ksize = 3, pad = 1),
            l13=L.Convolution2D(in_channels = k, out_channels = len(shogi.PIECE_TYPES), ksize = 1, nobias = True),
            l13_2=L.Bias(shape=(9*9*len(shogi.PIECE_TYPES)))
        )

    def __call__(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        h3 = F.relu(self.l3(h2))
        h4 = F.relu(self.l4(h3))
        h5 = F.relu(self.l5(h4))
        h6 = F.relu(self.l6(h5))
        h7 = F.relu(self.l7(h6))
        h8 = F.relu(self.l8(h7))
        h9 = F.relu(self.l9(h8))
        h10 = F.relu(self.l10(h9))
        h11 = F.relu(self.l11(h10))
        h12 = F.relu(self.l12(h11))
        h13 = self.l13(h12)
        return self.l13_2(F.reshape(h13, (len(h13.data), 9*9*len(shogi.PIECE_TYPES))))

フィルター枚数は、AlphaGoでは192だが、将棋は駒の種類が多いため一旦256とした。
フィルター枚数などのハイパーパラメータは実験により調整が必要である。

次回、作成したニューラルネットワークを使用して、棋譜から学習を行う予定。