【第42回_Beginner限定コンペ】PCゲームの勝敗予測にチャレンジ

# はじめに
みなさん2024年の目標は立てましたか？私は競馬で回収率をプラスにすることです。
そのためにも機械学習の腕を磨いて、「僕の考えた最強の競馬AI」を作るんだ！！

さて、最近は[Nishika](https://www.nishika.com/dashboard)ばかりチャレンジしていたので、気分を変えてSignateに手を出してみます（上位3人が表彰って厳しいっすよ、Nishika様！！上位10人くらいにしてくだせぇ〜）。

[【第42回_Beginner限定コンペ】PCゲームの勝敗予測](https://signate.jp/competitions/986)にチャレンジしたのでその様子をお送りします。

# Signateとは
ChatGPTに書いてもらいましょう。

>SIGNATEは、日本最大のデータサイエンスプラットフォームで、AI開発・運用、AI人材の育成・採用支援サービスを提供しています。2018年4月に開設されて以来、多様なデータサイエンスの課題に対応するコンペティション「Competition」、データサイエンススキルを学ぶプログラム「Quest」、データサイエンス職への転職を支援するサービス「Delta」、学生向けのプログラム「Campus」など、幅広いサービスを展開しています。

です！

https://signate.jp/

Siganteには称号制度があります。まずは「Intermediate」を目指すことにします。
それではスタート！

![スクリーンショット 2024-01-16 9.46.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493004/9a9c8e80-8016-46d8-7943-b72e81b6036f.png)
https://signate.jp/users/rankings/help

# 【第42回_Beginner限定コンペ】PCゲームの勝敗予測
>最初の10分間の対戦データを使ったPCゲームの勝敗予測

となっています。学習用データ、評価用データ、サブミッション用ファイルの３つが与えられます。ここら辺はどのコンペでも同じですね。
データの中身がサイトに載っているのでみてみましょう。どういうゲームか分かりませんが、なんとなくKill数やDeath数、経験値などがモデル精度に効きそうですね。
目的変数は青チームが勝ったか負けたかの二項分類となっています。

![スクリーンショット 2024-01-16 9.55.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493004/356d04dd-622d-11af-5baf-dfe1c0991106.png)

# 前処理
学習用データを「train_dataset」、評価用データを「test_dataset」として読み込んで、マージしてみます。

```python:merge
train_dataset['is_train'] = 1
test_dataset['is_train'] = 0

train_cols = train_dataset.columns.tolist()
test_cols = test_dataset.columns.tolist()
common_cols = list(set(train_cols) & set(test_cols))
train_only = list(set(train_cols) - set(common_cols))
test_only = list(set(test_cols) - set(common_cols))

for col in train_only:
    test_dataset[col] = None

for col in test_only:
    train_dataset[col] = None

merged_df = pd.concat([train_dataset, test_dataset], ignore_index=True, sort=False)

```

`merged_df.info()`で情報を見てみるとなんと**欠損値がありません**！！
しかも10000データというキリのいい数字！

![スクリーンショット 2024-01-16 10.05.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493004/1d2cdaa5-c346-77aa-044d-eb6c7cba4de8.png)

優しすぎるデータでありがたいのか歯応えがないのか・・・。
前処理しようと思いましたが、欠損地も無いしカテゴリカル変数も無いし特にやることはないですね。

# 特徴量エンジニアリング
順番がチグハグしますが、新たに生成した特徴量は以下のようなものがあります。実際は素のデータで一度モデル作った後、精度が上がるように特徴量を作成しています。

'blueKills' - 'blueDeaths'
'blueKills' / ('blueKills' + 'blueDeaths')
'blueTotalGold' / 'blueKills'
'blueTotalExperience' / 'blueKills'

上記は一例であと10個くらい作っています。

# モデル実装
とりあえずLightGBMとランダムフォレストを試しましたが、LightGBMの方が精度が良かったです。アンサンブルはせず、単一モデルでゴリ押します。
ハイパーパラメーターのチューニングは'optuna'にお任せしました。

```python:optuna
from sklearn.model_selection import KFold
import optuna.integration.lightgbm as lgb_op
from sklearn.model_selection import train_test_split

df_train,df_val = train_test_split(train_data,test_size=0.2,random_state=0)

col = 'blueWins'
train_y = df_train[col]
train_x = df_train.drop(col,axis=1)

val_y = df_val[col]
val_x = df_val.drop(col,axis=1)

trains = lgb.Dataset(train_x,train_y)
valids = lgb.Dataset(val_x,val_y)

params = {
  'objective':'binary',
  'metric': 'binary_logloss',
  'force_row_wise' : True,
  'verbose': -1
}

tuner = lgb_op.LightGBMTunerCV(params, trains,num_boost_round=1000,
folds=KFold(n_splits=3, shuffle=True, random_state=5))
tuner.run()

print('Best score:' ,{tuner.best_score})
print('Best params:')
print(tuner.best_params)
```
まずはoptunaでハイパーパラメーターのチューニングをして、Best_paramsを得ます。
二項分類なので、'objective'は'binary'、'metric'は'binary_logloss'を使用します。

```python:LightGBM
import lightgbm as lgb

model = lgb.train(tuner.best_params,
                  trains, valid_sets=[valids],
                  num_boost_round=5000,
                  callbacks=[lgb.early_stopping(stopping_rounds=100, verbose=True)]
                  )
```

モデルが構築できたのでバリデーションデータで精度を確認してみます。
LightGBMの二項分類では、モデルは各サンプルがポジティブクラス（1）に属する確率を出力します。したがって、出力される値は 0（絶対にネガティブクラス）から 1（絶対にポジティブクラス）の間の値となります。
そのため、閾値を設定し、その閾値より大きい確率を持つサンプルをポジティブクラスに、それ以外をネガティブクラスに分類します。
また、本コンペの評価方法は「Accuracy」なので、値を確認します。
（実際は混同行列やROCカーブも確認しています）

```python:LightGBM_val_data
from sklearn import metrics

estimated_val_y = pd.DataFrame(model.predict(val_x))
estimated_val_y.index = val_x.index
estimated_val_y.columns = ['estimated_val_y']

estimated_val_y['estimated_val_y'] = (estimated_val_y['estimated_val_y'] > 0.5).astype(int)

metrics.accuracy_score(val_y, estimated_val_y)
```
>0.803125

８割方正解していますね。このモデルで評価用データも予測を行い、提出します。

# 結果
36/59位でした！！無事上位60%に入り（ギリギリやないかい）、「Intermediate」に昇格することができました。

![スクリーンショット 2024-01-16 11.03.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493004/f3802207-f228-672e-dcd5-8bca6b448221.png)

# おわりに
とりあえず昇格できたのでこのコンペはここまでにします。
次は銅メダルの獲得を目指して、腕を磨いていきます。

それでは、次の記事でお会いしましょう。