備忘録 blog

Docker/Machine Learning/Linux

先後差を考慮した将棋のプロ棋士の強さモデリング(1)

はじめに

将棋は先手有利であるという通説があるが、実際にその効果はどのぐらいなのだろうか。また、「振り飛車党は先後の不利を克服できる」という説があるが、その説はどのぐらい信憑性があるのか、という点に対して、PyMC3 を用いたベイズモデリングによって解析してみたいと思う。

本稿で解析する内容は以下である。

  • 2021 年のデータを用いて、棋士の強さモデリングの先行例を再現する (1)
  • 棋士共通の先手・後手の影響をモデルに組み込むことで、その差を除いた強さ・勝負ムラを見てみる (1)
  • 棋士ごとの先手・後手の影響をモデルに組み込むことで、先後の強さ差が大きい棋士、少ない棋士を見てみる (2)

MCMC の手法的な性質、またデータが少ないことに起因して、何度か実行した時に結果がばらつくことがある。そのため、ここでの結果はあくまで1回の試行によって得られたものであって、絶対的なものではないということを先に述べておきたい。

先行例の調査

こちらの記事では、 PyMC3 を用いて棋士の強さをモデリングしており、強さの平均と勝負ムラについて解析している。

gaiasky.hatenablog.com

また、次の記事では、 PyStan を用いて同様のモデルでモデリングしている。この例では、先手と後手を別々の棋士であるとみなしてモデリングすることで、先手後手の棋士の実力差を見ている。

qiita.com

ここでは、 これらの記事を参考にしながら、PyMC3 を用いて 先手後手の差を組み込んだモデル を作り、それを用いて最新の勝敗データを用いて以下のことを検討してみたい。

対象は2021年のデータを用いた。その中で、日本将棋連盟に所属するプロ棋士うしの対局であり、勝敗が判明しているもののみを対象とした。(対象となる対局は2330局、棋士数は174人)

f:id:sharply:20220225183656p:plain

のちの解析で利用するため、先手勝ちか後手勝ちかという情報も入れてある。

2021年のデータで同じ実験を再現してみる

まずは、2021年の勝敗データを用いた場合に、どのような結果になるか見てみよう。先行例2に倣ってそのモデルを表現すると、

  • 棋士の強さをμ、実力のムラをσとして、対局時の実力を normal(μ, σ) として定める。
    • 棋士の強さμの分散(すなわち正規分布の分散)の事前分布を一様分布として定める。
  • 実力が高いほうが対局に勝利する。具体的には、実力の差をシグモイド変換した確率値 p に対して、 その確率 p で 1 が出るような試行であったとモデリングする(ベルヌーイ分布)。

モデルコードは PyMC3 による先行例を参考にした。先行例では s_mu = pm.Normal('s_mu', mu=0, sd=100) としていたが、サンプリングの初期化の過程で、μ = -inf となるときに発散してしまうことがわかってきた。以下がエラーメッセージである。

SamplingError: Initial evaluation of model at starting point failed!
Starting values:
{'s_mu': array(0.), 'mu': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0.]), 'sigma_log__': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0.]), 'loser_perf': array([0., 0., 0., ..., 0., 0., 0.]), 'winner_perf': array([0., 0., 0., ..., 0., 0., 0.])}

Initial evaluation results:
s_mu             -5.52
mu                -inf
sigma_log__      38.98
loser_perf    -2142.05
winner_perf   -2142.05
z             -1615.73
Name: Log-probability of test_point, dtype: float64

そこで、ある程度上限の大きい一様分布で表現することにして、上記のエラーを回避している。 このことの意味は、実力の分散の事前分布を一様分布で与えている、ということになる。

pm.traceplot(trace, var_names=['mu','sigma'])

f:id:sharply:20220225000854p:plain

2021 年の対局における計算結果は以下。

棋士の強さの平均(強い順)

mean sd hdi_3% hdi_97% mcse_mean mcse_sd ess_bulk ess_tail r_hat
藤井聡太 2.982 0.458 2.173 3.853 0.03 0.021 228 553 1.01
渡辺明 1.834 0.48 0.896 2.694 0.028 0.02 303 674 1.01
永瀬拓矢 1.76 0.415 1.005 2.565 0.026 0.018 262 627 1.01
伊藤匠 1.726 0.452 0.929 2.601 0.024 0.017 352 871 1
出口若武 1.719 0.461 0.866 2.569 0.023 0.016 419 810 1.01

棋士の勝負ムラの平均(大きい順)

mean sd hdi_3% hdi_97% mcse_mean mcse_sd ess_bulk ess_tail r_hat
石田直裕 1.108 0.34 0.519 1.752 0.019 0.013 318 484 1.02
豊島将之 1.081 0.318 0.52 1.673 0.02 0.014 230 268 1.01
高崎一生 1.073 0.328 0.461 1.654 0.018 0.013 311 551 1
渡辺和史 1.071 0.335 0.48 1.709 0.019 0.014 296 704 1
佐藤和俊 1.065 0.336 0.495 1.72 0.02 0.014 268 488 1

棋士の勝負ムラの平均(小さい順)

mean sd hdi_3% hdi_97% mcse_mean mcse_sd ess_bulk ess_tail r_hat
丸山忠久 0.935 0.308 0.414 1.514 0.018 0.012 278 701 1.01
豊川孝弘 0.934 0.292 0.4 1.459 0.013 0.009 478 582 1
塚田泰明 0.932 0.287 0.437 1.465 0.013 0.009 446 888 1
永瀬拓矢 0.93 0.276 0.456 1.45 0.018 0.013 219 552 1.02
藤井聡太 0.912 0.284 0.411 1.406 0.02 0.014 189 299 1

興味深いこととして、藤井聡太五冠(記事執筆当時)は強さが高く、勝負ムラが最も少ない。言い換えるとどの対局においても平均的に高いパフォーマンスを発揮しているということがいえるだろう。

グローバルな先手後手の差をモデルに埋め込んだモデル

次のモデルでは、先手・後手の差には全体的な傾向(具体的には、先手の方が若干有利)があるとして、その傾向をモデルに反映させるということをおこなってみたい。こうすることで、先手・後手のバイアスを除いて強さや勝負ムラを計算するということができるのではないかと考えている。

モデルは色々試行錯誤したが、以下のように定めた。

  • 棋士の強さをμ、実力のムラをσとして、対局時の実力を normal(μ, σ) として定める。
    • 棋士の強さμの分散(すなわち正規分布の分散)の事前分布を一様分布として定める。
  • 実力が高いほうが対局に勝利する。具体的には、実力の差をシグモイド変換した確率値 p に対して、 その確率 p で 1 が出るような試行であったとモデリングする(ベルヌーイ分布)。
    • この時に、実力の差に対して、先手勝ちの場合に δ、後手勝ちの場合に -δ の補正を加える。この補正項によって、先手の得を陽に扱って解析することができる。
      • この δ は-1〜1の一様分布に従うというようにここではモデリングする。
      • 例えばδを平均0の正規分布に従うとモデリングすることもできるが、そうするとδが0に近いほど確率が高くなるというバイアスが逆に乗ってしまうと考えられる。そうであると考えることもできるが、ここでは補正項の値は-1〜1の間でランダムに選ばれると仮定する。
with pm.Model() as model:
    # prior
    s_mu = pm.Uniform('s_mu',upper=300)
    mu = pm.Normal('mu', mu=0, sd=s_mu, shape=len_players)
    sigma = pm.Gamma('sigma', alpha=10, beta=10, shape=len_players)
    #delta = pm.Normal('delta', mu=0)
    delta = pm.Uniform('delta', lower=-1, upper=1)

    loser_perf = pm.Normal('loser_perf', mu=mu[battles.Loser-1], sd=sigma[battles.Loser-1], shape=len(battles))
    winner_perf = pm.Normal('winner_perf', mu=mu[battles.Winner-1], sd=sigma[battles.Winner-1], shape=len(battles))
    sente_perf = delta * battles.FirstWin
     
    diff = winner_perf-loser_perf+sente_perf
    p = pm.math.sigmoid(diff)
    z = pm.Bernoulli('z', p=p, observed=np.ones(len(battles)))
pm.traceplot(trace, var_names=['mu','sigma','delta'])

f:id:sharply:20220225184406p:plain

棋士の強さの平均(強い順)

mean sd hdi_3% hdi_97% mcse_mean mcse_sd ess_bulk ess_tail r_hat
藤井聡太 3.003 0.509 2.06 3.962 0.041 0.029 156 346 1.01
渡辺明 1.839 0.484 0.915 2.735 0.032 0.023 230 585 1.01
伊藤匠 1.814 0.468 0.963 2.741 0.023 0.016 430 727 1
永瀬拓矢 1.8 0.434 1.025 2.607 0.029 0.021 217 709 1.01
出口若武 1.662 0.477 0.78 2.577 0.028 0.02 289 755 1

棋士の勝負ムラの平均(大きい順)

mean sd hdi_3% hdi_97% mcse_mean mcse_sd ess_bulk ess_tail r_hat
渡辺和史 1.105 0.343 0.498 1.735 0.02 0.015 285 602 1.01
山本博 1.096 0.34 0.482 1.718 0.022 0.016 224 446 1.01
伊藤真吾 1.086 0.35 0.461 1.721 0.019 0.013 334 559 1
船江恒平 1.082 0.341 0.478 1.719 0.019 0.014 292 706 1
高崎一生 1.08 0.339 0.482 1.747 0.019 0.014 289 269 1.01

棋士の勝負ムラの平均(小さい順)

mean sd hdi_3% hdi_97% mcse_mean mcse_sd ess_bulk ess_tail r_hat
竹内雄悟 0.93 0.291 0.432 1.514 0.019 0.013 228 498 1.01
田中寅彦 0.929 0.297 0.44 1.511 0.017 0.012 290 659 1.01
藤井聡太 0.921 0.268 0.446 1.424 0.021 0.015 158 332 1.01
池永天志 0.912 0.294 0.411 1.467 0.02 0.014 203 376 1
永瀬拓矢 0.887 0.278 0.425 1.413 0.02 0.014 190 342 1

まとめ

まずは、各棋士の強さ及び勝負ムラについて調査した。次に、先後の差をモデルに組み入れることで、先後の差を除いた各棋士の強さ及び勝負ムラについて調査した。

次回は、モデリングに各棋士の先後ムラを組み込んでみたいと思う。