0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

403レースが暴いた「本当に効く補正・効かない補正」    —— 半年で作った競馬AIを、どう“育てて”いったのか —— シリーズ第二弾・V3の全記録

0
Posted at

稼働中のシステムをどう育てるか——競馬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

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?