レース予想(1:概説)

レース予想方法

直近3走×時系列モデルで“今”を評価する

今回は、レースIDを指定して出走各馬の直近傾向を数値化し、スタッキング構成の時系列モデルでスコアリング → Excelに一括転記するところまでを自動化しました。記事では処理の全体像と、学習時との違い、仕上げのExcel転記だけをシンプルに紹介します。


全体の流れ

  1. レースを指定して、そのレースの出走表・馬/騎手/調教師などの基本情報を取得
  2. 自前の過去データベース(horse_db.csv や血統表)と縦に結合
  3. 直近3走にフォーカスした前処理・特徴量化
  4. 学習時と同一のエンコード&スケーリングを適用
  5. LSTM+SetTransformer+MoE+アテンションのハイブリッドで各馬スコアを算出
  6. 馬名・騎手名などと合体して見やすいテーブル化
  7. 仕上げにExcel(既存ブックの「レース」シート)へ一括貼り付け

予想の考え方(モデルの“目線”)

  • 直近に重心:各馬の履歴から**直近3走(sequence_length=3)**を切り出し、
    • 体重変化・距離/斤量の“その馬の平均からのズレ”
    • クラス(距離カテゴリ×芝/ダ)に対する相対性
    • 騎手成績や騎手×馬のコンビ相性
    • 走破速度・上りのその馬×同条件での過去平均
    • **血統の得意傾向(距離・脚質・馬場・芝ダ)**の累積指標
      を時系列ベクトル化して評価します。
  • 似た履歴の“なぞり”:直近シーケンスが過去のどの時点と似ているかをコサイン類似度で数値化(安全策込み)。「今の状態」が“良かった頃”に近いかどうかを一緒に見ます。
  • モデル構成
    • LSTMで時系列の文脈を抽出 → Temporal Attentionで重要区間に重みづけ
    • **Mixture-of-Experts(MoE)**で複数の見方を合成
    • SetTransformerでレース内の“相対比較”を学習
    • 最後はスタッキングで2系統の出力を統合し、各馬のスコアに落とします。

学習時との“違い”はここ!

“再学習”はせず、学習時の処理をそのまま当てるのがポイント。違いと注意点をまとめます。

  • 同一のラベルエンコード
    • 学習時に保存した label_encoders.pkl をそのまま適用。
    • 未知カテゴリは -1 にフォールバック(学習時と同ルール)。
  • 同一のスケーリング
    • scale_stats.jsonmean/std と採用カラム順を再利用。
    • 分母0対策やNaN/Infのサニタイズも同じ。
  • 同一の埋め込み次元
    • embedding_info.json入力/出力次元を厳密一致させてから重み読込。
    • category_maps.json を使い、IDを学習時の埋め込みID空間にマッピング
  • 学習時と“同じ列順”でモデルに渡す
    • main_feature_order.json に保存した特徴量の順番を再現。
    • 足りない列は 0 で補完(学習と互換を崩さない)。
  • 履歴の扱い
    • 対象レースの該当馬のみに履歴をフィルタ。
    • シーケンス不足は末行パディングで長さ3に統一(学習時と同様の扱い)。

出力と順位づけ

最終的に、各馬を1行にまとめた見やすいテーブルを作ります。

  • 作る列(例)
    • 枠番 / 馬番 / 馬名 / 性齢 / 斤量 / 馬体重 / 体重増減 / 騎手名
    • 単勝 / 人気 / スコア順位(大きいスコア=上位)/ 過去履歴数
  • スコアは降順でランキング。同点は最小順位(1,1,3…)で採番。
  • ブログでは、上位数頭について**「なぜ上がったか」**を簡潔に言語化(例:
    • 直近の体重推移が“良かった頃”に近い
    • 同距離帯×芝/ダでの過去速度が相対的に高い
    • 騎手の直近勝率コンビ相性が良い
    • 血統的に距離適性が積み上がっている …など)

Excel への一括転記

既存のExcelブック(「Iwasshoi8v1」)の**「レース」シート**に、9行目から横並びで書き込みます。列対応は以下の通り。

Excel列データ列
A列枠番
B列馬番
C列馬名
D列性齢
E列斤量
F列馬体重
G列体重増減
H列騎手名
I列単勝
J列人気
K列スコア順位
L列過去履歴数
  • ブック名に 「Iwasshoi8v1」 を含むファイルが開かれている前提(見つからなければ明示エラー)。
  • シート名は 「レース」 固定。
  • 画面更新/自動計算を一時的にOFFにして高速化 → 最後に元へ戻す

start_row = 9
n = len(df_out)
if n == 0:
    raise RuntimeError("書き込む行がありません(df_out が空)")

excel = win32.Dispatch("Excel.Application")
target_book = None
for wb in excel.Workbooks:
    if "Iwasshoi8v1" in wb.Name:
        target_book = wb
        break
if target_book is None:
    raise RuntimeError("❌ ブック『Iwasshoi8v1』が開かれていません。Excelで開いてから実行してください。")

try:
    ws = target_book.Worksheets("レース")
except:
    raise RuntimeError("❌ シート『レース』がブックに存在しません。確認してください。")

# 画面更新/再計算オフで少し高速化
prev_calc = excel.Calculation
excel.ScreenUpdating = False
excel.EnableEvents   = False
excel.Calculation    = -4135

try:
    for i, row in df_out.iterrows():
        r = start_row + i
        ws.Cells(r, 1).Value  = "" if pd.isna(row["枠番"])      else row["枠番"]
        ws.Cells(r, 2).Value  = "" if pd.isna(row["馬番"])      else row["馬番"]
        ws.Cells(r, 3).Value  = "" if pd.isna(row["馬名"])      else str(row["馬名"])
        ws.Cells(r, 4).Value  = "" if pd.isna(row["性齢"])      else str(row["性齢"])
        ws.Cells(r, 5).Value  = "" if pd.isna(row["斤量"])      else float(row["斤量"])
        ws.Cells(r, 6).Value  = "" if pd.isna(row["馬体重"])    else float(row["馬体重"])
        ws.Cells(r, 7).Value  = "" if pd.isna(row["体重増減"])  else float(row["体重増減"])
        ws.Cells(r, 8).Value  = "" if pd.isna(row["騎手名"])    else str(row["騎手名"])
        ws.Cells(r, 9).Value  = "" if pd.isna(row["単勝"])      else float(row["単勝"])
        ws.Cells(r,10).Value  = "" if pd.isna(row["人気"])      else float(row["人気"])
        ws.Cells(r,11).Value  = "" if pd.isna(row["スコア順位"]) else int(row["スコア順位"])
        ws.Cells(r,12).Value  = int(row["過去履歴数"])

    rb = ws.Range(f"A{start_row}:L{start_row}").Value

finally:
    excel.Calculation  = prev_calc
    excel.EnableEvents = True
    excel.ScreenUpdating = True

print("✅ 一括書き込み 完了")

失敗時のチェックポイント

  • ブックが開いているか/シート名が正しいか
  • df_out が空でないか(馬番の数がおかしくないか)
  • ラベルエンコード・スケール統計・埋め込み情報のファイル不整合がないか
  • 対象レースの馬IDがきちんと取れているか(一致しないとdf_predsが空に)

使っている主な特徴量

  • 距離/斤量の“自馬平均からの差分”(3,5,7走で移動平均&z-score)
  • クラス基準(距離カテゴリ×芝ダ)の過去平均との差
  • 馬体重の前走比/移動平均/z-score(推移のトレンド値)
  • 騎手勝率(累積)と騎手×馬コンビ勝率
  • 過去の走破速度・上りの自馬平均(同芝/ダ)
  • **脚質のビン分け(逃げ・先行・差し・追込)**と直近履歴
  • 血統キーからの得意カテゴリ(距離・脚質・馬場・芝ダ)の累積最多

まとめ

  • 直近3走相対指標で“今の出来”を定量化
  • 学習時の前処理・エンコード・スケール・埋め込み完全踏襲
  • スコアはレース内相対で順位づけし、Excelへワンクリックで反映
  • ブログはスコア上位馬の根拠を短く添えるだけで、説明力がアップします

次回、学習時をもとに推論する再のパッケージについて説明します。

コメント

タイトルとURLをコピーしました