TadaoYamaokaの開発日記

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

Gumbel dlshogiを作る その3(バックアップ処理修正)

前回までで、Gumbel AlphaZeroのアルゴリズムで自己対局して、訓練データ出力するところまで実装できた。

動作確認のため、python-dlshogi2の学習済みモデルをTorch Scriptにエクスポートして、生成される棋譜を確認した。

Torch Scriptにエクスポートする処理
from pydlshogi2.network.policy_value_resnet import PolicyValueNetwork
import torch

class PolicyValueNetworkAddSigmoid(torch.nn.Module):
    def __init__(self, *args, **kwargs):
        super(PolicyValueNetworkAddSigmoid, self).__init__()
        self.base_model = PolicyValueNetwork(*args, **kwargs)
    
    def forward(self, x):
        y1, y2 = self.base_model(x)
        return y1, torch.sigmoid(y2)

model = PolicyValueNetworkAddSigmoid()
checkpoint = torch.load(r"checkpoints\checkpoint.pth")
model.base_model.load_state_dict(checkpoint['model'])
scripted_model = torch.jit.script(model)
scripted_model.save(r"model.pt")

動作確認

生成された棋譜を確認したところ、初手から飛車を振るなど、あきらかにおかしな手を選択していた。

position startpos moves 2h7h 8c8d 7g7f 8d8e 8h7g 3c3d 7i6h 2b7g+ 6h7g 5a4b 7h8h 6c6d B*5e 2a3c 5e6d B*4e 3i3h 4e6g+ 6i6h P*6f 6d7e 6g4e 4g4f 4e4f 7e6f 6a5b 6h5h P*6e 6f7e 7a6b 5h4g 4f3e 5g5f 6b6c 8h6h 3e4d 5i4h 4b3b 4h3i 7c7d 7e5g 6c6d 3i2h 8e8f 8g8f 5c5d 5g4f 5d5e 6h8h 5b6c 8f8e 8b5b 8e8d 5e5f 8d8c+ 6e6f P*5h 6f6g+ P*6e 6d5e 8c7b 8a7c 7b7c 5f5g+ 7c6c 5g4g 3h4g 5e4f 4g4f 5b5f S*4g 5f4f 4g4f 3b2a G*5b 4a3b 6c5c 6g5h 8h5h G*4g 5c4b 4g5h 4b3b 3a3b R*5a 2a1b 5a3a+ R*2b 4i5h 3c2e N*3f 4d3c P*4d P*5g 5h5g 4c4d P*4c P*5f 4c4b+ 5f5g+ 4f5g G*4g G*4h 4g5g 4h5g S*4h G*3h 4h5g+ G*3i P*5a 4b3b 3c3b 5b4b 3b3a 4b3a G*2a 3a2a 1b2a S*3c G*4c 3c2b+ 2a2b R*4a S*4b 3f4d S*3a G*3b 3a3b 4a4b+ 4c4b B*6d G*4c 4d3b+ 2b3b 7g6f 5g4g 3h4g N*3e P*4d R*8h P*5h 3e4g 4d4c+ 3b4c S*4d 4c4d 6f5e 4d3c S*4d 3c2b 6d4b+ 8h5h+ P*4h G*3h 2h1h R*2h 3i2h 3h2h 1h2h 5h4h G*3h S*3i 2h1h 4h3h S*2h 3h2h

原因

原因を調べたところ、バックアップ処理で、手番が変わっても価値が反転されていなかった。
将棋の自己対局では、手番が変わると価値が反転するため、バックアップする際に、価値を反転する必要がある。

公式の実装では、将棋のような自己対局のケースが考慮されておらず、子の価値をそのまま親に反映するようになっている。

論文では囲碁でも実験されており、どのように実行したのか謎である。

修正

バックアップ処理を以下のように修正した。

        # Here we update the value of our parent, so we start by reversing.
        leaf_value, index = loop_state
        leaf_value = 1 - leaf_value # ★修正
        parent = tree.parents[index]
        count = tree.node_visits[parent]
        action = tree.action_from_parent[index]
        parent_value = (tree.node_values[parent] * count + leaf_value) / (count + 1.0)
        children_value = tree.node_values[index]
        children_count = tree.children_visits[parent, action] + 1

        tree.node_values[parent] = parent_value
        tree.node_visits[parent] = count + 1
        tree.children_values[parent, action] = 1 - children_value # ★修正
        tree.children_visits[parent, action] = children_count

修正後の動作確認

修正後は、初手が2六歩となり、直っていそうである。

position startpos moves 2g2f 8c8d 7g7f 8d8e 8h7g 3c3d 7i7h 4a3b 1g1f 1c1d 2f2e 2b7g+ 7h7g 3a2b 9g9f 2b3c 3i3h 7c7d 3g3f 8a7c 3h3g 7a7b 6i7h 6c6d 3g4f 6a6b 5i6h 7b6c 4i5h 9c9d 3f3e 3d3e 4f3e 8e8f 8g8f P*8e 2e2d 2c2d 3e2d 3c2d 2h2d P*2c 2d3d S*3c 3d3e 8e8f S*7a 8b8a 7a6b 5a6b P*8b 8a8b P*8h 8b8a 7f7e 7d7e P*7d 6c7d 9f9e 9d9e 9i9e P*9d 9e9d P*3d 3e3f P*9c 9d9c+ 9a9c B*9b 8a8d P*9d 9c9d P*9e 9d9e 7g8f B*6c 8f9e 8d8b 9b7d+ 6c7d G*8d 7d6c 3f8f 6b5a 8d7c 8b8f 9e8f 6c2g+ P*2b R*4i 2b2a+ S*6i R*7a 5a4b S*5a 4b4a 5a6b+ 4a4b 7a5a+

まとめ

Gumbel AlphaZeroの自己対局の動作確認をしたところ、出力される棋譜が不自然だったため原因を調べた。
結果、バックアップ処理で手番が変わっても価値を反転しないことが原因だった。
修正後は、初手が妥当な2六歩となり、意図した通りに動作していることが確認された。

妥当な棋譜が生成できるようになったので、次回は、ニューラルネットワークモデルの学習処理を実装する予定である。