稼働中のシステムをどう育てるか——競馬AI V3全記録
対象期間:2026年4月1日〜5月18日 制作者:yuji
「競馬予測AIが完成した」——そう思っていた。
前回の記事では、ゼロから半年で17本のPythonスクリプト・4系統の並列アーキテクチャ・複勝3着内率51%を達成するまでの全記録を書いた。それが「完成」だと思っていた。
甘かった。
稼働を続けるシステムには、新たな問いが次々と生まれる。「どの補正が効いていて、どれがノイズか」「改善しようとすると別の場所が壊れる」「同じアルゴリズムのコピーが、なぜ違う精度を出すのか」——。
V3期間(2026年4/1〜5/18)の403レース・5,483頭が証明したのは、**「設計とは削除の連続である」**という事実だった。
V3期間サマリー
| 指標 | 値 |
|---|---|
| 新規・大幅改版スクリプト | 12本 |
| 検証レース数 | 403R(2/28〜5/17確定版) |
| V5 3着内率(最終) | 45.8%(ローテーション補正削除後) |
| 両者一致シグナル | 53.8%(V3最大の実践的発見) |
| 最重要アーキテクチャ変革 | 補正管理層(hosei_mixer)の誕生 |
1. V3最大の変革——「補正管理層」の誕生
問題:予想エンジンを触るたびに壊れるリスク
V3期間、seiki_keibayosou_app_v5.py(V5)はNo.19〜65と47回の変更を重ねた。開発初期の「改善したいときはV5本体を直接修正する」スタイルが、何度も問題を引き起こした。
- 補正係数を変えたら別の場所に波及した
- バグを直したら別の機能が壊れた
- 「どこを変えたら何が変わるか」の追跡が不可能になった
そこで4/22に設計した解決策が hosei_mixer_v1.py(補正管理層) だ。
┌────────────────────────────────────────┐
│ seiki_keibayosou_app_v5.py │
│ (予想エンジン層) │
│ ← 基本的に触らない・壊さない ← │
└────────────────────────────────────────┘
↑ CSV連携
tekisei_hoseichi.csv
basho_correction.csv
↑
┌────────────────────────────────────────┐
│ hosei_mixer_v1.py │
│ (補正管理層) │
│ ← ここで実験する・ここで育てる ← │
└────────────────────────────────────────┘
設計原則:V5予想エンジンは絶対に触らない。補正は外部から制御する。
失敗しても補正管理層だけをロールバックすれば済む。この2層分離設計により、「観測→補正値調整→再検証」サイクルが初めて安定して回るようになった。
# V5予想エンジンを一切変更せず、外部CSVから補正値を注入する
def save_tekisei_csv(horse_corrections: dict, mode: str = "seiki"):
"""
mode="seiki" → 本番用 tekisei_hoseichi_seiki.csv
mode="kensyou" → 検証用 tekisei_hoseichi_kensyou.csv
"""
path = TEKISEI_CSV_SEIKI if mode == "seiki" else TEKISEI_CSV_KENSYOU
with open(path, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['horse_id', 'correction'])
writer.writeheader()
for hid, val in horse_corrections.items():
writer.writerow({'horse_id': hid, 'correction': val})
hosei_mixerはV3期間でNo.01〜24と24回の変革を経て、5/18時点でv1.6として本格稼働。補正履歴はhosei_history.csvに自動蓄積され、「どの補正が効いたか」を振り返れるようになった。
2. ローテーション補正の「追加→検証→削除」
V3期間で最も学びが大きかった失敗
5/10〜11に実装したローテーション補正(V5 No.48〜54)は、「前走間隔と前走成績の組み合わせで精度が上がるのでは」という仮説から生まれた。V5初の「時系列要素」として、満を持して投入した。
# ローテーション補正の実装(V5 No.49〜54)
ROTATION_TABLE = {
(1, 'win'): +0.08, # 連勝狙いの中1週
(2, 'place'): +0.05, # 好調維持の中2週
(8, 'none'): -0.10, # 長休明け(8週以上)
# ...
}
def calc_rotation_bonus(weeks_since_last: int, last_result: str) -> float:
key = (weeks_since_last, last_result)
return ROTATION_TABLE.get(key, 0.0)
403R同条件比較——結果は明快だった
| 条件 | 1着率 | 3着内率 |
|---|---|---|
| ローテーション補正あり(旧) | 15.4% | 44.0% |
| ローテーション補正削除(現行) | 17.4%(+2.0pt ✅) | 45.8%(+1.8pt ✅) |
| V58(元からローテなし) | 20.6% | 46.1%(残差0.3pt) |
削除した方が精度が上がった。ローテーション補正はノイズだった。
なぜノイズになったか
競馬の「前走間隔」は単独では信号にならない。「中2週で前走1着」でも馬によって意味が全く異なる。適切にモデル化するには馬ごとの体質・厩舎方針・距離変化を複合的に考慮する必要があり、シンプルなルールテーブルでは逆効果になった。
時系列要素はV4期間で改めて設計し直す——これがV3の結論だ。「実装→検証→削除」というサイクルが完結した。
3. 同じアルゴリズムのコピーがなぜ違う精度を出したか
V3期間で最も知的に面白かった問題
V5(本番・正規系)とV58(検証用クローン)は同じアルゴリズムを持つ「twins」として設計した。しかし4/17〜21の59R比較検証で、V58がV5を約2pt上回るという事実が判明した。
「同じアルゴリズムなのになぜ差が出るのか」——これを体系的に追究した。
| 項目 | V5(正規) | V58(検証) |
|---|---|---|
| 3着内率 | 44.0%(旧)→ 45.8%(削除後) | 46.1% |
| 1着率 | 17.4% | 20.6% |
| 残差 | ← 3着内0.3pt / 1着3.2pt → |
差異の特定——2つの要因に絞られた
① ローテーション補正(V5のみ存在)
→ 削除により+1.8pt改善。V58との3着内残差は0.3ptに縮小。✅ 解決済み
② trainer_b 閾値設計の差
# V5(旧設計):シンプルだが甘い
def calc_trainer_b_bonus_v5(trainer_g1_wins: int) -> float:
if trainer_g1_wins >= 10:
return +1.0
else:
return 0.0 # ← ゼロ止め。実績薄調教師を「普通」と評価してしまう
# V58(厳格設計):実績薄にペナルティを課す
def calc_trainer_b_bonus_v58(trainer_g1_wins: int) -> float:
if trainer_g1_wins >= 10: return +1.5
elif trainer_g1_wins >= 5: return +1.0
else: return -0.5 # ← これが差を生んでいた
「シンプルで厳格な評価軸がシステムを精緻にする」——これがtwins問題の結論だ。
実績のない調教師を「ゼロ評価(普通)」するのか「マイナス評価(弱い)」するのか。この設計の差が、1着率3.2ptという数値の差になって現れていた。V4期間のtrainer_b逆移植として引き継がれる。
4. twins問題が生んだ副産物——V3最大の発見
追究の過程で、想定外の発見があった。「V5とV58の予測が一致したときだけ購入する」というフィルターを検証したところ——
🏆 両者一致シグナル:3着内率 53.8%
| 評価区分 | R数 | 3着内率 | 1着率 |
|---|---|---|---|
| ➡ 両者一致(V5 = V58) | 171R | 53.8% 🏆 | 21.1% |
| ⬆ UP(V5 > V58) | 129R | 42.6% | 11.6% |
| ⬆⬆ 大幅UP(V5が大幅上回る) | 82R | 25.6% ⚠️ | 9.8% |
ランダム期待値の約3倍。V3期間最大の実践的発見。
「大幅UP(正規が非正規を大きく上回る)」は3着内率25.6%と最低水準。V5が単独で高評価しているケースは「過剰補正の疑い」として割引くのが正解。2つのアルゴリズムの不一致の大きさが、不確実性の指標になる。
5. ローテーション削除後に顕在化した新問題
全体精度は改善した。しかし詳細検証で新たな問題が浮上した。「会場別・ランク別の分岐」だ。
競馬場別:V5(ローテ無し)vs V58 の3着内率
| 競馬場 | V5(A) | V58(B) | 差(A−B) | 推奨 |
|---|---|---|---|---|
| 🏇 中山 | 54.7% | 46.5% | +8.2pt ✅✅ | A主軸 |
| 🏇 阪神 | 54.4% | 52.2% | +2.2pt ✅ | A優位 |
| 🏇 新潟 | 44.4% | 47.2% | −2.8pt ⚠️ | B参照 |
| 🏇 東京 | 37.0% | 40.0% | −3.0pt ⚠️ | B参照 |
| 🏇 中京 | 34.4% | 41.9% | −7.5pt ⚠️⚠️ | B優先 |
| 🏇 福島 | 34.3% | 28.6% | +5.7pt | 要注意 |
騎手ランク別:逆転現象
| ランク | V5(A)3着内率 | V58(B)3着内率 | 差 |
|---|---|---|---|
| ランクB | 58.0%(69R) | 52.1%(73R) | +5.9pt ✅✅ |
| ランクA | 42.9%(112R) | 41.4%(99R) | +1.5pt |
| ランクC | 26.7%(90R) | 32.1%(134R) | −5.4pt ⚠️ |
ランクC騎手でV58が逆転優位に。ローテーション補正が「ランクCの下方抑制として機能していた部分」があり、削除によってその抑制が失われた可能性がある。
「ローテーション補正はノイズだった」は全体の話。条件によっては補正として機能していた部分もある。設計の削除はシンプルでも、その影響は複雑だ。
6. 403R確定版——最終成績表
■ 競馬場別 予想1位の3着内率(2/28〜5/17)
| 競馬場 | 3着内率 | 1着率 | R数 |
|---|---|---|---|
| 🏇 阪神 | 52.2% | 22.2% | 90R |
| 🏇 新潟 | 47.2% | 25.0% | 36R |
| 🏇 中山 | 46.5% | 15.1% | 86R |
| 🏇 中京 | 41.9% | 9.7% | 31R |
| 🏇 東京 | 40.0% | 11.1% | 45R |
| 🏇 福島 | 28.6% ⚠️ | 8.6% | 35R |
| ◎ 総合 | 45.8% | 17.4% | 403R |
総合45.8%はランダム期待値(約17〜20%)の約2.2倍を403Rの大サンプルで継続維持。
7. 実践的活用ガイド(V3確定版)
① 第一フィルター:両者一致を確認
→ 3着内53.8%・1着率21.1%の最強シグナル。これだけで買うレースを約40%絞れる。
② 得意会場を優先
→ 阪神・新潟・中山(47〜54%の高精度ゾーン)。中京・東京・ランクCはV58を参照。
③ 騎手ランクBに注目
→ V5使用時にランクAより**+5.9pt高い58.0%**。ランクA騎手は人気先行で過剰評価されやすい。
④ 大幅UPは割引
→ 正規が非正規を大きく上回る時は過剰補正の疑い(25.6%)。見送りが正解。
⑤ 福島は要注意
→ 全競馬場最低28.6%。可能なら見送り推奨。
8. V3から得た設計哲学
半年の構築期(V1〜V2)を経て、V3期間で学んだことを一言で言うと:
「削除は追加より難しく、価値がある」
ローテーション補正を追加するのは簡単だった。しかし403Rのデータが「これはノイズだ」と告げるまで検証し、削除を決断し、そして削除後に新たな問題が顕在化することを受け入れる——このサイクルこそがシステムを本当に育てる過程だ。
補正管理層(hosei_mixer)の誕生も、「追加の失敗を安全に試せる設計」があって初めて意味を持つ。2層分離アーキテクチャは**「失敗コスト」を下げるための構造的な解答**だった。
V3期間確定コーディングルール
① 修正のたびNo.XX採番・省略禁止
② コメント変更はgrepで実コード照合
(コメントと実コードの不一致バグを2回踏んだ)
③ py修正後はscan_dependencies.py実行
(全22ファイルの依存関係を自動スキャン)
④ バッチフォルダ更新時は_batch_dirs同時更新(絶対ルール)
ルールはすべて「実際に踏んだバグ」から生まれている。理論ではなく実データが設計を変える——第一弾で書いた哲学は、ルール管理にも貫かれている。
おわりに——V4期間へ
| 課題 | 内容 |
|---|---|
| 🔜 trainer_b逆移植 | V58のペナルティ設計をV5に逆移植。1着率+3.2ptを目指す |
| 🔜 会場×アルゴリズム戦略 | 中山・阪神はV5主軸、中京・東京はV58参照を実運用検証 |
| 🔜 宝塚記念(6月) | G1騎手補正confidence=1.0到達。初の満信頼度G1検証 |
| 🔜 時系列要素の再設計 | ローテーション補正の失敗を踏まえ、複合設計で再実装 |
V3最大の発見は数値ではなく設計思想だった。
「実験できる構造が、学習できるシステムを作る」
次回(第3弾):V4期間 trainer_b逆移植・会場×アルゴリズム戦略の実証結果を予定。
seiki_keibayosou_app_v5.py v5.6(No.65) / hosei_mixer_v1.py v1.6(No.24)
403R / 5,483 horses / 2026-02-28〜2026-05-17