Paoの技術力を磨くブログ

機械学習やブロックチェーン等の技術を身に付けていくブログです。

深層強化学習を用いた一人麻雀AIを作ってみた

最近はブロックチェーンまわりばかりだったが、 前々からやっていた麻雀AIの強化学習について一旦まとめておく。

麻雀好きの人から機械学習興味ある人まで読めるよう書いたみたけど、どうでもいいとこは飛ばしながら、分からないとこあれば気軽にコメントください。

概要

  • 一人麻雀のAIを深層強化学習で作ってみた
  • 自分が持っている牌すら選択できない状況から、ある程度有望そうな牌を選べるところまで学習した
  • 最終的には、18順の一人麻雀でテンパイ率60%弱までになった
  • ただ、他の手法に比べるとかなり弱く、まだまだこれからといった感じ

背景

麻雀AIでは、「どの牌を切るか?」「鳴くべきか?」「リーチすべきか?」など多くの判断が必要だが、 その中で基本となるのは「どの牌を切るか?」といった部分であり、点棒状況や相手の手を考慮しないシンプルなものを一人麻雀という。

※ここでいう一人麻雀は副露しないものとして以降扱います。

AIというと最近流行りの深層学習(ディープラーニング)のイメージがあるが、 麻雀AIでは研究はされているが、実用的にはあまり使われてない。

理由としては、モンテカルロ法とかで十分強いのと、そもそも教師あり学習自体、教師となる人間を真似するだけで超えられないといったものがある。
(有名な麻雀AI 爆打モンテカルロ法教師あり学習(単純パーセプトロン)を使っているはず。)

後者を解決する方法として、強化学習というものがある。
これは真似すべき正解はなく、AIが自ら試行錯誤して学んでいくという方法である。
そしてこれに深層学習を応用したものを深層強化学習という。
AlphaGOでも使われていたり、マリオカートなんかも学習している例もある。

強化学習だと、AIが自分で学んでいくので、人間を超えることも可能になる。
というワクワクする方法なので、深層強化学習の麻雀AIを作ってみた。

手法

深層強化学習の中で、A3Cという手法を用いた。
詳しくは説明しないが、2016年頃に出たもので、強化学習の中ではメジャーな手法。
Actor-Criticという状態価値と方策を同時に学習するところが、AlphaGoZeroとも共通であり、麻雀にも有効と何となく考えたため採用した。

ネットワーク

状態価値関数と方策関数で前半は共通のネットワークを用いた。
全て全結合のネットワーク。
畳み込みニューラルネットも試していたが、学習に時間がかかるのと、強化学習では全結合することが多いため、この形とした。
ざっくりとした図は下の通り。
特徴量の数は180、共通部分の中間層のユニット数は200、状態価値と方策のユニット数は100、方策の出力数は34(牌の種類数)。

f:id:go5paopao:20180726003912p:plain

特徴量

シンプルに34種類の牌それぞれが0~4枚のうち何枚手牌に含まれているか?を34x5=170。 とする予定だったが、ここ少し間違えた。
字牌も9種類分用意していて、36x5=180の特徴量としてしまった。 (常に0の特徴量が20次元ある)

あとは手動の特徴量(メンツがいくつあるか等)も用意してもよかったが、深層学習でその辺は自動で抽出して欲しかったので入れてない。
それで学習してくれた方が面白いし!

報酬の設計

ここ少し工夫をした。 そもそも報酬というのは、強化学習で試行錯誤していく中で何を成功体験、失敗体験とするかを決めるもの。
ゲームでいうと、相手に勝ったときとか、ステージをクリアした時にプラスの報酬を設定し、ミスをしたときにマイナスの報酬(罰)を設定する。

麻雀でシンプルに考えると、 和了したときにプラスの報酬を与えるべきだが、これだけでは上手くいかない。
強化学習では最初はほぼランダムに行動するが、34種類の牌をランダムに選んでも、天和でもない限り、ほぼ和了にたどり着くことがないためである。
そうすると、いつまで経っても学習が進まない。
例えるなら、歩くこともできない子供に野球でホームランを打ってと課題を与えるような感じ。

そこで、今回はステージを段階的に分けて、学習させる。
野球の例でいうと、まずは歩き方をおぼえる、次にバットの握り方、振り方、ボールの当て方、遠くへの飛ばし方と順に教えるようなイメージ。

今回は、以下のステージに分けて学習を行った。

  • ステージ0: 3巡目まで持っている牌を選択できたら報酬
  • ステージ1: 7巡目まで持っている牌を選択できたら報酬
  • ステージ2: 15巡目まで持っている牌を選択できたら報酬
  • ステージ3: 流局まで持っている牌を選択できたら報酬
  • ステージ4: イーシャンテンまで進めたら報酬
  • ステージ5: テンパイまで進めたら報酬
  • ステージ6: 和了できたら報酬

マイナスの報酬は共通して、持っていない牌を選択したときとした。

各ステージで報酬をもらえる成功率が一定の割合を超えたら次のステージに進むようにした。

本来であれば、和了だけでなく、和了時の点数まで報酬に含めるところまですべきだが、 簡略化のためそこまではしていない。

アカデミックな観点でいうと、報酬の段階を手動で設計するのは、あまり評価されない気がするが、他にいい方法が思いつかなかった。 もしいい方法を知っている方がいたら教えてください。

ソース

強化学習フレームワークとしてChainerrlを利用。
強化学習アルゴリズムの実装とか結構めんどくさいので、かなり便利。

ソースはこちら

github.com

学習結果

結論

ステージ5(テンパイしたら報酬)で、60%くらいまでいったところで学習が収束してきて終了した。

学習過程

ステージ4まで

f:id:go5paopao:20180726003920p:plain

横軸が試行回数、縦軸が成功率(報酬をもらえる割合)になっている。
ステージ0~3の成功とは、規定の巡目まで持っている牌を選択し続けたこと、ステージ4はイーシャンテンになったことを指す。
各ステージで右肩上がりになっていて、ステージが上がるとまた成功率が下がり、そこから右肩上がり、、となっているのがわかる。
ステージ4のクリア成功率を0.7としていたので、約100万回でステージ4までクリアした。

ステージ5

f:id:go5paopao:20180726003924p:plain

成功率、すなわちテンパイ率が最初は上がっているが、徐々に収束しているのがわかる。
試行回数について、ステージ4までよりもかなり大きく最終1000万回まで行った。
これ以上、上がりそうになく、コンピュータの電気代も馬鹿にならないのでここで終了した。

実際にどう打つか

学習したモデルで実際にどういった選択をするかを見てみた。
天鳳鳳凰卓で打っている自分の批評も踏まえながらw

というか、本当にテンパイ率60%なのか疑問なくらい残念な打牌です。
適当なルールベースのほうが全然マシです。

画像作るの大変なのでシミュレーションで打った手を表示します。

2m7m8m3p4p7p8p8p1s南北発中中 dahai:北
南か北切っておけばいいので、これは問題なし


2m7m8m3p4p7p8p8p1s6s南発中中 dahai:6s
???まずは字牌切ってほしいところ。6sならせめて1sでしょ。。


2m2m7m8m3p4p7p8p8p1s南発中中 dahai:発
南のほうがといいたいところだが、点数考慮してないので南でも発でも問題なし。


2m2m7m8m3p4p7p8p8p1s南発中中 dahai:発
裏目ったらしい。どんまい。


2m2m5m7m8m3p4p7p8p8p1s南中中 dahai:2m
これはちょっとないかな。。1sか南切りでしょ。 門前のみってこと考えると、3対子は微妙なので対子落としは悪くないけど。


2m5m5m7m8m3p4p7p8p8p1s南中中 dahai:2m
すでに1枚落としてるし、まぁいいでしょう。


5m5m7m8m3p3p4p7p8p8p1s南中中 dahai:1s
南のほうがいいけど、1sは悪くない


5m5m7m8m3p3p4p7p8p8p9s南中中 dahai:中
早く南を切ってくれ。。南以外であれば中は悪くないけど。


5m5m7m8m3p3p4p7p8p8p3s9s南中 dahai:3s
3sはないですね。。9sか南か中のどれかにしてほしい。


5m5m7m8m3p3p4p4p7p8p8p9s南中 dahai:中
ようやく中切ったか。あとは南。


5m5m7m8m3p3p4p4p7p7p8p8p9s南 dahai:南
待ってました南切り。これで手牌がすっきりした。


5m5m7m8m9m3p3p4p4p7p7p8p8p9s dahai:3p
9sを切ってください。9sが9mだったら3pはかなりあり。


5m5m7m8m9m3p4p4p7p7p8p8p4s9s dahai:4s
4sは切ってくれるのね。あとは9s切ってください。。


5m5m7m8m9m3p4p4p7p7p8p8p9s9s dahai:9s なぜ9s重なったところで9sを切るのか。。。


5m5m7m8m9m3p4p4p6p7p7p8p8p9s dahai:3p この3pはないですね。。9s切らないならせめて4p


5m5m7m8m9m4p4p6p7p7p8p8p9s発 dahai:発
これはOK


5m5m7m8m9m4p4p6p7p7p8p8p9s白 dahai:白
これもOK


5m5m7m8m9m4p4p6p7p7p8p8p7s9s dahai:7s
なぜ9sだけ切らないのか。。。


考察

いくつか今回うまく行かなった原因や改善ポイントについてまとめます。

特徴量設計がうまくいっていない

特徴量で不要な次元数を与えてしまったというミスも含めて、ここを改善すればもう少しましになると思います。
まだメンツやシュンツなどの特徴量を入れなくても、せめて山にいる可能性がある枚数を入れるとかすればましになると思います。

強化学習単体では限度がある

強化学習だけで強いAIを作るのは難しい可能性があります。 AlphaGOでも強化学習結果をそのまま使うのではなく、その結果をモンテカルロ木探索の中で使うといったやり方をしています。
なので、麻雀でも強化学習の結果をモンテカルロに活かすようなことをしたほうがいいかもしれないです。

強化学習部分の改善

強化学習における報酬の設定の仕方、利用する手法など改善の余地はあります。
ゲームによって向き・不向きもあるみたいなので、もう少し強化学習を調査して改善したいと思います。

深層学習部分の改善

今回の深層学習部分のネットワーク構造はかなり適当に全結合をしたものです。
畳み込み層を入れるなどのネットワーク構造の改善やその他の学習改善手法を取り入れることで精度が上る可能性があります。

プログラムにバグが有る可能性

上の例で9sを全然切らなかったことを考えると、報酬を決めるときのシャンテン数計算とか、特徴量作る部分とか我ながら疑いたくなります。
改めてバグがないか見直す予定です。

まとめ

今回は深層強化学習を使った麻雀AIを作ってみました。
まだまだ弱いけれど、教師データもなく、持っている牌すら選択できない状態から考えると、強化学習の可能性は示せたかなと思います。

今後もっと実用的になるように改善していく予定です。