TadaoYamaokaの日記

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

将棋AIの進捗 その55(勾配クリッピング)

dlshogiを初期値から学習を開始すると、lossがinfやnanになる場合がある。
初期値からの学習以外でも勾配爆発を防ぐために、勾配クリッピングのオプションを追加した。

KataGoでも勾配クリッピングを追加している。学習が安定してからは特に効果はないようだ。

勾配クリッピングにより同じデータを学習した場合に結果が変わるか確認した。

勾配クリッピング

PyTorchのclip_grad_norm_を使用する。
これは、勾配のノルムが閾値以下になるようにクリッピングを行う。
値でクリッピングするよりも勾配の方向が変わらないから良いのだとか、大差ないとか*1

なお、AMPを使用している場合は、クリッピングを適用する前にスケールを戻す必要があるので注意が必要である。
Automatic Mixed Precision examples — PyTorch 1.7.1 documentation

測定方法

dlshogiの強化学習で生成した8,939,266局面を使用して、初期値から学習する。
テストデータには、floodgateのレート3500以上の対局の棋譜からサンプリングした856,923局面を使用した。

測定結果

訓練損失
勾配クリッピング 訓練平均方策損失 訓練平均価値損失
なし 0.91759841 0.41159116
max_norm=2 0.96313657 0.41694899
max_norm=10 0.91871473 0.41199293
テスト損失
勾配クリッピング テスト方策損失 テスト価値損失
なし 1.04227679 0.58552035
max_norm=2 1.05631246 0.58615224
max_norm=10 1.04086553 0.58512790
テスト正解率
勾配クリッピング テスト方策正解率 テスト価値正解率
なし 0.38140064 0.67416036
max_norm=2 0.37752943 0.67332306
max_norm=10 0.38164004 0.67416620
考察

max_norm=2は、なしに比べて学習が遅くなっている。
max_norm=10となしはほとんど変わっていない。

まとめ

初期値からの学習を安定化させるため勾配クリッピングを試した。
小さめの値にすると学習が遅くなることがわかったため、通常の学習に影響がない適当な値(10くらい)に設定しておくことにする。
KataGoでは学習率に合わせてスケーリングしているが、そこまでの調整は必要ないだろう。

将棋AIの実験ノート:方策の分布を学習

一般的な方策勾配法では、選択した行動aに対して、損失を計算するが、
\displaystyle
l_{policy} = - \log p(a)
AlphaZeroでは、方策の損失は、探索から求めた方策の確率分布\pmb{\pi}を使用している。
\displaystyle
l_{policy} = - \pmb{\pi}^\top \log \mathbf{p}

dlshogiでは、前者をベースにしたActor-Criticで更新を行っている。
後者の確率分布を学習する場合と、どちらが良いのか比較してみた。

実装

損失関数

選択した行動を学習する場合は、方策がソフトマックス関数で、出力がロジットの場合は、PytorchのCrossEntropyLossを使用して損失が計算できる。

確率分布を学習する場合は、組み込みの損失関数がないため、以下のように損失関数を定義した。

def cross_entropy_loss_with_soft_target(pred, soft_targets):
    return torch.sum(-soft_targets * F.log_softmax(pred, dim=1), 1)
教師データ

方策の確率分布が教師データに必要になるため、dlshogiで使用しているhcpeフォーマットでは対応できない。
また、合法手ごとの訪問回数を記録するため、局面により合法手の数が異なるため可変長フォーマットになる。
hcpeを拡張して、以下のようなフォーマットにした。

HuffmanCodedPosAndEval3 = np.dtype([
    ('hcp', dtypeHcp),
    ('eval', dtypeEval),
    ('bestMove16', dtypeMove16),
    ('result', np.uint8),
    ('seq', np.uint8), # 開始局面からの手数/2(今のところ使わない)
    ('candidateNum', np.uint16),
    ])
MoveVisits = np.dtype([
    ('move16', dtypeMove16),
    ('visits', np.uint16),
    ])

MoveVisitsはcandidateNumの数だけ繰り返す。
可変長フォーマットの場合は、今までの固定長のようにディスク上のデータすべてをメモリにそのまま読み込んで使用することができないので、シーケンシャルに読み込む処理が必要になる。
その場合は、開始局面のhcpと指し手のみ記録した方がデータサイズを節約できる。
また、resultを全局面に記録する必要もなくなるので、フォーマットはもう少しスリムにできる。
とりあえず実験したかったので、上記のような冗長なフォーマットになっている。

測定方法

強化学習で新たに教師データを生成するには時間がかかるため、方策の分布が記録されているAobaZeroの棋譜を使用した。
棋譜はarch000015000000からarch000015190000を使用し、合計19,695,636局面を学習した。

テストデータには、floodgateのレート3500以上の対局の棋譜からサンプリングした856,923局面を使用した。

比較対象のActor-Criticは、dlshogiで使用している式を使用した。
これはエントロピー正則化も含んでいる。

分布を学習する場合は、エントロピー正則化は行わない。

比較結果

訓練損失
訓練平均方策損失 訓練平均価値損失
Actor-Critic 0.90724320 0.58483684
分布を学習 1.97043909 0.58462431
テスト損失
テスト方策損失 テスト価値損失
Actor-Critic 0.98559491 0.57659638
分布を学習 0.93136043 0.56870430
テスト正解率
テスト方策正解率 テスト価値正解率
Actor-Critic 0.40432432 0.68084011
分布を学習 0.41709992 0.68707843
テストエントロピー
テスト方策エントロピー テスト価値エントロピー
Actor-Critic 1.67369065 0.60195279
分布を学習 1.82972054 0.59813645
考察

方策の訓練損失は式が異なるため直接は比較できない。
テスト損失は、dlshogiの損失計算に合わせている。
テスト損失、テスト正解率どちらも、分布を学習した方が、方策、価値どちらも良い値になっている。
ただし、価値については初期値の影響による誤差の範囲かもしれない。

また、方策のエントロピーが分布を学習した方が高く、より偏りの少ない方策になっていることがわかる。

まとめ

方策をActor-Criticで学習する場合と、探索後のルートノードの訪問数を使用した確率分布を学習する場合の比較を行った。
実験結果から、方策は分布を学習した方がよさそうということがわかった。

結果を受けて、dlshogiの強化学習でも分布を学習可能にする予定である。
教師データのフォーマットの見直しから行うことにする。

BoostでZlib Filtersを有効にしてビルドする

C++で、gzipの圧縮解凍を標準ストリームで行いたかったので、ライブラリを調べたところ、Boost.IostreamsZlib Filtersを追加することで対応できることがわかった。

しかし、Windowsではデフォルトでは有効になっておらず、「conda install -c conda-forge boost」のビルド済みパッケージにも含まれていない。
そのため、ソースからビルドする必要がある。

この記事では、zlibを有効にいsてビルドする方法について記す。

Windows

boostダウンロード

boostをダウンロードする。
https://www.boost.org/

適当な場所に解凍する(以下、boost_1_75_0をC:\に解凍したとして記述する)。

zlibのソースダウンロード

zlibのソースをダウンロードする。
https://zlib.net/

適当な場所に解凍する(以下、zlib-1.2.11をC:\に解凍したとして記述する)。

boostビルド

b2コマンドの引数「-s ZLIB_SOURCE=」にソースのパスを指定してビルドする。
以下は、staticリンクライブラリをビルドする例。

cd boost_1_75_0
bootstrap
b2 toolset=msvc threading=multi variant=debug,release link=static runtime-link=static address-model=64 --stagedir=stage/x64 -j 8 -s ZLIB_SOURCE="C:\zlib-1.2.11"

なお、Boost.Pythonを有効にするには、ビルド前に「C:\Users\\user-config.jam」に、以下のように記述しておく。

using python : 3.8 : C:\\Users\\kei\\anaconda3\\python ;

マニュアルには、user-config.jamにuse zlibの記述が必要なように書かれているが、必要なかった。

Ubuntu 18.04

Linuxではデフォルトでzlibが有効になっている。

conda install boost

でインストールしたboostで使用できる。
libboost_iostreamsに含まれるので、libboost_zlib相当は必要ない。

コード例
#include <iostream>
#include <fstream>

#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filter/gzip.hpp>

int main() {
	std::ifstream ifs("a.gz", std::ifstream::binary);
	if (!ifs) return 1;

	boost::iostreams::filtering_streambuf<boost::iostreams::input> filter;
	filter.push(boost::iostreams::gzip_decompressor());
	filter.push(ifs);

	std::istream stream(&filter);
	char buf[256];
	stream.read(buf, sizeof(buf));

	for (size_t i = 0; i < sizeof(buf); ++i) {
		std::cout << std::hex << (unsigned int)(unsigned char)buf[i] << " ";
	}
	std::cout << std::endl;
}
ビルド例
g++ -I$CONDA_PREFIX/include -L$CONDA_PREFIX/lib a.cpp -lboost_iostreams

のようにboost_iostreamsをリンクする。

将棋AIの実験ノート:オフポリシー補正

強化学習において、サンプル効率が低いという課題の対処として、リプレイバッファを使用して過去の方策で生成したデータも学習に使用するということが行われている。
一般的に挙動方策(経験を蓄積する際の方策)と推定方策(学習する方策)が異なる場合をオフポリシーと呼ぶ。
逆に一致する場合をオンポリシーと呼ぶ。

方策勾配法などのオンポリシーの手法で、サンプル効率を高めるためにリプレイバッファを使用することは、経験的に破壊的に大きなポリシー更新につながることがある。

AlphaZeroの強化学習

AlphaZeroもリプレイバッファを用いておりオフポリシーで学習を行っている。
ただし、AlphaZeroはシミュレータ内で探索を行った結果を方策としており、いわゆるモデルベースの手法なので、オフポリシー/オンポリシーの分類は適切ではないかもしれない。

AlphaZeroでは、方策の分布を学習しており、Actor-Criticとは異なります。
分布を学習する場合には、オフポリシーでも適用可能は不明です。

dlshogiの強化学習

一方、dlshogiの強化学習は、分布を学習すると教師データのデータ量が増えるので、指し手のみを学習に使用している。
方策の学習則には、Actor-Criticを使用しており、これはオンポリシーの手法である。

オンポリシーの手法に、リプレイバッファを使用するのは問題があるのではないかと考えていたが、うまく学習できているのもあって、ちゃんと検証していなかった。
今回一度確認しておこうと思って、検証することにした。

オフポリシー補正

Actor-Criticをオフポリシーで使用するために、V-TraceやACER、PPOといったアルゴリズムがある。
ここでは実装が簡単なV-TraceとPPOを試すことにする。

V-Trace

V-TraceをActor-Criticに適用する際の方策の目的関数は以下の通り。
\displaystyle
\rho_s \nabla_\omega\log\pi_\omega(a_s|x_s) \big( r_s+\gamma v_{s+1} - V_\theta(x_s)\big) \\
ここで、
\rho_t=\min\big(\bar\rho, \frac{\pi(a_t|x_t)}{\mu(a_t|x_t)}\big)\bar\rhoは定数である。\piは推定方策、\muは挙動方策を表す。

参考:[1802.01561] IMPALA: Scalable Distributed Deep-RL with Importance Weighted Actor-Learner Architectures

PPO

PPOの目的関数は以下の通り。
\displaystyle
L^{CLIP}(\theta) = \hat{\mathbb{E}}_t \big[ min(r_t(\theta)\hat{A}_t, clip(r_t(\theta), 1-\epsilon, 1+\epsilon)\hat{A}_t) \big]
ここで、r_t(\theta)=\frac{\pi_\theta(a_t|s_t)}{\pi_{\theta_{old}}(a_t|s_t)}\epsilonは定数である。

参考:[1707.06347] Proximal Policy Optimization Algorithms

検証方法

強化学習を回すのは時間がかかるので、AobaZeroの棋譜を使用して検証することにする。
AobaZeroの棋譜には候補手の訪問回数が記録されているため、それを挙動方策の遷移確率に利用する。

AobaZeroの棋譜のarch000015000000 ~ arch000015090000を使用。
AobaZeroは、探索結果の行動価値が記録されていないので(最近は記録されている)、アドバンテージのベースラインは0とする。
テストデータにはfloodgateからサンプリングした943456局面(レート3500以上、50手以上の対局、30手目以降)を使用する。

検証結果

訓練損失
平均方策損失 平均価値損失
Actor-Critic 1.90775840 0.62536926
V-Trace 1.87498970 0.62457944
PPO -0.00017280 0.61246887
テスト損失
テスト方策損失 テスト価値損失
Actor-Critic 1.06661845 0.55477527
V-Trace 1.06576384 0.55451178
PPO 2854212034370635596562432.0 0.58852763
テスト正解率
テスト方策正解率 テスト価値正解率
Actor-Critic 0.40007314 0.72041744
V-Trace 0.40022049 0.72101849
PPO 0.00014841 0.67355092

V-Traceは、Actor-Criticよりも少しだけ良い値だが、ほぼ誤差の範囲である。
PPOは全く学習できなかった。


アドバンテージのベースラインに、学習時に推論した価値を使用するパターンでも試してみた。

訓練損失
平均方策損失 平均価値損失
Actor-Critic 0.75596953 0.62025761
V-Trace 0.74280808 0.62022987
PPO 0.05604221 0.61169946
テスト損失
テスト方策損失 テスト価値損失
Actor-Critic 1.10852726 0.55280487
V-Trace 1.10677386 0.55142957
PPO inf 0.59008008
テスト正解率
テスト方策正解率 テスト価値正解率
Actor-Critic 0.38780105 0.72076196
V-Trace 0.38771095 0.72095700
PPO 0.00033497 0.67275483

傾向は変わらない。


Actor-CriticとV-Traceの差がほとんどないので、訓練データを倍にしてみた。PPOは学習できないので除外。

訓練損失
平均方策損失 平均価値損失
Actor-Critic 1.75861482 0.61717799
V-Trace 1.74029188 0.61730657
テスト損失
テスト方策損失 テスト価値損失
Actor-Critic 1.02857797 0.52934007
V-Trace 1.02989807 0.52900566
テスト正解率
テスト方策正解率 テスト価値正解率
Actor-Critic 0.41769314 0.73888017
V-Trace 0.41761576 0.73889289

やはり差は誤差の範囲である。

まとめ

Actor-Criticのオフポリシー補正手法であるV-TraceとPPOを試した。
結果、Actor-CriticとV-Traceでほとんど違いは見られなかった。
AlphaZeroの自己対局では挙動方策はMCTSで探索した結果であるため、以前のパラメータで探索した結果と現在のパラメータで推論した結果で差がでにくいのかもしれない。
そもそもモデルベースなので、正確にシミュレーション可能なモデルがあればオフポリシーでも学習できそうである。

PPOはなぜか全く学習できなかった(実装の誤りの可能性もある)。

AlphaZeroと同じように分布を学習した場合についても別途検証してみたい。

将棋AIの実験ノート:Attention Branch Network

以前に、Mask-Attentionについて記事にしたが、同様の手法にAttention Branch Network(ABN)がある。
ABNは、Attention Branchの損失も計算して訓練する点がMask-Attentionと異なる。
f:id:TadaoYamaoka:20210129213306p:plain
ここでは、ABNをdlshogiのネットワークに適用して、AIが注視している座標を可視化してみる。

ネットワーク構成

policyブランチとvalueブランチそれぞれに、Attentionブランチを追加する。
policyブランチとvalueブランチは、元のネットワークから変更していない。
f:id:TadaoYamaoka:20210129220935p:plain
policy attention mapとvalue attention mapは、2Dの画像になっており、AIが注視している箇所を表す。

損失

policy attentionブランチの損失はソフトマックス交差エントロピーvalue attentionブランチの損失はシグモイド交差エントロピーとして、元のネットワークの損失に加算したものを全体の損失とする。

訓練

dlshogiの強化学習で生成した60,679,983局面を使用して訓練した。

可視化

訓練したモデルを使用して、いくつかの局面で可視化してみた。

初期局面

f:id:TadaoYamaoka:20210129222532p:plain:w300

policy attention map

f:id:TadaoYamaoka:20210129223155p:plain:w300

value attention map

f:id:TadaoYamaoka:20210129223225p:plain:w300

policyの予測は「2六歩」と「7六歩」が高いが、policy attention mapはその位置の値が低くなっている。
高くなるなら分かりやすいが、低くなっているので、値が何を示しているのか良くわからない。

逆に、value attention mapは、「2六歩」と「7六歩」の位置の値が高くなっているので、なんとなく注視している箇所を示していそうである。

サンプル局面

f:id:TadaoYamaoka:20210129223852p:plain:w300

policy attention map

f:id:TadaoYamaoka:20210129224157p:plain:w300

value attention map

f:id:TadaoYamaoka:20210129224224p:plain:w300

policyの予測は「3三桂成」が高いが、policy attention mapはその位置の値が低くなっている。
やはり値が何を示しているのか良くわからない。

value attention mapは、「3三」の値が高くなっている。
玉の周りも値が高いので、注視箇所を示していそうである。

精度

ABNは可視化とパフォーマンス向上を両立できる点に利点がある。
SENetでは、チャネル方向にAttentionの導入してパフォーマンスを向上している。
ABNでは画素方向にAttentionを適用している。

元のネットワークとABNを追加したネットワークで、精度を比較した。

訓練損失
policy平均損失 value平均損失
元のネットワーク(resnet+swish) 0.68139506 0.38120323
ABN 0.63697344 0.37672683
テスト損失

floodgateからサンプリングした棋譜で評価した際のテスト損失

policy損失 value損失
元のネットワーク(resnet+swish) 0.97505525 0.54548708
ABN 0.97899158 0.54168929
テスト正解率
policy正解率 value正解率
元のネットワーク(resnet+swish) 0.42881633 0.70599075
ABN 0.43730732 0.71036645

policyのテスト損失は良くなっていないが、正解率は高くなっている。
valueのテスト損失、正解率ともに、元のネットワークよりも良くなっている。

ソース

Attention Branch Network · TadaoYamaoka/DeepLearningShogi@d62f7c7 · GitHub

まとめ

将棋のニューラルネットワークにABNを適用することで、注視している箇所の可視化ができるか試してみた。
policy attention mapについては、値の意味が良くわからなかったが、value attention mapについては注視している箇所を示しているかもしれない。

AIが注視している箇所を可視化することで、初心者がどのあたりに注目して形勢を判断すればよいかのヒントに使えないかと思って試してみたが、これが役に立つかは将棋がわからないとよくわからないのであった・・・。

Jupyter QtConsoleを起動するとコマンドプロンプトが起動する問題の対策

Pythonを対話的に使用する際は、Jupyter QtConsoleを好んで使用している。

Jupyter Notebookを使っている人の方が多いと思うが、Jupyter QtConsoleは起動が速く、Jupyter NotebookをブラウザやVS Codeなどから使用するよりも手軽に使用できる。
また、入力の履歴が残ったり、グラフを別ウィンドウにできたりといった利点もある。

Jupyter QtConsoleは、正確にはいつからかは忘れたがAnacondaの数年前のバージョンから、起動時にバックグラウンドでコマンドプロンプトが同時に起動するようになった。
このウィンドウが邪魔なのだが、気にしないようにして使用していた。

あらためて回避策がないか調べたら、以下の回避策があった。
python - how to launch recent Jupyter QtConsole on Windows without a console window - Stack Overflow

ショートカットを作成して、

pythonw <Anaconda3のScriptsのパス>\jupyter-qtconsole-script.py

のようにリンク先を設定すればよい。
作業フォルダーにデフォルトの作業フォルダも指定しておくとよいだろう。

このショートカットをPATHが通ったフォルダにqtconsoleという名前で配置しておけば、ファイル名を指定して実行から、「qtconsole」で起動できる。

バッファサイズとウィンドウサイズをカスタマイズするには、先ほどのリンク先のコマンドに以下のように引数を追加すればよい。

--JupyterWidget.buffer_size=10000 --JupyterWidget.console_width=150

将棋AIの進捗 その54(補助ターゲット)

深層強化学習では、メインの学習タスクに加えて、補助タスクを同時学習することで、パフォーマンスを改善するということが行われている。
[1611.05397] Reinforcement Learning with Unsupervised Auxiliary Tasks

囲碁AIの例

囲碁AIのKataGoでは、

  • 占領した領域
  • スコア

が補助ターゲットとして使用されている。

チェスAIの例

チェスAIのLeela Chess Zeroでは、

  • 残り何手で終局か(Move Left)

が補助ターゲットとして使用されている。

通常、補助ターゲットは、学習時のみ使用して、推論時には利用しない。
ただし、Leela Chess ZeroのMove Leftは、ゲームをより短い手数で終了するために探索でも活用されている。

将棋での補助ターゲット

将棋は詰みによる終局は、直前まで駒損していても詰ませれば勝ちなので、終局時の駒得のような補助ターゲットは適切ではない。
入玉の有無はありかもしれない。

Leela Chess Zeroのような後何手で終局かは、将棋の詰み手順は、ディープラーニングが苦手としているので、強化学習時に最短手数で詰ませられないと正しく学習できないかもしれない。

データ作成が簡単で、効果がありそうな補助ターゲットとして、思いつくものとしては、

がある。

ここでは、この2つを補助ターゲットに追加して、効果があるかを検証してみる。

教師データ

強化学習で検証するには時間がかかるので、floodgateの棋譜を使用して教師ありで検証する。

2018年~2020年の棋譜から、レーティングが3000以上、80手以上の棋譜から、序盤は偏りがあるため30手目以降の局面を使用する。
最大手数に達した対局は除く。

訓練:テスト=9:1の割合で、棋譜単位で分割する。

棋譜 局面数
訓練 133114 13971526
テスト 14818 1555972

実装方法

教師データフォーマット

dlshogiで使用しているhcpeのgameResult(8bit)の3bit目と4bit目で、千日手入玉宣言勝ちを表現する。

ニューラルネットワーク

価値ヘッドの出力は1つで勝率を出力しているが、そこに千日手入玉宣言勝ちに対応する出力ユニットを追加する。

        # value network
        self.l22_v = nn.Conv2d(in_channels=k, out_channels=MAX_MOVE_LABEL_NUM, kernel_size=1, bias=False)
        self.l23_v = nn.Linear(9*9*MAX_MOVE_LABEL_NUM, fcl)
        self.l24_v = nn.Linear(fcl, 1)
        # sennichite, nyugyoku
        self.l24_aux = nn.Linear(fcl, 2)
損失

千日手入玉宣言勝ちの出力の損失関数に、シグモイド交差エントロピーを使用する。
千日手入玉宣言勝ちの損失平均を補助ターゲットの損失とする。

千日手入玉宣言勝ちで終局する対局は、全体からすると割合が小さいため、正例に重み付けを行う。
PyTorchでは、BCEWithLogitsLossのpos_weightで重みを与えることができる。

学習データによって割合は異なるため、訓練データ中の千日手入玉宣言勝ちの割合を調べて、動的に重みを与える。

pos_weight = torch.tensor([
    len(train_data) / (train_data['result'] // 4 & 1).sum(), # sennichite
    len(train_data) / (train_data['result'] // 8 & 1).sum(), # nyugyoku
    ], dtype=torch.float32, device=device)
bce_with_logits_loss_aux = torch.nn.BCEWithLogitsLoss(pos_weight=pos_weight)

通常の損失と補助ターゲットの損失で加重平均をとり、全体の損失とする。
補助ターゲットの重みは0.1とした。

学習結果

学習結果は以下のようになった。
※末尾に(aux)の付いた系列(オレンジ)が補助ターゲットありの結果
※テストデータは100ステップごとに640局面をサンプリング
f:id:TadaoYamaoka:20210121202103p:plainf:id:TadaoYamaoka:20210121202106p:plainf:id:TadaoYamaoka:20210121202110p:plainf:id:TadaoYamaoka:20210121202113p:plainf:id:TadaoYamaoka:20210121202116p:plainf:id:TadaoYamaoka:20210121202119p:plainf:id:TadaoYamaoka:20210121202123p:plainf:id:TadaoYamaoka:20210121202126p:plain

訓練データすべてを学習後(SWAあり)、テストデータ全部で評価した結果は以下の通り。

テスト方策損失 テスト価値損失 テスト補助ターゲット損失
通常 0.93448454 0.39452724 -
補助ターゲットあり 0.94912096 0.39225218 1.08832618
テスト方策正解率 テスト価値正解率
通常 0.41683142 0.79983546
補助ターゲットあり 0.43945136 0.80143781
考察

補助ターゲットを追加することで、同一データを用いて学習した場合でも、方策と価値の両方で、精度が上がっている。

テストの補助ターゲット損失は、グラフを見ると低下していないため、千日手入玉宣言勝ちの予測の汎化性能は高くない。
ただし、出現頻度が少ないため、さらにデータを増やすことでテストでも精度が上がる可能性がある。

まとめ

千日手入玉宣言勝ちを補助ターゲットに加えることで、精度が上がることが確かめられた。
ONNXに出力する際は補助ターゲットを削除できるため、推論の速度には影響を与えることなく精度が向上できる。

今後、最大手数で引き分けになったかや、終局時に入玉しているかなどを補助ターゲットに加えるなども検証してみたい。