🏗️

【モデル編②】3モデル連携アーキテクチャ:競馬のプロセスを模倣する

に公開

【モデル編②】3モデル連携アーキテクチャ:競馬のプロセスを模倣する

はじめに:一枚岩モデルの限界

前回の記事で導入した「ランク学習」は、レース内の相対的な強さを評価できる強力な手法です。
しかし、競馬の予測はそれほど単純ではありません。

  • どの馬がハナを主張するのか?(展開
  • それによって全体のペースはどうなるのか?(ペース
  • ハイペースなら、どの馬が最後に脚を使えるのか?(上がり

これらの要素は複雑に絡み合っており、単一の巨大なモデルに「全部まとめて考えろ」と丸投げしても、なかなか安定した予測はできません。
そこで、**「人間がレースを分析するプロセス」**を模倣し、複数の専門家モデルを連携させるアーキテクチャを考案しました。

1. 3モデル連携アーキテクチャの全体像

レースの結果は、一連の因果関係によって決まります。この流れを模倣するため、予測プロセスを以下の3つのフェーズに分解し、それぞれを専門のモデルに担当させます。

  1. Model 1: 位置取り予測モデル (回帰)

    • 入力: 過去の成績、枠順、騎手など
    • 予測: 各馬がレースのどの位置につけるか(pred_position_score
  2. Model 2: 道中ペース予測モデル (回帰)

    • 入力: Model 1の予測結果(先行馬の数など)+基本特徴量
    • 予測: レース全体の道中のペース(pred_mid_pace
  3. Model 3: 上がり3Fタイム予測モデル (回帰)

    • 入力: Model 1, 2の予測結果+基本特徴量
    • 予測: 各馬が使うであろう上がり3Fのタイム(pred_last_3f_time

この構造のキモは、前段モデルの予測結果を、後段モデルの入力(特徴量)として使う点です。
これにより、「先行馬が多い(とModel 1が予測した)から、ペースは速くなる(とModel 2が予測する)」といった、ドミノ倒しのような予測が可能になります。

最終的に、これらのサブモデル群が生み出した予測結果(pred_position_score, pred_mid_pace, pred_last_3f_time)を全ての特徴量に加えて、メインのモデル(ランク学習 or タイム予測)が最終的な着順を予測します。

2. 実装の核心:パイプライン処理

この「予測結果のバケツリレー」は、src/keiba/services/analysis/application/training_service.py の中でパイプラインとして実装されています。

# c:\keiba_app\src\keiba\services\analysis\application\training_service.py

if __name__ == '__main__':
    # ... (データロード・特徴量生成) ...
    
    # モデル1: 位置取り
    model1 = train_model_1(df)
    df = apply_model_1_prediction(df, model1) # ★予測結果をdfに追加
    
    # モデル2: ペース
    model2 = train_model_2(df)
    df = apply_model_2_prediction(df, model2) # ★予測結果をdfに追加
    
    # モデル3: 上がり3F
    model3 = train_model_3(df)
    df = apply_model_3_prediction(df, model3) # ★予測結果をdfに追加
    
    # モデル4: 走破タイム (最終モデル)
    model4 = train_model_4(df)

apply_model_X_prediction 関数が、学習済みモデルを使って予測を行い、その結果を新しい列としてデータフレームに追加しています。
これにより、次のモデルは前のモデルの「思考結果」を踏まえて、より高度な判断を下すことができます。

3. 依存関係の厳格な管理

このアーキテクチャで最も重要なのが、データリーク(未来情報の参照)の徹底的な防止です。
例えば、Model 2(ペース予測)が、本来知るはずのないModel 3(上がり予測)の結果や、レースの最終着順を見てしまってはいけません。

この依存関係は、src/keiba/config/columns.py で定義された除外リストによって厳格に管理されています。

# c:\keiba_app\src\keiba\config\columns.py

# モデル2 (道中ペース予測) で除外するカラム
DROP_COLS_M2 = [
    'mid_pace',       # 自分自身の答え
    # レース結果(リーク)
    'last_3f_time',   # Model3以降で予測される情報
    'finish_time',    # 最終結果
    'rank',           # 最終結果
    ...
]

各モデルの学習時には、このリストに基づいて特徴量がフィルタリングされます。
これにより、「Model 2はModel 1の結果は知っているが、それ以降の未来は知らない」という状態を保証し、モデルがカンニングすることを防いでいます。

まとめ:専門家集団による分業体制

単一の万能モデルを目指すのではなく、プロセスを分解し、それぞれの専門家モデルに分業させる。
この3モデル連携アーキテクチャにより、以下のメリットが生まれました。

  • 解釈性の向上: 最終予測が外れた場合、どのサブモデルの予測が原因だったのかを追跡しやすくなります。
  • モデルの安定化: 各モデルはより単純なタスクに集中できるため、学習が安定し、改善も容易になります。
  • 柔軟な拡張性: 「上がり3F予測」モデルだけを、より高度なアルゴリズムに差し替える、といった改善が簡単に行えます。

次回は、この連携システムの根幹をなす「位置取り予測モデル」と「タイム予測モデル」が、開発の過程でどのように進化していったのか、その具体的な改善の道筋について解説します。

GitHubで編集を提案

Discussion