【麻雀AI】天鳳鳳凰卓の9年分のプレイをCNNを用いたNNモデルに学習させてみた

# はじめに
[ネット麻雀天鳳](https://tenhou.net/)における最上レベルの卓、鳳凰卓。
ネット麻雀天鳳では新人（十級）〜天鳳位（十一段）の21段階でプレイヤのレベルが段位分けされており、その中でも七段以上のみが参加を許されるフィールドが鳳凰卓です。
分布としてはアクティブユーザの上位0.36%、初段以上だけで数えても上位3.4%の猛者たちが集う場所になっています。

今回この鳳凰卓での2011年から2019年のプレイを設計したニューラルネットワーク（以下NN）モデルに学習させてみたところ打牌一致率70.4%、鳴き一致率93.2%を記録するものが出来たので、今回それをどうやって作ったか、実際にどんな挙動をするのかを紹介していきます。

動作環境~~（コードは出てこないけど）~~

- Python 3.8.6
- TensorFlow 2.3.1

技術に興味ねーよ！！！挙動だけ見せろ！！！って方は[実際のプレイ](#実際のプレイ)までジャンプ。

コードは出てきません。
概要説明と解説がほとんどです。
そのぶん機械学習わかんねえよ！って方にもある程度わかるように説明したつもりです。

# NNモデルの設計 
今回は自分の手牌など牌の状態に関するところはConvolutional Neural Network（以下CNN）で処理し、その出力結果とその他雑多な情報（今何局目か、とか他家がリーチしているかの情報）を結合させて最終的にMulti Layer Perceptron（以下MLP、Denseという呼び方の方が一般的？）に処理させています。


Tensorflowが出力してくれたモデルの概要図はこんな感じ。
![ryujin_model.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550388/ce64c491-4305-26ee-5a6c-74e11b04fc3f.png)

色枠の説明
↓入力層

|色|説明|
|---|---|---|
|赤　 | 萬子の情報
|青　 | 筒子の情報
|黄緑 | 索子の情報
|紫　 | 字牌の情報
|黄　 | 出た牌の情報（鳴き判断に使う）
|茶　 | その他雑多な情報（今何局目とか）


↓出力層

|色|説明|
|---|---|---|
|水色　 | 打牌選択
|ピンク | 鳴き選択

赤、青、黄緑枠とつながっている灰色点線枠部分はkerasのFunctional APIを使うとTensorflowがうまく出力してくれず、~~揉み潰して~~簡略化して表現してしまうみたいなので左の方にある灰色点線枠の物と同じ物がそのままあると見てもらえれば良いです。
入力が上からきて、下に出力していくイメージですね。

でもってTensorflowが出力してくれるNNモデルのパラメータ数がこんな感じ。

``` model.summary()
==============================
Total params: 2,388,950
Trainable params: 2,381,186
Non-trainable params: 7,764
______________________________
```

NNモデルとかパラメータ数とかよくわからんわという方向けにすごいテキトーな説明をすると、今紹介したNNモデルには2,381,186個の変数（重みとかよく言われるもの）があるということを表していて、この変数に格納されている値が学習によって調整されていきます。
そしてそのよしなに調整された変数（重み）を使って入力からきた数値をゴニョゴニョ計算するとあら不思議、出力として何らかの値（今回で言うとプレイヤがとるべき行動）を出してくれる訳ですね。

# 入力、出力の設計
では実際にどんな形式で入力を与えて、どんな出力させるように設計したかを説明します。
と言っても全部説明してるとキリがないのでキモになるCNNの入力（赤、青、黄、紫枠部分）と打牌選択の出力（水色枠部分）だけを説明します。

こんな手牌があったとします
![example.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550388/605e702d-020a-ddfc-11f9-77a5bb50348d.png)
この手牌の情報を何とかしてNNモデルに入力できるように変換したい訳ですが、今回はこんな感じにしました（察して）
![convert.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550388/9f1d397d-8483-03fb-a62d-e11d35ceb822.png)

それぞれ牌の枚数の情報を2値画像に変換したわけです。
完全に同じではないですがこれと似た要領でそれぞれの種類の牌について、「どの牌が何枚見えているか」、「他プレイヤの安全牌」、「ドラが何か」などなどを表現し画像を重ねて最終的には3次元配列にして、NNモデルの入力としました。

そしてそれに対応する出力は鳳凰卓の人間のプレイヤが実際に選んだ手を出力として与えます。
表現する方法としては長さ38の配列で表現します。
例として上の手牌だときっと筒子の5が切られるでしょうからこんな感じになりますね。

```
　　  0 1 2 3 4 5 6 7 8 9
萬子 [0,0,0,0,0,0,0,0,0,0,
筒子  0,0,0,0,0,1,0,0,0,0,
索子  0,0,0,0,0,0,0,0,0,0,
字牌  0,0,0,0,0,0,0,0]
```

筒子の5に対応する部分にだけ1が立っています。
この入力と出力のセットをひたすらNNモデルに学習させると、NNモデルは鳳凰卓民の真似をするように出力を覚えていきます。

# 使ったデータとベンチマーク
冒頭でもお話ししましたが、天鳳鳳凰卓の9年分（2011年〜2019年）のプレイログを学習に使いました。

総局面数は約6億局面
つまり入力と出力のセットが6億個あり、それを学習に使いました。

そしてテストには学習に使っていないゲームからランダムに1局面を選ぶという作業を2万半荘について行うことで2万局面分のテストデータを用意し、NNモデルの選択と実際の鳳凰民の選択がどれくらい一致するかを判定したところ、打牌選択については**70.4%**、鳴き選択については**93.2%**の一致率が出ました。

鳴きについてはなぜこんなに高い一致率が出ているのか正直謎です。
もしかしたらコードが間違っている可能性が多分にあるのでご注意ください。

ルール上鳴ける局面で明らかに鳴かない局面の方が全体の内の多くを占めると思うので、そこをきっちり当てさえすればそれだけで高い一致率が出てしまうというのはあるかもしれません。
今回のテストデータの内訳を見てみたところ、20000局面中鳳凰民が実際に鳴いた局面は1860局面しかなくAIがひたすら鳴かないという選択をするだけでも一致率が約91%は出てしまう計算になるので、違う評価方法が必要かもしれませんね。

# 実際のプレイ
お待たせしました！ジャンプしてきた人も良いジャンプ力でした！

天鳳牌譜エディタでみられるようにしたAIの実際のプレイが[こちら](https://tenhou.net/5/#json=%7B%22title%22%3A%5B%22%22,%22%22%5D,%22name%22%3A%5B%22%22,%22%22,%22%22,%22%22%5D,%22rule%22%3A%7B%22aka%22%3A1%7D,%22log%22%3A%5B%5B%5B0,0,0%5D,%5B25000,25000,25000,25000%5D,%5B33,17,12,14,13%5D,%5B39,21,25,28,31%5D,%5B17,12,31,36,46,12,26,24,41,15,38,36,29%5D,%5B52,24,16,23,13,37,38,43,34,17,22,14%5D,%5B29,31,41,46,38,37,38,43,13,17,%22r36%22,14%5D,%5B32,43,36,42,26,46,37,41,18,31,16,42,17%5D,%5B29,21,19,36,33,23,19,11,34,16%5D,%5B29,21,41,46,43,23,26,11,36,31%5D,%5B14,45,37,32,47,28,11,14,18,34,34,13,46%5D,%5B37,16,29,21,13,%2237p3737%22,11,45,%2213p1313%22,15,43,25%5D,%5B11,45,46,21,47,29,11,28,45,32,43,14%5D,%5B41,51,23,22,39,23,11,12,19,28,32,22,42%5D,%5B29,27,26,21,15,53,35,43,47,24,38,35%5D,%5B41,39,19,42,32,29,12,11,47,%22r43%22,38%5D,%5B%22%E4%B8%8D%E6%98%8E%22%5D%5D%5D%7D)。（別ページに飛びます）
結構いい感じに打ってくれてませんか？

注意点ですが、今回AIにリーチ判断は学習させておらず、聴牌すると強制リーチにしています。
この部分は目下開発中なので完成したらまたお披露目することがあるかもしれません。

# あとがき（苦労話）
ここまで来るまでにやったことですが、

天鳳から牌譜をダウンロードしてくる
↓
ダウンロードしてきた牌譜の仕様を理解する
↓
学習データの設計をしてデータを作るコードを書く
↓
NNモデルを設計、構築する
↓
学習させてみる
↓
遅すぎて僕の寿命がきてしまうところだったのでCythonで高速化
↓
ひたすら学習とデバッグ
↓
AIがプレイできるゲーム環境を作る
↓
AIのプレイを天鳳牌譜エディタでみられる生テキストを出力するようにする

と、ざっくりと分けてもそこそこ重い作業がこれくらいありました...
元々大学でゲーム情報学の研究をしていたのですが麻雀の研究ってあまりみなかった気がするのもこの辺の壁が無駄に高くて邪魔をしているのかなーと勝手に思ったり。

今回天鳳からダウンロードしてきた牌譜は天鳳のサーバ負荷軽減のためにも~~（ここでつのだにこびを売っていくスタイル）~~[こちら](https://drive.google.com/drive/folders/19lXnBEPuaro3gyBvqWWz2ZGg8h0USHjJ?usp=sharing)に公開しますので、使いたい方はぜひお使いください。
コードも公開したいのですがまだ見せられたもんじゃねぇ！状態なので今回はお見送り。

他の行動判断部分や強化学習への移行、ゆくゆくはSuphx打倒もゆるーく目指しているのでよければ応援お願いしますm(_ _ )m