はじめに¶
私は競馬予想AIの開発をしています。動画で制作過程の解説をしています。良ければ見ていってください。
また、共有するソースの一部は有料のものを使ってます。
同じように分析したい方は、以下の記事から入手ください。
6.セカンドモデルの作成¶
前回の話からセカンドモデルが決定したので、
本Notebookではその作成方法を示す
6-0.セカンドモデルの説明¶
モデル作成の動機¶
血統情報を考慮したモデルの作成がしたい
モデルの目的¶
出走馬の血統情報と産駒たちの
勝率を追加したモデルを作成する
確認したい仮説¶
産駒の勝率と血統の馬IDから、
同じ血統を持つ競走馬の勝率を考慮した
モデルを学習できると考えた
特徴量¶
ファーストモデル+
父, 母, 母父, 母母父の馬ID+
父, 母, 母父, 母母父の産駒の勝率
目的変数¶
1着なら1, そうでないなら0のバイナリ
6-1.下準備¶
ソースの一部は有料のものを使ってます。
同じように分析したい方は、以下の記事から入手ください。
import pathlib
import warnings
import sys
sys.path.append(".")
sys.path.append("..")
from src.model_manager.lgbm_manager import LightGBMModelManager # noqa
from src.core.meta.bet_name_meta import BetName # noqa
from src.data_manager.preprocess_tools import DataPreProcessor # noqa
from src.data_manager.data_loader import DataLoader # noqa
warnings.filterwarnings("ignore")
root_dir = pathlib.Path(".").absolute().parent
dbpath = root_dir / "data" / "keibadata.db"
start_year = 2000 # DBが持つ最古の年を指定
split_year = 2014 # 学習対象期間の開始年を指定
target_year = 2019 # テスト対象期間の開始年を指定
end_year = 2023 # テスト対象期間の終了年を指定 (当然DBに対象年のデータがあること)
# 各種インスタンスの作成
data_loader = DataLoader(
start_year,
end_year,
dbpath=dbpath # dbpathは各種環境に合わせてパスを指定してください。絶対パス推奨
)
dataPreP = DataPreProcessor()
df = data_loader.load_racedata()
6-2.血統情報のロード¶
今回から新たに血統情報を読み込むメソッドを追加data_loader.load_horseblood()
を実行する
dfblood = data_loader.load_horseblood()
6-3.前処理の実行¶
今回より血統情報を追加する前提項目10の前処理を追加dataPreP.exec_pipeline
メソッドに第二,第三引数を指定することで前提項目10が実行される
df = dataPreP.exec_pipeline(df, dfblood, ["s", "b", "bs", "bbs", "f"])
意図しない血統情報を指定すると警告ログを出して知らせてくれるので、適宜見直してください
【警告ログイメージ】
一応カラム一覧を確認
df.columns
以下が追加した血統情報たち
'stallionId', 'stallionName', 'breedId',
'breedName', 'bStallionId', 'bStallionName',
'b2StallionId', 'b2StallionName'
6-4.モデル作成用インスタンス作成¶
第一引数で指定したパスに学習したモデル情報を保存するためのインスタンスを作成します
lgbm_model_manager = LightGBMModelManager(
# modelsディレクトリ配下に作成したいモデル名のフォルダパスを指定。
# フォルダパスは絶対パスにすると安全です。
root_dir / "models" / "second_model", # セカンドモデルのモデルID
split_year,
target_year,
end_year
)
6-5.モデル作成の準備¶
学習に必要な説明変数と目的変数の作成およびインスタンスへの登録
やってることは行ごとにコメントを残してます
# 説明変数にするカラム
feature_columns = [
'distance',
'number',
'boxNum',
'odds',
'favorite',
'age',
'jweight',
'weight',
'gl',
'race_span',
"raceGrade", # グレード情報を追加
] + dataPreP.encoding_columns
# 血統情報を追加
feature_columns += ["stallionId", "breedId", "bStallionId", "b2StallionId"]
# 勝率情報の追加(6-6節で解説してます)
feature_columns += ['winR_stallion', 'winR_breed',
'winR_bStallion', 'winR_b2Stallion']
# 目的変数用のカラム
objective_column = "label_in1"
# 説明変数と目的変数をモデル作成用のインスタンスへセット
lgbm_model_manager.set_feature_and_objective_columns(
feature_columns, objective_column)
# 目的変数の作成: 1着のデータに正解フラグを立てる処理を実行
df = lgbm_model_manager.add_objective_column_to_df(df, "label", 1)
6-6.データセットの作成¶
今回から指定したカテゴリの勝率を計算する処理を追加lgbm_model_manager.make_dataset_mapping
メソッドを実行する際に、第二,第三引数を指定する
第二引数では計算したいカテゴリ情報リストのリスト
第三引数ではさらに細かく集計する分類を指定する
下記の例では、馬場(field
)と距離カテゴリ(dist_cat
)ごとに、
父(["stallionId"]
), 母(["breedId"]
), 母父(["bStallionId"]
), 母母父(["b2StallionId"]
)の産駒たちの勝率を計算する
ちなみに、母×父の産駒たちの勝率を計算したい場合は、["stallionId", "breedId"]
と指定する
dataset_mapping = lgbm_model_manager.make_dataset_mapping(
df,
target_category=[["stallionId"], ["breedId"],
["bStallionId"], ["b2StallionId"]],
target_sub_category=["field", "dist_cat"]
)
# 上で作成したデータセットのマッピングをセットする
dataset_mapping = lgbm_model_manager.setup_dataset(dataset_mapping)
実際に追加されているか確認
dataset_mapping["2019first"].train.columns
一番最後にある
'winR_stallion', 'winR_breed', 'winR_bStallion', 'winR_b2Stallion'
の四つが勝率情報
6-7.モデル作成実行¶
とくに追加したものはないので、いつも通りにモデル作成の実行
# 学習用パラメータ(ここでは適当に設定しておく)
params = {
'boosting_type': 'gbdt',
# 二値分類
'objective': 'binary',
'metric': 'auc',
'verbose': 0,
'seed': 77777,
'learning_rate': 0.01,
"n_estimators": 10000
}
lgbm_model_manager.train_all(
params,
dataset_mapping,
stopping_rounds=500, # ここで指定した値を超えるまでは、early stopさせない
val_num=250 # ログを出力するスパン
)
for dataset_dict in dataset_mapping.values():
lgbm_model_manager.load_model(dataset_dict.name)
lgbm_model_manager.predict(dataset_dict)
6-8.モデルのエクスポート¶
モデルのエクスポートをするためには、モデルの成績を先に計算しておく必要がある
モデルの成績は以下を計算する
- 収支の計算
- 基礎統計の計算
- オッズグラフの計算
収支の計算¶
収支計算はメンテが出来てないので少し泥臭いが、以下のコードを実行して貰えればよい
現在のソースでは単勝の収支しか計算できない
bet_mode = BetName.tan
bet_column = lgbm_model_manager.get_bet_column(bet_mode=bet_mode)
pl_column = lgbm_model_manager.get_profit_loss_column(bet_mode=bet_mode)
for dataset_dict in dataset_mapping.values():
lgbm_model_manager.set_bet_column(dataset_dict, bet_mode)
_, dfbetva, dfbette = lgbm_model_manager.merge_dataframe_data(
dataset_mapping, mode=True)
dfbetva, dfbette = lgbm_model_manager.generate_profit_loss(
dfbetva, dfbette, bet_mode)
dfbette[["raceDate", "raceId", "label", "favorite", bet_column, pl_column]]
基礎統計の計算¶
基礎統計では回収率と的中率および人気別のベット回数の集計を行う
以下のコードを実行するだけ
lgbm_model_manager.basic_analyze(dataset_mapping)
オッズグラフの計算¶
オッズグラフの話は以下の動画を確認ください
モデルを評価する上で最も重要なことを話しています
dftrain, dfvalid, dftest = lgbm_model_manager.merge_dataframe_data(
dataset_mapping,
mode=True
)
summary_dict = lgbm_model_manager.gegnerate_odds_graph(
dftrain, dfvalid, dftest, bet_mode)
print("'test'データのオッズグラフを確認")
summary_dict["test"].fillna(0)
モデルのエクスポート
以下を実行することでモデルの成績をエクスポートできる
lgbm_model_manager.export_model_info()
6-9.性能の確認(WEBアプリ起動)¶
以下のコードを実行するとWEBアプリが起動します
! python ../app_keiba/manage.py makemigrations
! python ../app_keiba/manage.py migrate
! echo server launch OK
# ! python ../app_keiba/manage.py runserver 12345
「server launch OK」の表示がでたら以下のリンクをクリックしてWEBアプリへアクセス
6-10.結果
モデルID | 支持率OGS | 回収率OGS | AonBOGS | |
---|---|---|---|---|
1 | second_model | 1.02706 | -4.55201 | 3.09785 |
2 | first_model (baseline) |
0.41924 | -7.64492 |
結果から、ファーストモデルに比べてセカンドモデルでは、加重平均的に回収率を3.098ポイント上回っている
コメント