TadaoYamaokaの開発日記

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

将棋AIの進捗 その60(固定プレイアウトで水匠5と互角になる条件)

dlshogiの棋力測定する際に、dlshogiの過去のバージョンとの対局だとレーティング差が実際よりも大きくなる傾向がある。
そこで、棋力測定では水匠5を加えてリーグ戦で連続対局している。

普段の棋力測定では、対局条件をフィッシャールールの持ち時間で対局を行っており、dlshogiの30ブロック(A100 1枚)と水匠5(32スレッド)を互角にするには、水匠5の持ち時間を10倍にするとだいたい互角になる。
参考:将棋AI実験ノート:30ブロックの学習 - TadaoYamaokaの開発日記

持ち時間を条件にすると、GPUやCPUが変わると、互角の条件が変わってしまうため、固定プレイアウト(固定ノード数)でも互角になる条件を調べた。

計測

水匠5を2500倍

まず、dlshogiのプレイアウト数(Const_Playout)を5万に固定して、水匠5のノード数(NodesLimit)を2500倍にして測定した。
dlshogiはV100 1枚、3スレッド
水匠5は、8スレッド、やねうら王 V7.0を使用した。

思考時間は、dlshogiは、1手約8秒、水匠5は、1手約30秒になる。

同一の棋譜になると棋力を正しく測定できないため、dlshogiは序盤32手まで、評価値にしきい値を設けた上でランダムにプレイするようにした。
これにより、dlshogiの棋力は少し下がる。

計測コマンド
python -u -m cshogi.cli /work/DeepLearningShogi/usi/bin/usi /work/YaneuraOu/bin/YaneuraOu-by-gcc --name1 pre33 --name2 suisho5-8thx2500 --options1 Const_Playout:50000,Random_Ply:32,Random_Cutoff:25,Random_Cutoff_Drop:1,PV_Mate_Search_Threads:1,C_init:127,C_base:27126,C_fpu_reduction:31,C_init_root:112,C_base_root:33311,Softmax_Temperature:140,Draw_Ply:320,UCT_NodeLimit:50000000,OwnBook:false,PV_Interval:0,DNN_Model:/work/model/model-pre33_resnet30x384_relu_b4096lr004-012.onnx,UCT_Threads:0,UCT_Threads8:3 --options2 NodesLimit:125000000,USI_Hash:10240,Threads:8,PvInterval:9999,ResignValue:10000,MaxMovesToDraw:320,NetworkDelay2:0 --draw 320 --games 1000 --pgn 20221209_pre33_vs_su5_po50k-01.pgn
測定結果
   # PLAYER              :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
   1 pre33               :    46.5   29.4    86.5     138    63     100   80   13   45     9
   2 suisho5-8thx2500    :   -46.5   29.4    51.5     138    37     ---   45   13   80     9

White advantage = 77.24 +/- 30.91
Draw rate (equal opponents) = 10.04 % +/- 2.67

dlshogiが、R+93.0になった。

水匠5を5000倍

次に、水匠5をdlshogiの5000倍にして測定した。

思考時間は、dlshogiは、1手約8秒、水匠5は、1手約80秒になる。

計測コマンド
python -u -m cshogi.cli /work/DeepLearningShogi/usi/bin/usi /work/YaneuraOu/bin/YaneuraOu-by-gcc --name1 pre33 --name2 suisho5-8thx5000 --options1 Const_Playout:50000,Random_Ply:32,Random_Cutoff:25,Random_Cutoff_Drop:1,PV_Mate_Search_Threads:1,C_init:127,C_base:27126,C_fpu_reduction:31,C_init_root:112,C_base_root:33311,Softmax_Temperature:140,Draw_Ply:320,UCT_NodeLimit:50000000,OwnBook:false,PV_Interval:0,DNN_Model:/work/model/model-pre33_resnet30x384_relu_b4096lr004-012.onnx,UCT_Threads:0,UCT_Threads8:3 --options2 NodesLimit:250000000,USI_Hash:10240,Threads:8,PvInterval:9999,ResignValue:10000,MaxMovesToDraw:320,NetworkDelay2:0 --draw 320 --games 1000 --pgn 20221210_pre33_vs_su5_po50k-01.pgn
測定結果
   # PLAYER              :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
   1 pre33               :    13.6   23.7    99.0     183    54      87   83   32   68    17
   2 suisho5-8thx5000    :   -13.6   23.7    84.0     183    46     ---   68   32   83    17

White advantage = 95.71 +/- 25.12
Draw rate (equal opponents) = 18.37 % +/- 3.01

dlshogiが、R+27.2になり、有意差なしとなった。
なお、序盤をランダムプレイありで対局しており、ランダムプレイなしの場合より誤差は大きくなる。

tanuki(Joyful Believer)とも対局

tanuki(Joyful Believer)が公開されたので、tanuki(Joyful Believer)とも7000倍の条件で対局してみた。

思考時間は、dlshogiは、1手約8秒、tanuki(Joyful Believer)は、1手約110秒になる。

   # PLAYER             :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
   1 pre33              :     6.8   43.6    33.5      63    53      62   29    9   25    14
   2 tanuki-8thx7000    :    -6.8   43.6    29.5      63    47     ---   25    9   29    14

White advantage = 139.72 +/- 46.11
Draw rate (equal opponents) = 15.70 % +/- 4.82

7000倍でほぼ互角になっている。

ノード数のカウント方法

dlshogiとやねうら王では、ノード数のカウント方法に違いがあり、やねうら王のノード数を約1/4.2にするとだいたい同じカウント数になる。
カウント方法を補正すると、約1190倍でほぼ互角になる。

まとめ

dlshogiの30ブロックと水匠5を固定プレイアウト(ノード)で対局した場合、水匠5(8スレッド)のノード数をdlshogi(1GPU、3スレッド)の5000倍にするとほぼ互角になることが分かった。
スレッド数が変わると条件も変わるので注意が必要である。
水匠5は少ないスレッド数で測定しているので、スレッド数を増やす場合は、スレッド数の平方根に比例して倍率を増やす必要がある。

なお、公開しているモデル(dr2_exhi)と水匠4が互角の強さの頃に、測定したときは、NPSの比は、約338倍であった。

dlshogiのNPSは、ブロック数を増やすごとに低下しているが、NPSが低くなると弱くなっていると思われることがあるが、この結果からそうではないことがわかる。

将棋AIの進捗 その59(第3回電竜戦で見つかった課題と対策検討)

第3回電竜戦では、水匠が準備した先手番角換わりの長手数の定跡が、dlshogiの盲点を突いていて定跡を抜けた時点で大差になっていた。

具体的には、以下の局面の85手目の8三角打が先手優勢であることをdlshogiが見つけらず、盲点となっていた。

8三角打から進んだ87手目まで進めると、9七玉で先手有利を見つけることができる。

85手目は、水匠5では8三角打が最善手で先手優勢であること見つけており、dlshogiと水匠5で評価が割れる局面となっている。

モデル精度の課題

モデル精度が上がればこのような盲点も減らすことができると考える。

自己対局でモデル精度を改善する場合は、自己対局中に現在見落としている手もある程度選ばせる必要がある。
自己対局中には、ルート局面で方策に一様ランダムなノイズを加えているため、見落としている手を選ぶ可能性がある。
ただ、その確率はかなり低い。

そこで、以下の改善方法を考えている。

エクスプロイターの学習

自己対局のみでは、盲点に気付く確率は低いため、盲点を探してそこを突く別の対局相手が必要になる。
そのためには、現在のモデルの盲点を突くことを学習する別のモデルを学習することが有効だと考える。

現在のモデルを固定して、ある程度ランダム性を持たせた相手を用意し、連続対局で棋譜を生成する。
ランダムで手を選ぶ際は、一定の割合で評価値が下がる手も選ぶようにする。
対局相手で使用するモデルは、その棋譜のみを学習する。
それを繰り返すことで、盲点を突くことに特化したモデルが学習できる。

対局で生成した棋譜は、メインのモデルの方の学習にも混ぜるようにすることで、盲点をふさぐことができる。

定跡作成の課題

上記の85手目の局面のように、数手先まで手を進めると反省する局面になるのを回避するには、さらに何手か戻って手を変える必要がある。
何手まで戻ればよいかは自明ではない。

現在、dlshogiの定跡作成では、dlshogiで事前に深く探索した手を登録することで定跡を作成している。
上記のようにdlshogiが気付いていない手は、数手先まで進めば気付いたとしても、定跡に登録済みの局面には反映することができない。
定跡の手を伸ばした後に、前の局面にさかのぼって定跡を修正する仕組みが必要である。

Min-Maxによる修正

やねうら王の定跡作成で実装されている作成済み定跡をMin-Maxで探索して評価値を修正する方法が、低コストで実現できる案である。
葉ノードの誤差がルートに伝播する問題があり、これまではあまり採用したくなかった。
しかし、定跡作成中に常に更新し続けることで、誤差は減らすことができるので、上記課題の対策として採用したいと考えている。

現在、dlshogiは、定跡を作成する手番側の手は、定跡を作成済みの局面では常に最善手を選択して手を伸ばしている。
一度間違った手が登録されると修正されない問題があるため、定跡に登録された最善手の代わりに、定跡をMin-Maxで探索した手を選択するようにする。
通常は最善手と手は同じになるはずだが、数手先に反省した局面があるとそれが反映されるはずである。
また、現在のdlshogiの定跡作成の最善手に絞って深く手を伸ばしていく特性も残すことができる。

まとめ

第3回電竜戦で見つかった、モデル精度と定跡作成の課題について、対処方法を検討した。
今後、これら2点の課題について改善に取り組むつもりである。
定跡については大会向けの対策になるが、dlshogi自体を強くするためにやはりモデル精度の向上の方を目指したい。

将棋AIの進捗 その58(訓練の省メモリ化と高速化)

現状のdlshogiのモデルの訓練の実装では、訓練データが多い場合にメモリを大量に消費する課題があった。

dlshogiのモデルの訓練は、訓練データをすべてメモリに読み込んで処理を行っている。
そのため、一度に学習できるデータはメモリに読み込める分に制限される。
それ以上のデータを学習する場合は、訓練データを分割して学習する必要がある。

また、1台のマシンでGPUを複数使用して同時に実験を行いたい場合、それぞれメモリを消費するため、メモリが不足する。

対策

メモリ消費を抑えるために、前処理を行ったデータをキャッシュファイルに出力して、キャッシュファイルをシークしながらランダムアクセスして読み込む機能を追加した。
OSがページファイル単位でメモリに読み込んでキャッシュするため、物理メモリに乗る容量であれば、ほとんど処理時間は変わらない。
また、複数の実験を同時に行う場合には、同一のキャッシュファイルを使用すると、OSが同じページファイルから読み込むのでメモリ消費を抑えられる。

高速化

キャッシュファイルから読み込む場合、IO待ちが発生するため、その間にCPUを遊ばせないように、ミニバッチを作成する際のデータ読み込みから特徴量を作成する一連の繰り返し処理をOpenMPで並列で処理するようにした。
キャッシュファイルを使用しない場合でも、特徴量作成処理がわずかにボトルネックになっていたため、同様にOpenMPで並列化を行った。
スレッド数2以上にしてもかえって遅くなったため、スレッド数は2とした。

測定

キャッシュ機能と並列化を実装して、学習時間を比較したところ、10ブロックモデル・4096バッチ・1000ステップ学習の平均学習時間(GPUはA100)は、

条件 平均学習時間(h:m:s)
キャッシュファイルなし(並列化なし) 0:03:36
キャッシュファイルなし(並列化あり) 0:03:27
キャッシュファイルから 0:03:32

となった。

並列化により、元のバージョンから4.01%速くなった。
キャッシュファイルから読み込む場合は、元のバージョンから1.85%速くなった。並列化ありでキャッシュファイルを使用しない方が速いが、元のバージョンより遅くはなっていないので良しとする。

使い方

train.pyのオプションで、--cacheでキャッシュファイルを指定すると、キャッシュファイルがない場合は作成され、キャッシュファイルがある場合はキャッシュファイルを使用する。
キャッシュファイルがない場合は、いったんデータをすべてメモリに読み込んでからキャッシュファイルを作成した後、メモリを解放してから、キャッシュファイルを使用する。

まとめ

1台のサーバで、複数実験を行う際にメモリ不足が起きていたため、キャッシュファイルから読み込むことで省メモリ化するオプションを実装した。
また、IO待ちが発生する対処として特徴量作成処理の並列化も実装したことで、キャッシュファイルを使用しない場合も4%ほど高速化された。

第3回世界将棋AI電竜戦 結果報告

週末(12/3,4)に開催された第3回世界将棋AI電竜戦に参加しました。

HEROZチームとして、「dlshogi with HEROZ 30b」と「dlshogi with HEROZ 20b」という2つのソフトでエントリしました。

大会の概要

世界将棋AI電竜戦は、コンピュータ将棋開発者の有志により立ち上げられたコンピュータ将棋の大会です。
毎年11月末ごろに開催されており、今年が3回目の開催で、34チームが参加しました。
予選を通過した上位10チームで総当たりのリーグ戦(A級リーグ)で優勝が争われました。

電竜戦では、人間による参加も可能で、人間はプレイ中にAIで検討することも可能です。
後で書きますが、この大会ではこれが優勝を分けることになりました。

大会の結果

A級リーグでは、「dlshogi with HEROZ 30b」が15勝3敗で準優勝となりました。
「dlshogi with HEROZ 20b」は13勝5敗で3位でした。

最終局

優勝争いとなった最終局では、後手番で千日手以上で優勝でしたが、先手番の水匠が用意した角換わりの長い定跡に嵌ってしまい、定跡を抜けた時点でdlshogiの評価値で先手+257の局面でそのまま逆転することなく負けてしまいました。

vsあらきっぺさん

3敗のうち1敗は、水匠とdlshogiを検討に使った人間による参加のあらきっぺさんに負けており、こちらも事前に準備された先手角換わりの作戦にやぶれています。
優勝した水匠の方では、角換わりの手順であらきっぺさんに操作ミスがあったということで千日手になっています。
dlshogi側が後手勝率の悪い角換わりを避けることができていないということなので、
今後の改善のテーマを与えてくれたと思って、結果については受け入れます。
ただ、結果のみが真実です。

2つのソフトの違い

ディープラーニングのモデルサイズが異なる2つのソフトで参加しています。
「dlshogi with HEROZ 30b」は、ResNet30ブロック384フィルタのモデルサイズで、
「dlshogi with HEROZ 20b」は、ResNet20ブロック256フィルタのモデルサイズです。

ブロック数とパラメータ数

ブロック数が増えるとパラメータ数は、比例して増えます。
フィルタ数が増えると、パラメータ数は、2乗で増えます。
30bのパラメータ数は、20bの3.375倍になります。

精度と探索速度

パラメータ数が増えることで、精度が大幅に向上しています。
その代わり、推論速度が遅くなり、NPSは、GPUにA100を使用した場合約1/3になります。

強さは、互角局面を使用した測定では、
同一探索数で、30bがR+163.1、
同一持ち時間では、30bがR+24.2で、30bがわずかに強いくらいです。

大会では、どちらが強いか確認したかったため、2つのソフトで参加しました。
結果は、それほど差はなかったようです。

今後の課題

先手角換わり定跡

今回の結果で、先手角換わり定跡は、dlshogiの探索のみで回避するのは難しいことが分かりました。
後手番の定跡で勝率の悪くない別の戦型にすることがとりあえずの対策になりますが、dlshogi自身を強くすることにはならないため、根本的な対処方法を考えたいと思っています。
モデルの精度を向上させて、正しく評価できるようにすることを目指すつもりです。
それには、何らかの学習方法の改善が必要になるだろうと思っています。

終盤優勢からの逆転

8回戦では、先手番でNNUE系ソフトのJoyful Believerと水匠にそれぞれ20bと30bが終盤優勢から逆転されて負けています。
終盤はNNUE系よりディープラーニング系の方が間違うことが多いというのは以前から言われていますが、
最近のdlshogiは終盤の精度もかなり上がってきています。
モデル精度を上げていくことで、こういうケースは徐々に減っていくはずだと思っています。

まとめ

大会に参加したことで、技術的にまだ解決できていない課題を見つけることができました。
根本的な改善となるような新しい手法を見つけたいと思っています。

運営の皆様、参加者の皆様、2日間長丁場大変お疲れさまでした。

DeepNash(R-NaD)でクーン・ポーカーを学習する

DeepMindArxivで発表したDeepNash(R-NaD)OpenSpielに実装されたので試してみた。
※2022年12月にScienceにも掲載されました。

DeepNash(R-NaD)

DeepNash(R-NaD)は、不完全情報ゲームで探索なしでナッシュ均衡に収束するモデルフリーの深層強化学習アルゴリズムである。
海外で人気があるStrategoで人間のエキスパートレベルに達している。

クーン・ポーカー

キング・クイーン・ジャックの3枚のカードで行う単純なポーカーで、混合戦略ナッシュ均衡の解が解析されている。
プレイヤーには1枚ずつカードが配られ、残り1枚は未使用で、交互にベットかパスかを選択する。
利得表は以下の通りである。最大3手で勝負が付く。

OpenSpiel

DeepMindが開発しているオープンソース強化学習フレームワークである。
様々なゲームと強化学習アルゴリズムが実装されている。
R-NaDも最近追加された。
クーン・ポーカーでのテストコードも用意されている。

テスト

用意されているテストコードでは、学習の結果は表示されないので、損失と平均報酬、学習した方策を表示するようにコードを追加した。

--- a/open_spiel/python/algorithms/rnad/rnad.py
+++ b/open_spiel/python/algorithms/rnad/rnad.py
@@ -928,6 +928,9 @@ class RNaDSolver(policy_lib.Policy):
         "actor_steps": self.actor_steps,
         "learner_steps": self.learner_steps,
     })
+    logs['rewards'] = timestep['actor']['rewards'][9,:,:].mean(axis=0)
+    logs['states'] = timestep['env']['obs'][0]
+    logs['policy'] = timestep['actor']['policy'][0]
     return logs
--- a/open_spiel/python/algorithms/rnad/rnad_test.py
+++ b/open_spiel/python/algorithms/rnad/rnad_test.py
@@ -28,8 +28,12 @@ class RNADTest(absltest.TestCase):

   def test_run_kuhn(self):
     solver = rnad.RNaDSolver(rnad.RNaDConfig(game_name="kuhn_poker"))
-    for _ in range(10):
-      solver.step()
+    for _ in range(10000):
+      logs = solver.step()
+      data = {}
+      for state, policy in zip(logs['states'], logs['policy']):
+        data[int(state.dot(np.arange(11).T))] = policy
+      print(_, logs['loss'], logs['rewards'], data[2], data[3], data[4])

テスト結果

10000ステップ学習を行った結果、損失と平均報酬、1手目の方策は以下の通りとなった。

損失


平均報酬(移動平均100)


1手目の方策
カード アクション 確率
ジャック パス 0.824698
ジャック ベット 0.175302
クィーン パス 0.687148
クィーン ベット 0.312852
キング パス 0.478646
キング ベット 0.521354

※プレイヤーは、自分のカードのみで判断することに注意

考察

損失は、500ステップくらいで下がりきっている。

平均報酬は、理論値は、−1/18(≒−0.055)になる。
グラフを見ると、−0.04あたりに収束しており、理論値よりは高めの値になっている。

1000ステップ後に学習された1手目の方策は、キングのベットの確率/ジャックのベットの確率=2.97となっており、最適戦略の3に近づいている。
しかし、クィーンは常にパスが最適戦略であるが、パスの確率は0.687148で1からは離れている。

まとめ

OpenSpielに実装されたDeepNash(R-NaD)をクーン・ポーカーで試してみた。
結果、最適戦略に近づくことが確認できたが、理論値には収束しなかった。
ただし、モデルの構成(テストコードは中間層2層のMLP)やハイパーパラメータの調整で変わる可能性はある。

DeepNashは、行動空間が広く、手数の長い不完全情報ゲームでも適用できるアルゴリズムであり、適用できるゲームは広そうである。
大会が開催されているガイスターAIにも適用できそうなので、試してみたい。

余談

テストしているときにOpenSpielのR-NaDの実装にバグを発見したので、issueで報告したら修正された。
github.com

将棋AI実験ノート:30ブロックの学習

dlshogiのモデルサイズは、10ブロック192フィルタから初めて、精度が飽和するたびに、15ブロック224フィルタ、20ブロック256フィルタと段階的に大きくしている。

参考:


直近の大会(第32回世界コンピュータ将棋選手権、第3回電竜戦TSEC)では、20ブロック256フィルタのモデルを使用していた。

その後も、20ブロック256フィルタのモデルで、強化学習を続けていたが、精度が飽和したため、並行して30ブロック384フィルタのモデルの学習も行っている。

20ブロック256フィルタのモデル

30ブロックモデルとの強さを比較するために、まずは、20ブロック256フィルタのモデルが、第32回世界コンピュータ将棋選手権以降どれくらい強くなったか測定を行った。

持ち時間5分2秒加算で、互角局面集24手目から対局した結果は、以下の通りである。

pre28が第32回世界コンピュータ将棋選手権のモデル、
pre38が精度が飽和するまで学習した最新の20ブロックである。

参考として、公開している15ブロック224フィルタのモデル(dr2_exhi)もリーグに加えて測定した。
また、基準として、水匠5(32スレッド)をリーグ加えている。強さをそろえるため水匠5は、持ち時間を10倍にした。
dlshogiは、A100 1GPU、3スレッドである。

   # PLAYER             :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
   1 pre38              :    66.0   25.4   224.5     380    59      96  192   65  123    17
   2 pre28              :    28.5   32.4   122.0     255    48      63  103   38  114    15
   3 suisho5-32thx10    :    21.2   26.6   186.5     375    50     100  145   83  147    22
   4 dr2_exhi           :  -115.7   45.7    38.0     132    29     ---   24   28   80    21

White advantage = 10.93 +/- 13.46
Draw rate (equal opponents) = 19.38 % +/- 1.71

20ブロックの最新モデル(pre38)は、公開している15ブロックのモデルからは、R+181.7、
第32回世界コンピュータ将棋選手権のモデル(pre28)からは、R+37.5になっている。

精度

テストデータにfloodgateのR3500以上の棋譜からサンプリングした856,923局面(重複なし)を使用した場合の精度は以下の通りである。

モデル モデルサイズ 方策正解率 価値正解率
dr2_exhi 15ブロック224フィルタ 0.523406 0.7607527
pre28 20ブロック256フィルタ 0.5376619 0.768036
pre38 20ブロック256フィルタ 0.5410133 0.7686995

15ブロックから20ブロックで精度が大きく向上し、20ブロックモデルは、第32回世界コンピュータ将棋選手権のモデルからはそれほど精度が向上していない。

30ブロック384フィルタ

20ブロックのモデルで強化学習したデータを使って学習している30ブロック384フィルタのモデルの精度が20ブロックを上回ったので、同じ持ち時間で強くなっているか測定した。

持ち時間400秒2秒加算で、互角局面集24手目から対局した結果は、以下の通りである。

基準として、水匠5(32スレッド)をリーグ加えている。強さをそろえるため水匠5は、持ち時間を10倍にした。
dlshogiは、A100 1GPU、3スレッドである。

pre38が精度が飽和するまで学習した最新の20ブロック、
pre33が学習途中の30ブロック384フィルタのモデルである。

   # PLAYER             :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)    W    D    L  D(%)
   1 pre33              :    14.0   16.5   353.5     667    53      89  301  105  261    16
   2 suisho5-32thx10    :    -3.9   16.4   324.0     659    49      67  253  142  264    22
   3 pre38              :   -10.2   16.4   317.5     664    48     ---  261  113  290    17

White advantage = 18.01 +/- 9.64
Draw rate (equal opponents) = 18.15 % +/- 1.25

30ブロックのモデルが、20ブロックのモデルに対して、同じ持ち時間でも、R+24.2になっている。


同じプレイアウト数(30000)、水匠5は400倍の1200万だと、

   # PLAYER             :  RATING  ERROR  POINTS  PLAYED   (%)  CFS(%)     W    D     L  D(%)
   1 pre33              :   178.3   14.9  1089.0    1354    80     100  1028  122   204     9
   2 pre38              :    15.2   13.4   708.0    1352    52     100   642  132   578    10
   3 suisho5-32thx10    :  -193.5   15.6   230.0    1348    17     ---   177  106  1065     8

White advantage = 22.08 +/- 9.29
Draw rate (equal opponents) = 11.67 % +/- 0.80

30ブロックのモデルが、20ブロックのモデルに対して、R+163.1となる。

精度

テストデータにfloodgateのR3500以上の棋譜からサンプリングした856,923局面(重複なし)を使用した場合の精度は以下の通りである。

モデル モデルサイズ 方策正解率 価値正解率
pre38 20ブロック256フィルタ 0.5410133 0.7686995
pre33 30ブロック384フィルタ 0.5598156 0.7760576

30ブロックのモデルの精度は、20ブロックに対して大幅に向上している。
まだ学習途中であり、さらに精度を向上できる見込みである。

学習時間

バッチサイズ4096、1000ステップの平均学習時間を比較すると以下の通りである。

モデルサイズ 時間(h:m:s)
20ブロック256フィルタ 0:07:25
30ブロック384フィルタ 0:19:44

学習時間は、約2.66倍になっている。

まとめ

dlshogiのモデルサイズを30ブロックにした場合、20ブロックに対してどれくらい強くなるか測定を行った。
その結果、同じ持ち時間で20ブロックを上回ることが確認できた。
20ブロックのモデルの精度は飽和しているが、30ブロックはまだ学習途中であり、さらに強くできる余地がある。
今のところ経験的には、モデルサイズは大きいほどよいという法則が成り立ちそうである。

ただし、学習時間は、約2.66倍になっているため、いきなり大きいモデルを学習するより段階的に大きくしていくのが効率がよいと考える。
また、より大きなモデルサイズを学習するには、同じバッチサイズではGPUメモリが不足するため、学習の工夫も必要になってくる。

アニメデータセットでリコメンドを試す(NMF編)

少し古いがWEB+DB PRESS Vol.129のレコメンドエンジンの記事を読んでいて、勉強のために記事とは異なるデータセットで試したいと思った。

記事ではMovieLensのデータセットで使っているが、アニメレビューのデータセットを使って試してみた。

モチベーション

見たいアニメを探すときに、全体で人気の高い作品よりも、自分に似たユーザが高く評価している作品を探せると、より自分に合った作品を探すことができる。
そのような場合に、レコメンドエンジンを使うと、多数のユーザのレビューの情報から、自分に合った作品を探すことができる。

データセット

アニメレビューのデータセットを探したところ、Kaggleに「Anime Recommendations Database vol.2」というデータセットを見つけた。
Anime Recommendations Database vol.2 | Kaggle

MyAnimeList.netからスクレイピングして作成したデータセットのようだ。
作品ごとのレーティングと、ユーザごとの作品のレーティングが含まれる。

ユーザごとの作品のレーティングには、11,039,694件のデータがある。

アルゴリズム

レコメンドエンジンのアルゴリズムには、古典的には協調フィルタリングがあるが、データ数が多いと次元の呪いの問題に合い計算量が指数的に増加してしまう。

次元圧縮を行う方法に、行列分解により行列を近似する非負値行列因子分解(Non-negative Matrix Factorization; NMF)がある。
行列分解による方法は、2008年のNetflix Prizeで優勝したアルゴリズムで使われてた方法である。

テストデータ

データセットのデータで評価しても面白くないので、自分で選んだ作品を元に、新しい作品を推薦できるか試すことにした。

傾向が似ていそうな以下のアニメを選択し、これらのレーティングを10とした。

anime_id title
8769 Ore no Imouto ga Konnani Kawaii Wake ga Nai
5680 K-On! Music,Slice of Life,Comedy,School
1195 Zero no Tsukaima
21273 Gochuumon wa Usagi Desu ka?
31953 New Game!

実装

データ準備

データセットのユーザごとの作品のレーティングを読み込み、テストデータを追加する。
テストデータのユーザIDは使用されていない0とする。

import pandas as pd
import numpy as np

ratings = pd.read_csv('ratings.csv')
myratings = pd.DataFrame([{'user_id': 0, 'anime_id': anime_id, 'rating': 10} for anime_id in [31953, 8769, 5680, 1195, 21273]])
ratings = pd.concat([ratings, myratings])
疎行列作成
from scipy.sparse import csr_matrix

matrix_data = csr_matrix((ratings['rating'], (ratings['user_id'], ratings['anime_id'])))
NMFによる行列分解

潜在因子の次元に圧縮して因子行列を得る。

from sklearn.decomposition import NMF

latent = 50
nmf = NMF(n_components=latent)
# ユーザ因子行列
W = nmf.fit_transform(matrix_data)
# アイテム因子行列
H = nmf.components_

処理時間は、3分くらいかかった。

行列復元

因子行列を復元することでレコメンドのためのスコアデータを得る。

WH = np.dot(W, H)
レコメンド

テストデータ(ユーザID=0)の行をソートして、上位10件の作品を取得する。

top10 = np.argsort(WH[0])[::-1][:10]
animes[animes['anime_id'].isin(top10)]

結果

rank anime_id title rating
1 66 Azumanga Daioh 7.97
2 355 Shakugan no Shana 7.5
3 356 Fate/stay night 7.32
4 1195 Zero no Tsukaima 7.3
5 1691 Kaze no Stigma 7.24
6 1840 Zero no Tsukaima: Futatsuki no Kishi 7.47
7 1887 Lucky☆Star 7.76
8 2787 Shakugan no Shana II (Second) 7.6
9 2966 Ookami to Koushinryou 8.26
10 2993 Rosario to Vampire 6.82

テストデータに選んだ作品のシリーズや、ラノベ作品が比較的多く選ばれた。

テストデータ2

別のデータでも試してみた。

anime_id title
16498 Shingeki no Kyojin
6336 Mobile Suit Gundam Unicorn
30 Neon Genesis Evangelion
1575 Code Geass: Hangyaku no Lelouch
14719 JoJo no Kimyou na Bouken (TV)
結果

上位10件は、以下のような結果になった。

rank anime_id title rating
1 20 Naruto 7.92
2 30 Neon Genesis Evangelion 8.32
3 31 Neon Genesis Evangelion: Death & Rebirth 7.45
4 32 Neon Genesis Evangelion: The End of Evangelion 8.52
5 33 Kenpuu Denki Berserk 8.49
6 43 Koukaku Kidoutai 8.29
7 47 Akira 8.17
8 199 Sen to Chihiro no Kamikakushi 8.81
9 227 FLCL 8.03
10 269 Bleach 7.8

今度は、だいぶ作品の傾向が変わり、少年漫画系の作品が多く選ばれている。
単にレーティングが高い作品が選ばれているわけでないことが確認できた。

まとめ

アニメレビューのデータセットを使ってレコメンドを試してみた。
レコメンドエンジンのアルゴリズムには行列分解による方法(NMF)を使った。
自分でテストデータを作成して、傾向が似た作品が選ばれるかを確認したろこと、ある程度傾向が似た作品が選ばれることが確認できた。

次は、深層学習を使ったレコメンドを試したい。