麻雀の強化学習をする その1(強化学習の仕組みを作る編)

気づいたらこのブログにCTFのことしか書いてないので本業っぽいこともやっていきたいと思い、面白そうなコンテストを探していたら RiichiLab-Mahjong AI Competitionというのを見つけたので、麻雀の強化学習を試してみることにした。

まずは手元で色々試してみて、将来的には勉強も兼ねて既存の強い麻雀AIの実装などを参考にしながら強いAIを作れたらいいなと思っている。

実装の概要など

今回はゲームエンジン以外の部分は自前で実装した。夏季休暇の5日間でやろうと思っていたらいつの間にか1か月経っていた(見通しの甘さ)

最初なのでとりあえず形だけ作っておいて、今後整理していく予定。

github.com

ゲームエンジン

麻雀のゲームエンジンを自作するのはとても時間がかかるので既存のもので使えそうなものを探してみたところ、Mjxというライブラリが使えそうだったのでインストールしてみた。Mjxは更新が止まっておりPyPI上のライブラリは壊れているようだったが、リポジトリから直接インストールできた。

pip3 install git+https://github.com/mjx-project/mjx

モデルと特徴量

モデルの改良は今後やる予定なので、とりあえず最初のモデルとして線形層を重ねただけのモデルを用意した。現状は盤面とアクションの情報から現在の状態価値と各アクションの行動価値を推論するモデルになっている。

Mjxでは"mjx-small-v0", "han22-v0", "mjx-large-v0"という3種類の特徴量セットが実装されており直接使用できるが、今回はこれを使わずに

ゲームエンジン -> 自作のBoard, Actionクラス -> 自作特徴量

という変換を実装した。 色々な手法を試したいというのと、できるだけ特定のゲームエンジンへの依存を少なくしたかったため(特にRiichiLabのコンテストではmjaiが使われているようなので、提出を考えるなら少なくとも将来的にmjaiでも動作させる必要がある)

盤面の特徴量は現時点では

  • 場風、自風
  • 自分の手牌+河
  • 相手の鳴き+河
  • シャンテン数

など最低限のものしか実装しておらず、リーチ状況や点数の状況などはまだ入っていない。

学習方法

基本的には自己対戦データを使用するが、麻雀ではランダムに行動するエージェントが和了できる確率は非常に小さいため、本当の意味での自己対戦だけではまともに学習できない可能性が高い。 よって、今回はモデルの推論によってプレイするActor3体+ShantenAgent(シャンテン数が小さくなる牌があれば捨てる)1体の牌譜を使って学習を行うことにした。つまり、オンポリシーとオフポリシーのエージェントが混在していることになる。

学習結果

手元のデスクトップPC(10コア20スレッド)で対戦しながら4時間ほど学習したところ、以下のような感じになった。

学習時間(横軸)と直近100半荘の最終スコアの移動平均(縦軸)

最初はShantenAgentが圧勝していたが次第にスコア差が縮まっていき、200分程度で大体同じ強さになっていることが分かる。 実際のところ、Actionの特徴量には捨てることでシャンテン数が減る牌の情報を加えているためShantenAgentの動きを学習して同等の強さになること自体は難しくなかったと思われる。

今後

とりあえず直近の目標としては、ShantenAgentに勝ち越すモデルを作りたい。

人間のプレイでも「シャンテン数を小さくするようにプレイする」という原則から外れることは基本的にはなく、そこに「リーチがかかったら安全牌を切る(=降りる)」「基礎的な牌効率」などが入ってくればある程度は強くなるのでは?と考えているが、現状の特徴量にはそれを学習するための情報がないので特徴量の整備から始めたいと思っている。 もしかすると、それらの情報をきちんと学習するためにはモデルの構造や報酬設計などを見直す必要もあるかもしれない。 より強くなるためには役を考慮することも重要だが、それは単に手牌を入力するだけではなく別途対策が必要になりそうな気がする(それぞれの役が成立する確率をサブタスクとして学習するとか)

まとめ

  • 麻雀の強化学習のために学習の仕組みを実装した
  • ShantenAgentと同等の強さまで学習できた
  • 次は特徴量の整備をしたい