ポンコツオヤジの競馬×機械学習2
前回アップしてからだいぶ日が経ちましたがその後の経過です。
randomforestにて競馬の順位を予測するモデルを作成しましたが精度不足だった為、精度向上目的で外れ値の処理、入力の選定など行いましたが精度上がらず。
次にLightGBMにてランキング予測する手法に切り替えてみましたがTOP3の精度としては75%程度。3連系が当たるときはありますが、上位人気ばっかりでオッズがつかず。。。
LightGBMでの結果
2/3,4週:-2000円
2/10,11週:-1000円
という状況であまりうまくいってません。。。
そもそも出てきた結果をそのまま使う場合何故その馬なのかがわからない。
ということで原点に戻って馬一頭一頭と向き合ってその馬の特性に合うかという観点で予測する本来のスタイル(言ってませんでしたが)にします。
で、そうして見えてきた傾向をモデルとして準備。というアプローチにします。
ただ、今回過去データを使って検証していくうちに今まで見てなかった傾向が見れたのでそれらも使っていけるかなと思ってます。
今後面白そうな傾向があったらこちらで紹介もしていこうと思います。
あ、最後に2/17,18週の結果ですが
+7000円
フェブラリーSでセキフウを本命にしていたので捲れました。
それ以外の結果は散々。。。
さ!来週に向けて振り返りから!
以下に今回検証した内容を紹介しておきます。
1. データの前処理
欠損値処理: fillna メソッドを使用して、NaN値を適切な方法で補完
特定の列の欠損値に-1を入れる: df['column_name'].fillna(-1,inplace=True)。
# 欠損値処理
df.fillna(method='ffill', inplace=True)
# 特定の列の欠損値に-1を入れる
df['specific_column'].fillna(-1, inplace=True)
2. 特徴量エンジニアリング
時系列データの処理: 過去のレース結果や日付の差分を新しい列として追加。
ラグ特徴量の作成: shift メソッドを使用して、過去のデータを取得。
# 時系列データの処理
df['Date'] = pd.to_datetime(df['Date'])
df['DateDiff'] = (df['Date'] - df['Date'].shift()).dt.days
# ラグ特徴量の作成
for feature in Features:
for i in range(1, 7):
df[f'{feature}{i}'] = df.groupby('KettoNum')[f'{feature}{i}'].shift(-i)
df[f'{feature}{i}'].fillna(method='ffill', inplace=True)
3. LightGBM モデルの学習
LGBMRanker モデルを使用してランキング問題を解決。
学習時の評価指標: NDCG、MAP など。
ハイパーパラメータの最適化: optuna ライブラリを使用して最適なハイパーパラメータを見つける。(5参照)
import lightgbm as lgb
# データの分割
x_train, y_train, group = train_test_split(train)
x_test, y_test, eval_group = train_test_split(test)
# モデルの学習
model = lgb.LGBMRanker(random_state=0)
model.fit(x_train, y_train, group=group, eval_set=[(x_test, y_test)], eval_group=[list(eval_group)],
eval_metric='ndcg', eval_at=[1, 2, 3])
4. モデルの保存と読み込み
学習したモデルを保存: booster_.save_model メソッドを使用して学習済みモデルを保存。
モデルの読み込み: lgb.Booster クラスを使用して保存したモデルを読み込み。
# モデルの保存
model.booster_.save_model('lambdarank_model.txt')
# モデルの読み込み
loaded_model = lgb.Booster(model_file='lambdarank_model.txt')
5. Optuna を使用したハイパーパラメータ最適化
optuna ライブラリを使用してハイパーパラメータの最適化。
最適なハイパーパラメータの保存と読み込み。
import optuna
# 定義した目的関数
def objective(trial):
params = {
'num_leaves': trial.suggest_int('num_leaves', 10, 200),
'learning_rate': trial.suggest_loguniform('learning_rate', 1e-4, 1.0),
'feature_fraction': trial.suggest_uniform('feature_fraction', 0.1, 1.0),
# 他のハイパーパラメータも追加
}
model = lgb.LGBMRanker(**params, random_state=0)
model.fit(x_train, y_train, group=group, eval_set=[(x_test, y_test)], eval_group=[list(eval_group)], eval_metric='ndcg', eval_at=[1, 2, 3])
ndcg_score = model.evals_result_['valid_0']['ndcg@3'][-1]
return ndcg_score
study = optuna.create_study(direction='maximize')
study.optimize(objective, n_trials=100)
# 最適なハイパーパラメータの保存
with open('best_params.pkl', 'wb') as f:
pickle.dump(study.best_trial.params, f)