Oregin
簡単な前処理(inning数、裏表列の追加、game_infoの結合,カテゴリカル変数のエンコーディング)を行ったサンプルコードです。ご参考までご活用ください。※Google Colabで実行可能です。
CV= 0.14017 LB= 0.13157 でした。
ディレクトリ構成
# xfeatのインストール !pip install git+https://github.com/pfnet-research/xfeat.git
# ------------------------------------------------------------------------------ # 各種ライブラリのインポート # ------------------------------------------------------------------------------ import pandas as pd import numpy as np import json import os import random import string import re from pathlib import Path from tqdm import tqdm import lightgbm as lgb from sklearn.model_selection import KFold from sklearn.metrics import f1_score from sklearn import preprocessing
# データ読み込み ##################################### ###### train ######################## ##################################### train = pd.read_csv('../data/train_data.csv') target = train['y'] train = train.drop(['id','y'],axis=1) ##################################### #### test ########################### ##################################### test = pd.read_csv('../data/test_data.csv') test = test.drop('id',axis=1) ##################################### #### test ########################### ##################################### game = pd.read_csv('../data/game_info.csv') game = game.drop('Unnamed: 0',axis=1)
# 訓練データのみにある列名(テストデータにはない列名)のリストを作成 delcollist = [] for col in train.columns: if not col in test.columns: delcollist.append(col) # 訓練データのみにある列名を削除 train = train.drop(delcollist,axis=1)
# inning を 数値に変換 train['inning_num'] = train['inning'].apply(lambda x: re.sub("\\D", "", x)) test['inning_num'] = test['inning'].apply(lambda x: re.sub("\\D", "", x))
# 表裏を判定する関数 def omote_ura(x): if '表' in x: return 0 else: return 1
# 表裏の列を追加 train['inning_ForB'] = train['inning'].apply(lambda x: omote_ura(x)) test['inning_ForB'] = test['inning'].apply(lambda x: omote_ura(x))
# game_infoの追加 train = pd.merge(train, game, how='left') test = pd.merge(test, game, how='left')
# inningの削除 train = train.drop('inning',axis=1) test = test.drop('inning',axis=1)
# カテゴリカル変数のカラムを抽出 categorical_columns = [x for x in train.columns if train[x].dtypes == 'object']
# カテゴリカル変数をカウントエンコードする from xfeat import CountEncoder encoder = CountEncoder(input_cols=categorical_columns) train = encoder.fit_transform(train) test = encoder.transform(test)
# 訓練データにターゲット列を追加する train['target'] = target
# カテゴリカル変数をターゲットエンコーディングする from sklearn.model_selection import KFold from xfeat import TargetEncoder fold = KFold(n_splits=5, shuffle=True, random_state=42) encoder = TargetEncoder(input_cols=categorical_columns, target_col='target', fold=fold) train = encoder.fit_transform(train) test = encoder.transform(test)
# エンコーディング前の列を削除する train = train.drop(categorical_columns,axis=1) test = test.drop(categorical_columns,axis=1)
train = train.drop('target',axis=1)
SEED = 42 NFOLDS = 5
#####################################################3 ### LGBで学習、予測する関数の定義 ######################################################## def Train_and_Pred(train,target,test): # -------------------------------------- # パラメータ定義 # -------------------------------------- lgb_params = { 'objective': 'multiclass', 'boosting_type': 'gbdt', 'n_estimators': 50000, 'colsample_bytree': 0.5, 'subsample': 0.5, 'subsample_freq': 3, 'reg_alpha': 8, 'reg_lambda': 2, 'random_state': SEED, "bagging_fraction": 0.5520399476847848, "bagging_freq": 1, "feature_fraction": 0.4436319472771827, "lambda_l1": 0.01113869595673112, "lambda_l2": 8.706009358617911e-07, "learning_rate": 0.012307412937706345, "min_child_samples": 18, "num_leaves": 8, } # -------------------------------------- # 学習と予測 # -------------------------------------- kf = KFold(n_splits=NFOLDS, shuffle=True, random_state=SEED) lgb_oof = np.zeros(train.shape[0]) lgb_pred = 0 for fold, (trn_idx, val_idx) in enumerate(kf.split(X=train)): X_train, y_train = train.iloc[trn_idx], target.iloc[trn_idx] X_valid, y_valid = train.iloc[val_idx], target.iloc[val_idx] X_test = test # LightGBM model = lgb.LGBMClassifier(**lgb_params) model.fit(X_train, y_train, eval_set=(X_valid, y_valid), eval_metric='logloss', verbose=False, early_stopping_rounds=500 ) lgb_oof[val_idx] = model.predict(X_valid) lgb_pred += model.predict(X_test) / NFOLDS f1_macro = f1_score(y_valid, lgb_oof[val_idx], average='macro') print(f"fold {fold} lgb score: {f1_macro}") f1_macro = f1_score(target, lgb_oof, average='macro') print("+-" * 40) print(f"score: {f1_macro}") print(f"model score: {model.score(train, target)}") return f1_macro,lgb_pred
#学習と予測の実行 score,pred = Train_and_Pred(train,target,test)
# ------------------------------------------------------------------------------ # 提出ファイルの作成 # ------------------------------------------------------------------------------ #テスト結果の出力 submit_df = pd.DataFrame({'y': pred.round().astype(int)}) submit_df.index.name = 'id' submit_df.to_csv('submission.csv')
皆さんの知見をいただきたいのですが...
lgb_pred += model.predict(X_test) / NFOLDS
この箇所ではfold毎の平均予測値を算出していますので、例えばfold毎の予測値が6, 1, 1, 1, 1だったとすると2が算出されます。今回の場合は他クラス分類なので、最頻値の1を予測値として採用するのが適当かと。そうすると、関連部分の抜粋コードは以下のようになるのではないかと思っているのですが認識に相違はあるでしょうか。ただ、このコードにはまだカウント数が同値の複数の最頻値が出現した場合の対処コードを追加する必要があります。
from scipy.stats import mode ... def Train_and_Pred(train,target,test): ... lgb_pred = np.zeros(test.shape[0]*NFOLDS).reshape(-1, NFOLDS) ... lgb_pred[:, fold] = model.predict(X_test) ... score,pred = Train_and_Pred(train,target,test) ... #テスト結果の出力 value, count = mode(pred, axis=1) submit_df = pd.DataFrame({'y': value.ravel().astype(int)})
ところがこのコードを適用すると、LB0.13090となり、@Oreginさんの結果(LB 0.13157)より悪化してしまったという...^^;
ありがとうございます。 ご指摘の通り大小関係のない他クラス分類で、予測値の平均は処理として意味をなさないですね・・・。 Scoreが良かったのは、たまたまPublic評価用のデータに適合していただけですね。 最頻値を採用することが適切ですね。
BIZENさんの「最頻値」を取るほうが筋は通っている気がします。予測targetが、0:ボール, 1:ストライク, 2:ファウル…となっていて、0,0,2,2の「平均」は1となりますが、<ボール>と<ファウル>の間をとって、<ストライク>というのは理屈として変ですよね。
「平均」を取るLBの方が良いのは、予測targetが投球結果で↓これなので、0~3に偏るので偶然だと考えました。 0:ボール, 1:ストライク, 2:ファウル, 3:アウト, 4:シングルヒット, 5:二塁打, 6:三塁打, 7:ホームラン
@genki_Hさん、ありがとうございます。
ご指摘ありがとうございました。 予測値の最頻値を取るように修正したバージョンを投稿いたしました。 https://prob.space/competitions/npb/discussions/Oregin-Postb9d508bafe7dfccde206 少しだけ、LBが向上しました。