本コンペでは、オンライン大会があることでも有名なゲームの対戦データを用いて、勝敗予測モデルの開発にチャレンジいただきます。
eスポーツは、2019年時点で既に\$957.5M、2023年には~\$1,600Mにまで拡大が予想されている急成長市場です*1。
国内では任天堂・コナミといった企業が発売している、スプラトゥーン・ウイニングイレブン等が有名で、
スプラトゥーンにおいてはスプラトゥーン甲子園、プレミアリーグが開催されるほどの大きな広がりを見せています。
そこで本コンペにおいては、ある有名ゲームのオンライン対戦データを用いて、勝敗を予測するアルゴリズム開発にチャレンジいただきます。
学習データの中には、ステージ、武器、ウデマエ、レベル、勝敗といった情報が含まれております。
対戦を優位に進めるために、どの武器を選択すべきか、今後の対戦に役立てられるようモデル化できればと思います。
今までプレイ経験のない方も、ぜひこのコンペをきっかけに参戦いただけますと幸いです。
1位:100,000円
*1 : Newzoo Global Esports Market Reportより参照
データをダウンロードするにはログインまたはユーザー登録して下さい
コンペに使用するデータは2つに分けられます。
訓練データセットとテストデータセットにはそれぞれ66,125レコードと28,340レコードのサンプルが含まれています。
訓練データは、バトルと勝敗の値が各レコードごとに記載されています。機械学習モデルの構築に使用してください。
テストデータセットでは、勝敗のデータは存在しません。テストデータセットを使用して、どの程度新しいデータに対してモデルが適合しているかを確認し、各バトルの勝敗を予測してください。
また、提出ファイルの例に関しては、評価方法のタブを参照してください。
データセットのカラムは以下の通りになっています。
column name | example | meaning |
---|---|---|
period | 2018-07-01T00:00:00+00:00 |
どのピリオド(スケジュール)のバトルかを示します。 Indicates which period is. |
game-ver | 3.1.0 |
ゲームバージョンを示します。 Indicates official game version. |
lobby-mode | regular |
regular : レギュラーマッチ Regular battlegachi : ガチマッチ Ranked battlefest : フェスマッチ Splatfest |
lobby | standard |
standard : ひとりプレー(野良) Solo Queue squad_2 squad_4 : リーグマッチ/チームフェス League(Ranked)/Team(Splatfest)private : プライベートマッチ Private battle |
mode | nawabari |
nawabari : ナワバリバトル Turf Wararea : ガチエリア Splat Zonesyagura : ガチヤグラ Tower Controlhoko : ガチホコバトル Raimakerasari : ガチアサリ Clam Blitz |
stage | battera |
ステージ Stage |
A1-weapon | wakaba |
ブキ Weapon |
A1-rank | s+ |
ウデマエ Rank |
A1-level | 99 |
ランク Level |
A2-weapon | ||
A2-rank | ||
A2-level | ||
A3-weapon | ||
A3-rank | ||
A3-level | ||
A4-weapon | ||
A4-rank | ||
A4-level | ||
B1-weapon | ||
B1-rank | ||
B1-level | ||
B2-weapon | ||
B2-rank | ||
B2-level | ||
B3-weapon | ||
B3-rank | ||
B3-level | ||
B4-weapon | ||
B4-rank | ||
B4-level | ||
y | 1 | 1: Aチームが勝利した A team won 0: Bチームが勝利した B team won |
AIZAWA hina氏によるstat.inkライセンスはCreative Commons - 表示4.0国際
に基づきます。
目標は、テストデータセットのバトルの情報にもとづき、勝敗を予測することです。y(勝敗)を二値で予測してくだい。
回答用のsubmission.csvを用意する(エントリーとヘッダー行を含む) 。
提出されたファイルに余分な行や列(idとy以外)が含まれていた場合はエラーとなります。
提出ファイルは以下の列のみを必ず含んでください:
id,y
0, 1
1, 1
2, 0
3, 1
本コンペでは、開催期間終了後 賞金対象者のコードを公開し、ユーザーの皆様にチーティング有無をレビューしていただき順位確定させる、オープンレビュー方式のコンペティションを行います。
コード公開後1週間:
レビュアー(ユーザー)より、チーティングの疑いに関するコメントがある場合は、ご回答をお願いいたします。
※チーティングとは無関係のコメント(ノウハウに関する質疑 等)についてもご回答いただけると幸いですが、順位確定の判断材料とは致しません。
公開コードを確認いただき、チーティングが疑われる場合は、トピックを通して質疑の投稿をお願いいたします。
レビュアーからの質疑と、回答状況をふまえて、最終的に運営側で順位確定を判断します。
開始日 2020/8/19 17:00 JST
終了日 2020/10/18 24:00 JST
エントリー締め切り なし
※コンペ期間中であっても、不正が疑われる場合は、運営より確認のためメール連絡させていただくことがございます。一週間以内にご回答いただけない場合も、不正と判断させていただきます。
原則外部データ使用は禁止としておりますが、正解データの入手につながらない場合は、下記承認プロセスにより使用できることといたします
注)ただし、コンペ終了まで10日をきってからの申請は禁止とします
公平性の担保、チーティング等の不正防止のため、予告なくルールの追加・変更を行う場合がございます。
ご不便をおかけすることもあるかと思いますが、サービス向上のためご了承ください。
コンペティションへの参加に際しては、ProbSpace利用規約(以下、「利用規約」といいます。)に加え、本ProbSpace参加規約(以下「本規約」といいます。)に同意いただく必要があります。利用規約にて定義された用語は、本規約においても同様の意味で用いられるものとします。
第1条(適用)
第2条(定義)
本規約において次の各用語の定義は、それぞれ以下に定めるとおりとします。
第3条(権利の帰属)
第4条(入賞者の義務)
第5条(禁止事項)
第6条(本コンペの変更、中断、終了等)
第7条(損害賠償)
第8条(本規約の変更)
当社は、必要と判断した場合には、参加者に対して事前に通知する(本コンペにかかる当社ウェブサイト上での告知その他当社が適当と認める方法を含みます。)ことにより、いつでも本規約を変更することができるものとします。なお、変更内容の通知後、参加者が当社の定める期間内に本コンペへの参加を取り消す手続をとらなかった場合には、当該参加者は変更後の規約に同意したものとみなされます。当社は、本規約の変更により参加者に生じたすべての損害について一切の責任を負いません。
第9条(その他)
本契約の準拠法は日本法とし、本契約に起因し又は関連する一切の紛争については、当社の本店所在地を管轄する裁判所を第一審の専属的合意管轄裁判所とします。
(制定)2020年6月22日
はい。
最も精度の高い学習モデルを作成した優勝者には、賞金10万円を贈呈します。
順位確定までのプロセスについては、ルール「Open Review Competition」を参照ください。
可能です。チームページから作成いただけます。
こちらから作成いただけます。
コンペティション参加にはアカウント登録が必要となりますのでご注意ください。
Seed を固定することが推奨です。
ただし、Seed を固定しなくても提出用コードとしては認めています。
このチュートリアルでは、オンラインゲームの対戦データに対して
を行います。
#環境確認
import pandas as pd
import numpy as np
import sklearn
!python3 --version
print(pd.__version__)
print(np.__version__)
print(sklearn.__version__)
import matplotlib
print(matplotlib.__version__)
Python 3.7.3
0.25.3
1.17.4
0.22
3.1.1
まずはデータを読み込んで見ましょう。csvデータの読み込みは複数のやり方がありえますが、pandas
のread_csv
関数はその中でも機能が豊富で、扱いやすいためこれを使います。
これを使うと、csvデータを読み込み、pandas.DataFrame
にして返してくれます。
#データの読み込みと前処理
import pandas as pd
train_data = pd.read_csv("train_data.csv", index_col='id')
print(train_data.shape)
(66125, 31)
また、DataFrame
には.head()
というメソッドが定義されており、これを呼び出すとDataFrame
の先頭の数行を確認できます。
train_data.head()
id | period | game-ver | lobby-mode | lobby | mode | stage | A1-weapon | A1-rank | A1-level | A2-weapon | ... | B2-weapon | B2-rank | B2-level | B3-weapon | B3-rank | B3-level | B4-weapon | B4-rank | B4-level | y |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2019-10-15T20:00:00+00:00 | 5.0.1 | regular | standard | nawabari | sumeshi | sshooter_becchu | NaN | 139 | soytuber_custom | ... | hokusai_becchu | NaN | 26.0 | herocharger_replica | NaN | 68.0 | sharp_neo | NaN | 31.0 | 1 |
2 | 2019-12-14T04:00:00+00:00 | 5.0.1 | regular | standard | nawabari | arowana | parashelter_sorella | NaN | 198 | jetsweeper | ... | squiclean_b | NaN | 118.0 | campingshelter | NaN | 168.0 | sputtery_clear | NaN | 151.0 | 0 |
3 | 2019-12-25T14:00:00+00:00 | 5.0.1 | gachi | standard | hoko | ama | nzap89 | a- | 114 | quadhopper_black | ... | nzap85 | a+ | 163.0 | prime_becchu | a- | 160.0 | dualsweeper_custom | a | 126.0 | 0 |
4 | 2019-11-11T14:00:00+00:00 | 5.0.1 | regular | standard | nawabari | engawa | bamboo14mk1 | NaN | 336 | splatroller_becchu | ... | liter4k | NaN | 189.0 | promodeler_mg | NaN | 194.0 | hotblaster_custom | NaN | 391.0 | 0 |
5 | 2019-12-14T06:00:00+00:00 | 5.0.1 | gachi | standard | hoko | chozame | bold_7 | x | 299 | hissen_hue | ... | sputtery_hue | x | 45.0 | bucketslosher_soda | x | 246.0 | wakaba | x | 160.0 | 1 |
5 rows × 31 columns
訓練データにおける勝敗の比率がどのようになっているか見てみましょう。
np.sum(train_data['y'] == 1) / train_data['y'].count(), np.sum(train_data['y'] == 0) / train_data['y'].count()
(0.524703213610586, 0.47529678638941397)
このデータでは少しAチームの勝利数が多いようです.
続いて、下記のコードを使用して使用するデータ全体の概観を確認します。
各項目について欠損値以外('non-null')の値がどの程度含まれているか把握できます。また、データの形式についてもここで判別可能です。
train_data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 66125 entries, 1 to 66125
Data columns (total 31 columns):
period 66125 non-null object
game-ver 66125 non-null object
lobby-mode 66125 non-null object
lobby 66125 non-null object
mode 66125 non-null object
stage 66125 non-null object
A1-weapon 66125 non-null object
A1-rank 51681 non-null object
A1-level 66125 non-null int64
A2-weapon 66125 non-null object
A2-rank 51681 non-null object
A2-level 66125 non-null float64
A3-weapon 66125 non-null object
A3-rank 51681 non-null object
A3-level 66125 non-null float64
A4-weapon 66074 non-null object
A4-rank 51638 non-null object
A4-level 66074 non-null float64
B1-weapon 66125 non-null object
B1-rank 51681 non-null object
B1-level 66125 non-null int64
B2-weapon 66125 non-null object
B2-rank 51681 non-null object
B2-level 66125 non-null float64
B3-weapon 66124 non-null object
B3-rank 51681 non-null object
B3-level 66124 non-null float64
B4-weapon 66058 non-null object
B4-rank 51624 non-null object
B4-level 66058 non-null float64
y 66125 non-null int64
dtypes: float64(6), int64(3), object(22)
memory usage: 16.1+ MB
データの形式を見ると、数値についてはfloat64/int64
型になっていることが確認できます。A1-weapon
、A2-weapon
、A3-weapon
、A4-weapon
、B1-weapon
、B2-weapon
、B3-weapon
、B4-weapon
は文字列で武器の名前が書かれているようです。A4-weapon
、B3-weapon
、B4-weapon
については少しですがnull
のデータがありました。
モデルに投入する目的変数(y),説明変数(X)を作成します
from sklearn.preprocessing import MultiLabelBinarizer
mlb = MultiLabelBinarizer()
mlb.fit([set(train_data['A1-weapon'].unique())])
MultiLabelBinarizer(classes=None, sparse_output=False)
def trans_weapon(df, columns=['A1-weapon', 'A2-weapon', 'A3-weapon', 'A4-weapon']):
weapon = df.fillna('none')
weapon_binarized = mlb.transform(weapon[columns].values)
return pd.DataFrame(weapon_binarized, columns=mlb.classes_)
def make_input_output(df, with_y=False):
a_weapon = trans_weapon(df, ['A1-weapon', 'A2-weapon', 'A3-weapon', 'A4-weapon'])
b_weapon = trans_weapon(df, ['B1-weapon', 'B2-weapon', 'B3-weapon', 'B4-weapon'])
X = pd.concat([a_weapon, b_weapon], axis=1)
if with_y:
y = df['y']
return X, y
return X
make_input_output
関数ではAチーム、Bチームのそれぞれでいずれかのメンバーがある武器を利用しているかを表すデータを作成しています。
これはsklearn
のMultiLabelBinarizer
という機能を用いることで作成できます。単にカテゴリカルデータではなく、一つのレコードが複数のカテゴリカルに属する可能性があるデータでは活用できるでしょう。
X, y = make_input_output(train_data, with_y=True)
/usr/local/lib/python3.7/site-packages/sklearn/preprocessing/_label.py:987: UserWarning: unknown class(es) ['none'] will be ignored
.format(sorted(unknown, key=str)))
いよいよ予測モデルの作成に入ります。model.fit('説明変数','目的変数')
と記述することでモデルの学習が可能となります。目的変数を説明変数の組み合わせで説明可能な回帰モデルを作成できます。
#scikit-learnライブラリをimport
import sklearn
from sklearn.ensemble import RandomForestClassifier
#線形回帰モデルのインスタンス化
model = RandomForestClassifier()
#予測モデルの作成
model.fit(X, y)
RandomForestClassifier(bootstrap=True, ccp_alpha=0.0, class_weight=None,
criterion='gini', max_depth=None, max_features='auto',
max_leaf_nodes=None, max_samples=None,
min_impurity_decrease=0.0, min_impurity_split=None,
min_samples_leaf=1, min_samples_split=2,
min_weight_fraction_leaf=0.0, n_estimators=100,
n_jobs=None, oob_score=False, random_state=None,
verbose=0, warm_start=False)
list(reversed(sorted(list(zip(model.feature_importances_, X.columns)))))[:10]
[(0.013357165181876553, 'prime_becchu'),
(0.011443964257055605, 'splatroller'),
(0.010996128432155314, 'nzap85'),
(0.01082213018002065, 'splatroller'),
(0.010276424036862188, 'nzap89'),
(0.010195469546078965, 'prime_becchu'),
(0.01001466293689794, 'wakaba'),
(0.009995963033009037, 'wakaba'),
(0.009555699489158472, 'nzap89'),
(0.009123588414361871, 'dualsweeper_custom')]
RandomForestではfeature_importances_
で特徴量の重要度を取得することができます。
重要度が大きな10つのカラムは以上の通りになります。
テストデータに対する回帰モデルの当てはまりの良さの指標としては'決定係数'などの指標が用いられます。
決定係数の値は、.score
で確認が可能です。
1に近いほど回帰式で予測された値が実際のデータに当てはまることを表します。
また、コンペの評価指標となるaccuracy
についても出力が可能です。
今回は、sklearn.metrics
に含まれるaccuracy_score
メソッドを使用します。
accuracyは予測した出力のうち正答した割合を示す指標であり、1に近いほど予測精度が高いことを表します。
モデルを元にした予測値は、model.predict('説明変数')
で表します。
# accuracyの出力
from sklearn.metrics import accuracy_score
y_true = y
y_pred = model.predict(X)
print(accuracy_score(y_true, y_pred))
0.9999848771266541
今回作成したモデルのaccuracyは99.9%程度でした。
最後に、作成したモデルを使用してテストデータでの勝敗を予測します。
テストデータの説明変数については予め、上記でモデルに学習させたデータの説明変数と同様の前処理をする必要があります。
path_test = "test_data.csv"
test_data = pd.read_csv(path_test, index_col='id')
test_data.head()
id | period | game-ver | lobby-mode | lobby | mode | stage | A1-weapon | A1-rank | A1-level | A2-weapon | ... | B1-level | B2-weapon | B2-rank | B2-level | B3-weapon | B3-rank | B3-level | B4-weapon | B4-rank | B4-level |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 2019-12-17T12:00:00+00:00 | 5.0.1 | gachi | standard | area | hakofugu | prime_collabo | x | 174 | herospinner_replica | ... | 160 | nautilus47 | x | 151.0 | l3reelgun_d | x | 213.0 | nzap89 | x | 306.0 |
2 | 2019-11-25T16:00:00+00:00 | 5.0.1 | gachi | standard | asari | anchovy | prime_becchu | s+ | 363 | nzap89 | ... | 326 | dualsweeper_custom | s+ | 289.0 | prime_becchu | s+ | 147.0 | splatroller | s+ | 156.0 |
3 | 2019-10-22T08:00:00+00:00 | 5.0.1 | gachi | standard | area | mutsugoro | furo_deco | s | 116 | bold | ... | 76 | momiji | s | 232.0 | nzap83 | s | 183.0 | promodeler_pg | s | 105.0 |
4 | 2019-12-30T04:00:00+00:00 | 5.0.1 | gachi | standard | asari | devon | prime_becchu | s+ | 192 | splatspinner_collabo | ... | 90 | quadhopper_black | s+ | 119.0 | soytuber | s+ | 172.0 | prime | s+ | 70.0 |
5 | 2019-10-15T16:00:00+00:00 | 5.0.1 | gachi | standard | yagura | anchovy | l3reelgun_d | x | 267 | rapid_becchu | ... | 259 | sshooter_becchu | x | 198.0 | splatscope | x | 50.0 | screwslosher_becchu | x | 287.0 |
5 rows × 30 columns
test_X = make_input_output(test_data)
/usr/local/lib/python3.7/site-packages/sklearn/preprocessing/_label.py:987: UserWarning: unknown class(es) ['none'] will be ignored
.format(sorted(unknown, key=str)))
先ほど使用したmodel.predict('説明変数')
の'説明変数'にテストデータの値を代入することで、テストデータの予測値を算出することができます。
下記のようにして、提出用のsubmission.csv
を出力することが可能です。
#テスト結果の出力
test_predicted = model.predict(test_X)
submit_df = pd.DataFrame({'y': test_predicted})
submit_df.index.name = 'id'
submit_df.to_csv('submission.csv')
コンペティションへの参加に際しては、ProbSpace利用規約(以下、「利用規約」といいます。)に加え、本ProbSpace参加規約(以下「本規約」といいます。)に同意いただく必要があります。利用規約にて定義された用語は、本規約においても同様の意味で用いられるものとします。
第1条(適用)
第2条(定義)
本規約において次の各用語の定義は、それぞれ以下に定めるとおりとします。
第3条(権利の帰属)
第4条(入賞者の義務)
第5条(禁止事項)
第6条(本コンペの変更、中断、終了等)
第7条(損害賠償)
第8条(本規約の変更)
当社は、必要と判断した場合には、参加者に対して事前に通知する(本コンペにかかる当社ウェブサイト上での告知その他当社が適当と認める方法を含みます。)ことにより、いつでも本規約を変更することができるものとします。なお、変更内容の通知後、参加者が当社の定める期間内に本コンペへの参加を取り消す手続をとらなかった場合には、当該参加者は変更後の規約に同意したものとみなされます。当社は、本規約の変更により参加者に生じたすべての損害について一切の責任を負いません。
第9条(その他)
本契約の準拠法は日本法とし、本契約に起因し又は関連する一切の紛争については、当社の本店所在地を管轄する裁判所を第一審の専属的合意管轄裁判所とします。
(制定)2020年6月22日