CatBoostを用いたチュートリアル(LocalCV:0.5458 PublicLB:0.5472)

はじめに

初投稿なので不備等あると思いますが、ご了承ください。
今回のデータにおいてカテゴリ変数が多く、エンコーディングで悩んでいる方が多いと思います。そこでカテゴリ変数をそのまま扱える決定木ベースの勾配ブースティング手法である「CatBoost」を用いた簡単なベースラインを実装します。
工夫すれば外部データを用いずともPublicLBが0.55を超えると思います。またアンサンブル学習のモデルの1つとして参考になれば幸いです。

実装

まず「CatBoost」をインストールします。

!pip install catboost

また前提としてディレクトリ構造は以下の通りです。参考にしてください。

├── input
│ ├── train_data.csv
│ └── test_data.csv
├── notebooks
│ └── catboost_baseline.ipynb
└── output

以下、実装コードです。

# パッケージのimport
import numpy as np
import pandas as pd
from sklearn.model_selection import KFold
from catboost import Pool, CatBoostClassifier
from sklearn.metrics import accuracy_score
INPUT_TRAIN = './input/train_data.csv'
INPUT_TEST = './input/test_data.csv'
N_SPLITS = 5
SEED = 0
LIST_PLAYER = ['A1', 'A2', 'A3', 'A4', 'B1', 'B2', 'B3', 'B4']
# データの読み込み
train = pd.read_csv(INPUT_TRAIN)
test = pd.read_csv(INPUT_TEST)
# 前処理
def processing(df: pd.DataFrame):
    """
    欠損値処理:
        weapon:str型'NaN'埋め
        level :0埋め
        rank  :str型'NaN'埋め
    """
    for player in LIST_PLAYER:
        df[f'{player}-rank'].fillna('NaN', inplace=True)
        df[f'{player}-level'].fillna(0, inplace=True)
        df[f'{player}-weapon'].fillna('NaN', inplace=True)
    
    """使わない列の除去"""
    drop_columns = ['id', 'period', 'game-ver', 'lobby']
    df.drop(columns=drop_columns, axis=1, inplace=True)

    return df

train = processing(train)
test = processing(test)
# 説明変数と目的変数を分ける
train_X = train.drop('y', axis=1)
train_y = train.loc[train_X.index, 'y']

# objectの列番号を取得
categorical_features_indices = np.where(train_X.dtypes==np.object)[0]
# Catboostによる予測
scores = []
y_pred = []

kf = KFold(n_splits=N_SPLITS, shuffle=True, random_state=SEED)
for i, (tr_idx, va_idx) in enumerate(kf.split(train_X)):
    tra_X, val_X = train_X.iloc[tr_idx], train_X.iloc[va_idx]
    tra_y, val_y = train_y.iloc[tr_idx], train_y.iloc[va_idx]
    train_data = Pool(tra_X, tra_y, cat_features=categorical_features_indices)
    valid_data = Pool(val_X, val_y, cat_features=categorical_features_indices)

    model = CatBoostClassifier(eval_metric='Logloss',
                               num_boost_round=10000,
                               random_seed=SEED)
    model.fit(train_data, 
              eval_set=valid_data,
              early_stopping_rounds=10,
              verbose=False,
              use_best_model=True)
    
    tmp_pred = model.predict(val_X)
    tmp_cv = accuracy_score(tmp_pred, val_y)
    scores.append(tmp_cv)
    y_pred.append(model.predict(test))
    print(f'epoch {i+1}/{N_SPLITS}: {tmp_cv}')

cv = sum(scores)/len(scores)
print(f'CV:{cv}')
epochs 1/5: 0.5469187145557656
epochs 2/5: 0.5436672967863894
epochs 3/5: 0.5463894139886578
epochs 4/5: 0.5485822306238185
epochs 5/5: 0.5434404536862004
CV:0.5457996219281663
# submit.csvの作成
sub = pd.DataFrame(index=test.index+1, columns=['y'])
sub.index.name = 'id'

# 多数決を取る(今回fold数が5より合計して3以上なら1、3未満なら0)
sub['y'] = np.where(sum(y_pred)>=3, 1, 0)
sub.to_csv(f'./output/cv{cv}.csv')
# PublicLB: 0.5472

以上で実装は完了です。
確率で出力することも可能です。詳しくは公式のドキュメント等を確認してください。
(参考:[CatBoost-Python package training parameters](https://catboost.ai/docs/concepts/python-reference_parameters-list.html))

この実装をもとにスコアを向上させる方法は様々あります。その例として
・トピックに挙がっている外部データを用いる
・他のモデルとアンサンブル学習をする
・levelやrankをチームごとまとめて新しく特徴量にする
などなど…

添付データ

  • catboost_baseline.ipynb?X-Amz-Expires=10800&X-Amz-Date=20241004T180436Z&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIP7GCBGMWPMZ42PQ
  • Favicon
    new user
    コメントするには 新規登録 もしくは ログイン が必要です。