[Splatoon2] 勝率70%の戦い方をAIから学ぶ

誰が戦犯か突きとめてやりたい

いや、ぼくは別に突きとめたくないんですけど、どうしても突きとめたい人がいるので突きとめる方法を考えたいと思います。

別にディープラーニングなんか使わなくても回帰分析とか統計でできそうな気もするんですがまあそれはおいといて。

寄与度を調べる

例えばチーム全体で20キルしたとしてその割合が(20, 0, 0, 0)のチームと(5, 5, 5, 5)のチームだとこれはどちらの方が勝つ確率が高いんでしょうか?

個人的には後者のほうが勝ちやすいと思っています。というのは、如何にキルをとれるプレイヤーでも5分間で全くデスしない状況というのは殆どないからです。

例えば20キルしたプレイヤーが5デスしたとします。キル数をデス数で割ったキルレシオは4.0となり、これはもうむちゃくちゃ優秀な値なのですが、5デスしたとすると1デスあたり復帰に8.5秒かかるので少なくとも42.5秒はそのプレイヤーが戦っていないことになります。

その間、仲間はずっと0キルなので相手が4人いてこっちが3人しかいないという数的不利な状況が少なくとも42.5秒あったということがわかります。

5分間は300秒なので全体の14.17%の時間帯で少なくともこちら側が不利だったことがわかります。1人いないということは25%の戦力ダウンなので、これは結構重要な要素にならないでしょうか?

つまり、チームとしてのリザルトはあまり重要ではなく(もちろん重要なのですが)、それよりも個人成績の方が大切だということがわかります。

どのようにして寄与度を求めるか

単純なキル数/デス数は関係ありません。

というのも、ガチマッチは試合によっては1分で終わったりするからです。ということは必要なのはチーム全体のキル数における自分のキル数なわけです。これは割合を使えば簡単に表現でき、チーム全体で合計が1になります。

この操作を正規化といいます

同様にして、デス数も正規化します。

リザルト例

例えば、以下のようなリザルトを考えます。

PowerKill+AssistKillDeathKill+(%)Assist(%)Kill(%)Death(%)
11311240.4193548390.1428571430.50.307692308
274330.2258064520.5714285710.1250.230769231
351400.1612903230.1428571430.1666666670
461560.1935483870.1428571430.2083333330.461538462
160690.19354838700.250.692307692
210170.03225806500.0416666670.538461538
320230.06451612900.0833333330.230769231
441360.1290322580.1428571430.1250.461538462

まず、勝利した側をAチーム、負けた側をBチームと便宜的に名付けます。

さて、では上のリザルトを見て「何故Aチームは勝てたか?」を人間的に考えたいと思います。

これはガチホコのルールなのですが、誰がどのくらいホコを進めたのかはリザルトからはわからない点に注意してください。なので、この寄与度はあくまでもキル数デス数から得られる参考値であり、実際の貢献度を測れるものではありません。

ひと目で目につくのはローラーのキル数の多さです。キルを取るのが勝利に最も近い要素であるのは間違いないので、ローラーが活躍したのでこの試合は勝てたと考えるのが最も自然です。

また、デス数もチームで一番多いのが6デスであり、相手チームのチャージャー以外のどのプレイヤーよりもデス数が少ないことがわかります。これだけでも十分な情報なのですが、更に以下のように考えを進めることができます。

チャージャーは後衛ブキであるので基本的にデス数は少ない。そのチャージャー以外のどのブキよりもデス数が少ない」という考えができます。

では、チャージャーよりもデス数が少ないことはどのくらいの寄与度があるのでしょうか?これは今まで人間が適当にパラメータを与えることしかできませんでした。このパラメータを実際のデータに合うように上手く自動で変化させられるのが、まさにAIがここまで発展するに至ったブレイクスルーでした。

スプラトゥーン2のガチマッチにおけるリザルトは近接32戦の勝率が高い順に並んでいるので、上にいればいるほど強いプレイヤーということになります。その点も考慮に入れた寄与度を考えるとパラメータが多くなりすぎてとても人間では調整ができません。「相手が単射程ばかりだとチャージャーは自由に動けるのでチャージャー寄与度にバイアスがかかる」とかそういう複雑な操作も機械学習を使えば簡単に行なえます。

パラメータを考える

コンピュータにデータを与えるためには、すべての情報を数値化する必要があります。

キルレシオ等は簡単に数値化できますが、ブキの種類などはどうやって表現すればよいでしょうか?

これは、ベクトル表現を使うことで解決できます。

まず、スプラトゥーン2にはヒーローレプリカを除いて91種類のブキが実装されています。そのうち4種類はスコープ付きなのですが、ここでは話を簡単にするためにスコープ付きは同種のブキとして扱います。

つまり4種類減って87種類になります。これでもいいのですが、メインのブキ性能だけでベクトルを作ってしまったほうが楽です。

要するにわかばシューターともみじシューターはメイン性能が全く同じなので同種のブキだと考えます。

すると、これらのブキは45次元のベクトル空間に表現可能になります。

45次元と言われてもピンとこないので話を簡単にします。

じゃんけんで考えよう

たとえばじゃんけんを学習させたいとします。「グー」「チョキ」「パー」はそのままでは学習できないので、それぞれに数値を与えなければいけません。

しかし、例えば「グー」に0、「チョキ」に1、「パー」に-1の数値を与えてしまうと、

  • 「グーはチョキに勝つ」→「0は1に勝つ」→「0>1」→誤り
  • 「チョキはパーに勝つ」→「1は-1に勝つ」→「1>-1」→正しい
  • 「パーはグーに勝つ」→「-1は0に勝つ」→「-1>0」→誤り

という考えがコンピュータの中で発生してしまい、コンピュータが混乱を起こしてしまいます。

そこで、「グー」に(1, 0, 0)、「チョキ」に(0, 1, 0)、「パー」に(0, 0, 1)というふうにそれぞれ独立した値を与えれば、さっきのようなおかしな不等式が生まれないことがわかります。

じゃんけんは出す手が3種類しかなかったので3次元のベクトルでしたが、スプラトゥーンのメインブキは45種類あるのでそれが45次元になるというだけです。

サブとスペシャルを考える

次に、スペシャルが全部で9種類あります。話を簡単にするためにボムピッチャー系は全部同じとして考えます。

9種類あるのでこれは9次元必要です。

同様にしてサブは11種類あるので11次元必要になります。

チームとしての表現する(実装するか検討中)

今は8人それぞれのパラメータを45+9+11=65次元のデータとして扱いました。

これでも悪くないのですが、65次元のデータを4人分扱うと260次元になります。これをまとめて1つのチームとして扱います。

こうすることで計算がものすごく楽になりますが、弊害も生まれます。

最も深刻な問題は、ブキの組み合わせによっては「全く同種の組み合わせと判定される異なるペアが存在する可能性がある」ということです。

パラメータ案

  • メインブキ45次元
  • サブ11次元
  • スペシャル9次元
  • キル率、アシスト率、デス率
  • スペシャル率

とここまで書いて思ったんですが…

これ、めんどくさくね???

チームリザルトから勝率を求める

ここでは一旦今までのことはすべて忘れる。

単純に、チーム全体のリザルトからこの試合は勝ったか負けたかを予想するAIをつくることにする。これなら260次元なんてでこないし、絶対簡単である。

ブキ種、スペシャル種も今は一旦忘れる。あるリザルトRはAIは95%勝てると予想しているが、実際には負けた。勝てたはずの試合だから、反省点が多い。

というような感じで、まずは全体として反省すべきかそうでないかを判定してくれる機能がほしいわけである。

必要なパラメータ

リザルトから得られるデータだけで考える。

すると、どうも以下のデータが重要そうな気がする。

  • 塗りポイント率(塗り面積は動きやすさに直結する。また、間接的にどのような人数状態の推移であったかを大まかに知ることができる)
  • キル率
  • アシスト率
  • 純粋キル率
  • デス率
  • スペシャル率

これはチーム全体として考えているので、冒頭で述べた20キル1人のワンマンチームと、バランスの良い全員5キルチームが全く同一のチームとして扱われている点にはご注意いただきたい。

するとパラメータはたったの6つである。これに勝ったか負けたかのデータがあればいいので、これなら簡単に求められそうである。

実装してみた

パラメータがたったの6つしかないので、わざわざディープラーニングを使うまでもない。というか、全然層が深くないのでディープですらない。

gachi.csv

データはここにおいておきます。各自ご自由にご利用ください。

import pandas as pd
from pandas import Series,DataFrame
import numpy as np

from sklearn import svm
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

import keras
from keras.datasets import fashion_mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout

dataset = pd.read_csv("gachi.csv", sep=",", header=0)

# Variables
x = DataFrame(dataset.drop("victory", axis=1))

# Objective variable
y = DataFrame(dataset["victory"])

x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.1)

model = Sequential()
model.add(Dense(input_dim=6, units=1, activation='sigmoid'))
model.summary()

model.compile(loss='binary_crossentropy', optimizer='rmsprop', metrics=['accuracy'])
model.fit(x_train, y_train, batch_size=100, epochs=50,verbose=1, validation_data=(x_test, y_test))

evaluate(x_test, y_test, verbose=1)

コードはこんな感じなのだが、入力層のサイズをいくら大きくしても全く精度に影響しない。

ネットワーク

Layer (type) Output Shape Param #
=================================================================
dense_1 (Dense) (None, 1) 7
=================================================================

単純パーセプトロンである。ただの線形関数なので、もはやディープラーニングとは呼べない。

TensorBoardで可視化

正解率は徐々に上がっていっているのですが、よく見ると50エポックでたったの1%しかあがっていません。むしろエポック0でも正解率が74.6%近くもあるので、これはパラメータとリザルトに強い相関があるためにすぐにAIが関係を見つけてしまっているのですね。

で、それ以上のことはパラメータが少なすぎてわからないので、いきなり高い精度がでて伸び悩むという現象が起きている気がします。

ヒストグラムはこんな感じ。中央によっていた値がだんだんと移動してきているのがわかります。

結局、どれが一番重要なパラメータなんだ?

適当に学習を進めて結果を出してみた。

パラメータが単純すぎるので、ウェイトが毎回かなりコロコロ変わるので10回のデータを利用した。どれも勝ったか負けたかを70%以上の精度で当てている。

ということは、それぞれのパラメータについてAIと同じような重み付け(優先度)でプレイして、実際にそのとおりの数値をだすことができれば70%の確率で勝てるということを意味している。

試行キル数(アシスト込み)キル数(アシスト抜き)アシスト数デス数スペシャル数塗りポイント
1-0.353-0.6630.746-0.092-0.257-0.149
20.083-0.194 0.084-1.190.033-0.428
3-0.4280.939-0.227-1.5010.355-0.706
4-0.251-0.0260.343-0.1470.058-0.919
5-0.4200.4740.276-1.1090.0170.358
60.3280.154-0.114-0.060-1.0720.098
70.1610.482-0.434-1.000-0.1420.249
8-0.2600.1250.843-1.113-0.114-0.873
9-0.046-0.2330.041-0.3320.110-0.493
100.5060.668-0.506-1.232-0.347-0.124
Avg-0.0680.1730.105-0.778-0.136-0.299

この表は、それぞれの(正の)値がどのくらい勝利に貢献しているかを示している。

顕著なのがデス数で、これは10回の試行全てで勝利に負の相関関係があることを示している。つまり、デスが多ければ多いほど勝利に遠ざかるということである。

ここから、AIは勝利条件として「デスは少ないほうが良い」ということを正しく理解できていることがわかる。

塗りは重要じゃない?

表を見ると塗りが勝利に貢献する割合がものすごく低いことに気がつくと思う。寄与度は-0.299であって、これは「むしろ塗らないほうが勝てる」とまで言える。

ではどうしてこのような結論に至ったのだろうか?スプラトゥーンは塗りが基本のゲームだったはずだ。キルできない人も塗りで強い仲間をアシストすることで楽しめるゲームではなかったか?

思うに、これは俗に言う「芋っている」という状態を指しているのではないかと考えられる。芋っているというのは前線にでて戦わずに、自陣を塗ってスペシャルを何回も撃つような動きをする人のことを指した言葉である。

ここで、塗りを最も軽視している試行4のデータに注目しよう。

このデータはほとんどのパラメータで負の相関関係を示しているが、アシスト数だけが飛び抜けて強い相関を示している。

アシスト数を増やすためにはスペシャルを撃つのが最も手っ取り早いが、このデータではスペシャル数もさほど重要視されていない。ということは、これは「メインウエポンやサブウエポンでアシストをとることが勝ちに繋がる」とAIが学習したということに他ならない。

例えばガチホコで初動で優勢になった場合には、一気に押し込みたいのでマルチミサイルやバブルなどのスペシャルは使う機会がないということがある。そういう状況ではスペシャル数も必然的に減ってくる。

つまり、「仲間全員が進軍して互いに助け合うような動きをすれば勝てる」というように置き換えて考えられないだろうか?

次に、塗りを二番目に軽視している試行8のデータに注目しよう。

ここでは先程よりもアシスト数が更に重視されており、さらにデス数がかなり重要であることがわかる。塗れない状況というのはデスしていて塗れないか、撃っているけれど既に自インクなので塗りポイントに計上されていないかのどちらかだと思うのだが、この場合前者ではいけないということである。

つまり、「塗りは少なくても勝てる状況はあるが、デスがめちゃくちゃ少なくないといけない」ということを意味している。また、アシスト数もかなり重要視されるのでセンプクしていてはダメということもわかる。

このように、AIで分析した結果からどのような戦い方が勝ちやすいかがおぼろげに見えてくる。

今度やりたいこと

あまりにパラメータが少なすぎるのと、ネットワークが単純パーセプトロンなせいで精度が75%付近から上がらないのが問題。

これはパラメータを増やせば解決できると思っている。

しかし、パラメータを増やすためにはstat.inkからダウンロードしてきたcsvファイルをパラメータに変換する必要がある。

それがちょっとめんどくさいかなーという感じだが、やればいい結果がでそうなのでそのうち実行してみたい。

あとはstat.inkからバトルIDを引数にしてJSONをGETする方法がわからないので、知っている方がいたら教えてください。ドキュメント見ても全然わかりませんでした。

シェアする