部署の方々に「Pythonでデータ加工できるようになっとけ!」と言われる日々を過ごしています。
というわけで、データ加工の修行がてらデータ分析のコンペにチャレンジすることに……
勉強は身近で楽しい方が面白いですよね。
そんなわけで、某人気色塗り対戦ゲームの試合データを用いて、勝敗予測をするコンペに参加してみました
(※すでに終了済みのコンペです)。
実践的にデータ分析をすることが初めてだったので、
「pandas中心に、データの加工方法を手を動かしながら学んでいく」
これを意識して取り組みました。
データ分析初学者
基本的なデータ加工方法に興味がある人
データ分析コンペに興味がある人
データ分析のコンペにはKaggle等様々なプラットフォームがありますが、
今回はProbspaceで開催されていた対戦ゲームデータ分析甲子園というコンペに参加しました。
(※現在は終了済み)
コンペの内容をざっくりと説明すると、某有名色塗り対戦ゲームの勝敗を対戦開始時点で予測する、という課題になります。
以下のような各試合の特徴量が与えられ、これを用いてAチーム/Bチームのどちらが勝つかを予測します。いわゆる二値分類ですね。
period | 試合開始時の時刻 |
lobby-mode | レギュラーマッチ/ガチマッチ ※1 |
mode | 試合ルール※2 |
stage | ステージ |
A1-weapon | プレイヤーA1のブキ |
A1-rank | プレイヤーA1のウデマエ※3 |
A1-level | プレイヤーA1のランク※3 |
※1 ガチマッチがいわゆるレート戦に値します。
※2 ガチアサリ、ガチエリア、ガチホコ、ガチヤグラ、ナワバリの5種類
※3 ウデマエがレート、ランクが経験値を指します。
trainデータ(学習用データ)には試合結果を表すデータyも格納されています。
1であればAチームの勝利、0であればBチームの勝利を示します。
import numpy as np |
import pandas as pd |
import lightgbm as lgb |
import csv |
#訓練データ・テストデータ分割 |
from sklearn.model_selection import train_test_split |
#正規表現 |
import re |
#訓練データ・テストデータの読み込み |
train = pd.read_csv('../**/train_data.csv') |
test = pd.read_csv('../**/test_data.csv') |
ブキのデータを取り込みます。ここでは、ブキのカテゴリ情報のみ取り込んでいます。
weapon_data = pd.read_csv('../**/weapon_data.csv') |
with open('../**/weapon_data.csv', newline='',encoding="utf-8") as csvfile: |
reader = csv.reader(csvfile) |
weapon_category_dict = {rows[2]:rows[1] for rows in reader} |
#ブキをブキ種で分け、置換 |
train = train.replace(weapon_category_dict) |
test = test.replace(weapon_category_dict) |
train = train.drop(['period','game-ver','id'], axis = 1) |
test = test.drop(['period','game-ver','id'], axis = 1) |
データの中身に文字列が含まれていると分析ができないため、すべて数値に置き換えます。
#ダミー変数に変換 |
train = pd.get_dummies(train) |
test = pd.get_dummies(test) |
#不正文字列を正規表現を利用して削除 |
train = train.rename(columns = lambda x:re.sub('[^A-Za-z0-9_\+\-]+', '', x)) |
test = test.rename(columns = lambda x:re.sub('[^A-Za-z0-9_\+\-]+', '', x)) |
ここで使用するtestデータは以下で作成するモデルの検証用データになります。
そのためコンペサイトからDLしたtestデータではなく、trainデータの一部をランダムに取り分けて検証用データとします(このデータは学習には使われません)。
#trainデータを分割 |
train_set, test_set = train_test_split(train, test_size = 0.2, random_state = 123) |
x_train = train_set.drop('y', axis = 1) |
y_train = train_set['y'] |
x_test = test_set.drop('y', axis = 1) |
y_test = test_set['y'] |
#LightGBM用にデータセットを作成 |
lgb_train = lgb.Dataset(x_train, y_train) |
lgb_test = lgb.Dataset(x_test, y_test) |
最初、誤って学習データ全体でモデル構築+答え合わせをしてしまいました……
テストの本番問題を使ってテスト勉強するようなものです。
params = { |
'boosting_type':'gbdt',#ブースティングの種類。ここでは勾配ブースティング |
'objective':'binary',#機械学習タスクの種類。ここでは二値分類 |
'metric':'auc',#モデルの評価指標 |
'num_leaves':16,#決定木の葉(ノード)の数。大きいほど複雑になる |
'learning_rate':0.1,#学習率 |
'n_estimators':100000,#作成する木の数 |
'random_state':0 #乱数のシード値 |
} |
先ほど作成したデータセットやパラメータをもとにモデルを作成します。
#モデル作成 |
bst = lgb.train(params = params, |
train_set = lgb_train, |
valid_sets = lgb_test, |
num_boost_round = 100, #ブースティングの回数 |
early_stopping_rounds = 100)# 100回ごとに検証精度の改善を検討→精度が改善しないなら学習を終了(過学習に陥るのを防ぐ) |
#あらかじめ切り分けておいた検証用データを予測 |
test_predicted = bst.predict(x_test) |
予測を実行すると、以下のようなログが表示されます。
[LightGBM] [Info] Start training from score 0.098605 |
[1] valid_0's auc: 0.528582 |
Training until validation scores don't improve for 100 rounds |
[2] valid_0's auc: 0.546332 |
[3] valid_0's auc: 0.548256 |
[4] valid_0's auc: 0.548496 |
[5] valid_0's auc: 0.548267 |
[6] valid_0's auc: 0.549871 |
[7] valid_0's auc: 0.550842 |
... |
[207] valid_0's auc: 0.566587 |
[208] valid_0's auc: 0.566348 |
Early stopping, best iteration is: |
[108] valid_0's auc: 0.569808 |
ログで確認すると、このモデルのAUCは0.569808でした。
0.5(完全ランダム)~1(完璧な予測)
の値となります。
※詳しくはこちらを参照↓
白黒はっきりしない判定の評価のしかた 〜ROC曲線と AUC〜
作成したモデルと配布されたtestデータを利用して、提出用のデータを作成します。
今回はしきい値を0.5に設定し、予測結果を1 or 0に置換します。
#配布されたtestデータを予測 |
y_pred = models[0].predict(test,num_iteration=bst.best_iteration) |
#四捨五入 |
y_pred = np.where(y_pred < 0.5, 0, 1) |
#提出用ファイルの作成 |
pred_df =pd.DataFrame({"id":test.index,"y":y_pred}) |
pred_df.to_csv("submission.csv",index=False) |
コンペの提出ページからファイルを提出すると、結果が表示されます。
PublicスコアとPrivateスコアの違いをざっくり説明すると、予測している対象の違いです。どちらもtestデータから得られた予測の結果ではあるのですが、privateとpublicでは抜粋箇所が異なります。このコンペは終了済みなのでどちらも確認可能なのですが、開催中はpublicしか見られません。最終的な結果としてはPrivateスコアが採択されます。
ということで、今回の結果は0.542884でした!
うーん、ほぼ五分五分ですね。
五分五分はちょっと悔しいので、特徴量を変化させて再度予測を行ってみました(結果はどちらもPrivateスコアになります)。
ウデマエ(rank)をダミー変数に置換 | 0.534651↓ |
ブキを種類でカテゴライズしない | 0.533663↓ |
ランク(level)を特徴量から落とす | 0.534415↓ |
サブウェポン・スペシャルのデータを特徴量として追加 | 0.540626↓ |
完璧に予測できたとは言い難いですが、
・実践的なデータ加工ができた
・機械学習の一通りの手順は試せた
ということで、初回にしては良い勉強になりました。
もう少し突き詰めるとしたら、
・さらなる特徴量の生成(ステージ・ブキの組み合わせ等)
・モデルの修正(パラメータのチューニングなど)
あたりに挑戦してみたいです。
また、今回は終了済みのコンペで腕試しをするという内容でしたが、
そのうち開催中のコンペにも参加してみたいです。