競馬予測AI(機械学習)を作るとき、最初にやるべきなのはモデル選びではなく、「発走前に何が見えていて、なぜその馬を候補にしたか」を後から再現できる記録(判断台帳)を用意することです。これがないと、バックテストの良い数字が実力なのか、偶然なのか、データリークなのかを区別できません。
この記事では、シリーズ第1回として、全馬の判断を残すCSVの列設計、データの置き場所の分け方、発走前オッズの保存方法、自動購入に進まないためのルールをまとめます。予測対象は各馬が複勝圏(3着以内)に入る確率です。
この記事の結論
- 出力は「買う馬の一覧」ではなく、判断した全馬を1頭1行で残すCSVにする。
decision(buy / skip)とreason(理由)を別の列にし、見送りの原因を切り分けられるようにする。- 取得した原本・特徴量・判断・精算を別ディレクトリに置き、払戻金などの結果情報が判定に混ざらないようにする。
- 候補がゼロの日もCSVを出力し、「候補なし」と「処理が動かなかった」を区別する。
- 自動購入はせず、「候補CSV → 手動確認 → 疑似運用 → 少額検証」の順で進める。
なぜ的中率・回収率だけでは不十分か
的中率や回収率は重要な指標ですが、次のような理由でも「良い数字」は簡単に出てしまいます。
- 高配当が数回当たっただけ
- 結果確定後の情報(確定オッズ・払戻金など)が特徴量に混ざった
- 検証期間がたまたま相性の良い時期だった
そこで目標を「儲かること」ではなく、「同じ判断をあとから再現できること」に置きました。数字の良し悪しにかかわらず理由をたどれれば、自分の検証結果にだまされずに済みます。
補足すると、筆者は学生時代にもスクレイピングから自動購入まで含む競馬AIを作りましたが、精度が足りず買うほど損をする結果でした。今回はその反省から、記録の仕組みを先に作っています。
判断CSVの列設計(全馬1行)
候補にならなかった馬も含め、判断したすべての馬について次の列を残します。
| 列 | 中身 | 目的 |
|---|---|---|
race_id / horse_num | 競走IDと馬番 | 結合のキー |
start_time / predicted_at | 発走時刻と判定時刻 | その時点で使えた情報かを確認する |
odds_timestamp / minutes_before | オッズを取得した時刻 | オッズの鮮度を確認する |
p_place / odds_used | 推定確率と使った複勝オッズ | 条件を後から再計算する |
decision / reason | buy / skip と、その理由 | 候補と見送りを区別する |
model_id / strategy_id | モデルと戦略の版 | 比較の条件をそろえる |
input_manifest | 入力ファイルの一覧とハッシュ | 後日の再実行 |
decision = {
"race_id": race_id,
"horse_num": horse_num,
"predicted_at": snapshot_at.isoformat(),
"odds_timestamp": odds_at.isoformat(),
"p_place": float(p_place),
"strategy_id": "place-3cond-fuk-1.5-8.0",
"buy": bool(buy),
"reason": reason,
"model_id": model_id,
}
decision と reason を分ける理由
「確率が低くて見送った」のと「オッズが取得できず判断できなかった」のは、意味がまったく違います。理由の列があれば、候補数が急に減ったときの原因を次のように切り分けられます。
| 増えた reason | 疑う場所 |
|---|---|
missing_prerace_odds | データ収集(モデルの問題ではない) |
below_probability_threshold | 確率の分布、または市場の変化 |
また、候補がゼロの日もCSVは必ず出力します。出力がないと「候補がなかった」のか「処理が止まっていた」のか区別できないためです。
データの置き場所を用途別に分ける
raw/odds/2023-06-10_10-02-15.csv # 取得した原本。書き換えない
processed/features_10min.parquet # 作成時の特徴量スキーマも保存
reports/decisions/2023-06-10.csv # 候補・見送り・使ったオッズ
reports/settlements/2023-06-10.csv # 結果確定後の精算だけを追記
最も重要なのは、判断CSVと精算CSVを分けることです。同じ表に払戻金や確定オッズがあると、いずれ「便利な列」として学習や判定に紛れ込みます。物理的に分けておけば、この種のデータリークは起きません。
ファイル名にも日付・オッズの時点・モデル・戦略を入れます。
horse_decisions_YYYYMMDD_10min_model-v003_strategy-place.csv
後で「特定の年だけ成績が悪い」と気づいたとき、学習時期の問題か、10分前オッズの欠損か、特定開催の結合失敗かを、ファイル名とログから調べられます。
発走前オッズの保存方法(JRA-VAN DataLab.)
発走前オッズは JRA-VAN DataLab. で保存しています。DataLab. はWindows専用のため、MacではParallels Desktop上のWindowsで定時保存を動かしました。
注意点は2つありました。
- 手動保存は抜けが出る:数週間たってから欠損に気づいたため、定時実行に切り替えた。
- 出力ファイル名が固定で上書きされる:取得時刻が分からなくなるため、保存のたびに取得時刻入りのファイル名へ退避するようにした。
「何時の値か」が分からないオッズは、検証に使えません。
自動購入に進まないためのルール
自動化は正しい判断を速く実行しますが、壊れた結合や古いオッズでも同じように速く実行してしまいます。そのため、進め方を次の順に固定しました。
候補CSV → 手動確認 → 疑似運用 → 少額検証 →(条件がそろってから)自動化
失敗時のルールも先に決めています。
- オッズ取得失敗・ログ出力失敗・レースID不一致のいずれかが起きたら、前回値で続けずに見送り、理由を残す。
- 予測スコアが高くても、入力の鮮度を証明できなければその判断は無効。
次のような日は精算にも進めません。
- 1日の候補件数が異常に多い
- 同じレースに矛盾する判断が複数出ている
- オッズの時刻が発走後になっている
まとめ:モデル作りに進む前のチェックリスト
- 任意の1日について「原本 → 特徴量 → 判断CSV → 精算CSV」をやり直せる
- 候補がゼロの日でもCSVが出る
- 全行にモデルの版と判定理由が入っている
この3つがそろってから、モデルの精度改善に進みました。この準備があったことで、後の回で出てくる「良すぎる数字」を疑うことができています。
シリーズの他の記事
注意:本記事は競馬データを用いた研究・技術解説です。利益や的中を保証するものではなく、馬券の購入を勧めるものでもありません。