TadaoYamaokaの開発日記

個人開発しているスマホアプリや将棋AIの開発ネタを中心に書いていきます。

将棋でディープラーニングする その39(ブートストラップ)

前回の日記で、ブートストラップについて少し書いたが、1000万局面では効果がわからなかったので、局面を増やして再度検証した。

ブートストラップ

前回も書いたが、本来の報酬(勝敗)とは別の推定量(探索結果の評価値)を用いてパラメータを更新する手法をブートストラップという。
elmo_for_learnで生成したデータには、局面の探索結果の評価値が含まれているので、バリューネットワークの値をその評価値に近づけるように学習することで、学習の効率を上げることができると思われる。
経験的にブートストラップ手法は、非ブートストラップ手法より性能が良いことが知られている。

ブートストラップ項を加えた損失関数

elmoと同様に、ブートストラップ項の損失には、2確率変数の交差エントロピーを使用する。

バリューネットワークの値をp、探索結果の評価値をシグモイド関数で勝率に変換した値をqとした場合、交差エントロピーは以下の式で表される。
\displaystyle
\begin{eqnarray*}
H(p, q) &=& - \sum_t p(t) \log q(t) \\
 &=& -p \log q - (1 - p) \log(1-q)
\end{eqnarray*}

交差エントロピー偏微分は、
\displaystyle
\frac{\partial H(p, q)}{\partial w} = q - p
となるが、Chainerで実装する場合、backwardの処理をGPUで計算できるように、cudaの処理を記述する必要がある。
技量不足でその部分を実装できなかったため、交差エントロピーを以下のように計算して、微分は計算グラフの処理に任せることにした。

def cross_entropy(p, q):
    return F.mean(-p * F.log(q) - (1 - p) * F.log(1 - q))

このブートストラップ項に係数\lambdaを掛けて、元の損失関数に加える。
損失関数は以下の通りになる。

loss1 = F.mean(F.softmax_cross_entropy(y1, t1, reduce='no') * z)
loss2 = F.sigmoid_cross_entropy(y2, t2)
loss3 = cross_entropy(F.sigmoid(y2), value)
loss = loss1 + loss2 + args.val_lambda * loss3

loss1は指し手予測(policy network)の損失、loss2は勝率予測(value policy)の損失、loss3がブートストラップ項である。

測定結果

ブートストラップなしで2億局面学習したモデルから、ブートストラップなし/ありで、8000万局面を学習して精度を比較した。

train loss1 train loss2 train loss3 test acc.(policy) test acc.(value)
ブートストラップ項なし 0.8883 0.4638 0.4439 0.7651
ブートストラップ項あり 0.8853 0.4741 0.4427 0.4444 0.7658

train loss2(value networkの損失)は、ブートストラップ項のなしの方が減少しているが、test accuracyはブートストラップ項ありの方がわずかに良い。
train loss3はブートストラップ項ありの場合のみ測定しているが、初期から減少を続けており、value networkの予測が評価値に近づいている。

GPSfishとの対局

ブートストラップ項ありで学習したモデルで、GPSfishと対局させた。
f:id:TadaoYamaoka:20170628075612p:plain
GPSfishに勝つことができた。
評価値はGPSfishとほぼ同じ傾向だが、GPSfishより早く評価値が付いている。

ブートストラップ項なしで学習したモデルでは以前と同様に、評価値がGPSfishより遅れて不利を判断しており、勝つことができなかった。
ブートストラップ項を追加することで、バリューネットワークの学習効率が上がることが確かめられた。

これで、ディープラーニングのみでも(GPSfishよりも)強いソフトが作れることが確認できた。
この方法で、さらに学習を進める予定。