スリットST推定の改善検討(仮説と検証計画)
スリット予想(スタート予想図)と 1 マーク予想が使う「予測 ST」の算出方法を、
現状の 全国平均 ST 据え置き から改善するための仮説カタログと検証計画。
本書は検討ドキュメント(pre-design)であり、確定仕様ではない。検証結果を踏まえて
採用構成が決まった時点で設計書に昇格させる。
- ゴール(合意済み): 実測 ST に対する予測精度の改善。回収率 A/B は本検討のスコープ外
(有望な構成が得られた後の次段判断)
- スコープ(合意済み): ST 推定値そのもの。スリット図(
StartPredictionDiagram)と
1 マーク走行距離計算(computeOneMarkDistances)の両方が同じ推定値を参照する
- 関連コード(fun-site リポジトリ):
packages/batch/src/site-builder/prediction-builder.ts の
buildStartPrediction() / packages/shared/src/utils/one-mark-distance.ts の
computeOneMarkDistances() / packages/web/src/components/StartPredictionDiagram.astro
- 正解データ: BoatraceCSV
data/results/realtime/(実測 ST)
1. 現状 (as-is)
1.1 予測 ST の算出
現状の「予測 ST」は programs/race_cards の 全国平均ST(公表値)をそのまま 使う。
| 使用箇所 |
式 / 挙動 |
スリット図 StartPredictionEntry.startTiming |
racer.nationalAvgST(stt 取得後も据え置き。展示 ST は注記表示のみで推定値には未反映) |
| 1 マーク走行距離 |
distance = (1 - 全国平均ST) + 強さpt / 50 - 1.6 |
| 平均 ST = 0.00(実績なし)の艇 |
描画側のみ 0.25 扱い(NO_RECORD_ST_FOR_DRAW)。距離計算は 0.00 のまま = 最速扱い |
1.2 現状の課題
- 鮮度: 公表平均 ST は期(約 6 ヶ月)集計のラグある値。直近の調子・当節の実測 ST・
F 休み明けの慎重化を反映しない
- コース非依存: 進入コースで ST 分布は系統的に異なる(カド勢は助走が付き早い、
イン起こしは遅い)のに、同じ選手ならどのコースでも同じ ST と予測している
- 展示 ST 未使用: 締切 5 分前に
previews/stt の実測展示 ST があるのに、表示注記
にしか使っていない
- F リスク回避を無視: F1/F2 持ちは節後半ほど慎重になる(ST が遅くなる)ことが
知られているが、
フライング累積本数 は予測に未使用
- 気象を無視: 向かい風はスリット全体が遅れ、追い風は早まる(F が増える)。
previews/sui は取得済みだが ST 予測には未使用
- 欠損処理が ad hoc: 平均 ST 0.00 の扱いが描画と距離計算で不整合(上表)
- 点推定のみ: ST のばらつき(選手ごとの安定性)を持たないため、スリット隊形の
確信度が表現できない
2. 正解データと評価方法
2.1 正解データ
data/results/realtime/YYYY/MM/DD.csv の
Nコース_艇番 / Nコース_スタートタイミング / Nコース_F(1=フライング)。
実際の進入コース順で記録されるため、コース条件付きの学習・評価がそのまま可能。
前処理ルール(Phase 1 で確定。詳細と根拠は
notebooks/st_estimation/report.md):
- F の表現(確定):
Nコース_F 列は実データでは文字 "F" / 空欄
(docs の旧記述 1/0 とは異なる → docs/data/results.md 修正済み)。F 艇の ST は負値。
正解には「F マークまたは ST < 0」を含めない(F マーク無しの負値も 7 件あり)
- L(出遅れ): 専用マークは無く、大きな ST(最大 0.99)として数値記録される。
実際に起きたスタートなので正解に含める
- 欠損: 実測 ST 欠損は
- の 5 件のみ。race_cards 結合不可 2.1%、stt 無し 8.0%
- 蓄積期間(確定): 2025-11-01〜で約 8.5 ヶ月・23.4 万艇走・1,639 選手
(選手あたり中央値 141 走)。起案時の見積り(2.5〜3 ヶ月)の約 3 倍あり、
選手×コース別の縮約推定にも現実味がある
2.2 評価指標
| 指標 |
単位 |
見るもの |
| MAE / RMSE(予測 ST − 実測 ST) |
艇 |
点推定の精度(主指標) |
| スリット順位の Spearman ρ / Kendall τ |
レース |
隊形の順序一致 |
| スリット先頭艇の一致率 |
レース |
まくり展開の起点予測 |
| 隊形 3 分類(横一線 / 内凹み / 外凹み)の一致率 |
レース |
UI 上の説明力 |
隊形分類の定義例: 内側 3 コースの平均 ST − 外側 3 コースの平均 ST が
+0.03 以上で内凹み、−0.03 以下で外凹み、それ以外は横一線(しきい値は要調整)。
2.3 ベースラインと検証プロトコル
- B0: 現行 = 全国平均 ST 据え置き(0.00 は 0.25 補完に統一して比較)
- B1: 展示 ST をそのまま予測値にする(realtime 時のみ。ナイーブ上限の確認)
- Phase 1 確定値(全期間 / daily): B0 は MAE 0.0548・Spearman 0.226・
先頭艇一致 23.4%・隊形3分類 41.7%(チャンス 40.2%)。B1 は MAE 0.1221・
Spearman 0.081 と B0 より大幅に弱い(展示 ST と実測の相関 0.047)。
当初の採用基準「realtime では B1 にも勝つこと」は実質無意味と判明したため、
採用判断は B0 比のみで行う。数値の詳細は
notebooks/st_estimation/report.md
- 時系列分割: 学習 = 直近 1 ヶ月より前 / 評価 = 直近 1 ヶ月。選手単位のリークに注意
(EWMA 系は評価レースより前の走のみ使う点を厳守)
- daily(朝バッチ相当: 展示・気象なし)と realtime(締切 5 分前相当)の
2 つの情報時点で別々に評価する。現行 UI と同じ制約を再現するため
3. 仮説カタログ
各仮説は独立に ablation できる形で列挙する。H1〜H4 が本命、H5 以降は追加検証。
H1: 実測 ST 履歴の時間減衰平均は公表平均 ST より精度が高い
- 内容: 選手ごとに
results/realtime の実測 ST を EWMA(半減期 30〜60 日で探索)
で集約した値を予測 ST の基礎にする。当節の実測 ST が自動的に強く効く
- 根拠: 公表平均 ST は期集計でラグが大きい。モーター能力指数 v2 で時間減衰 +
ベイズ収縮の枠組みは実績があり、同じ道具立てが流用できる
- 必要データ: results 履歴のみ(preview 非依存 → daily 時点で使える)
- 設計メモ: サンプル少ない選手は公表平均 ST(あれば)または級別平均へベイズ収縮
(k は 10 前後から探索)。F 走は平均に入れない
- リスク: 出走の少ない選手では推定が不安定。収縮先を公表平均 ST にすることで
「公表値を下回らない」構造にできる(蓄積は 8.5 ヶ月・選手中央値 141 走と
当初想定より厚く、リスクは限定的)
- Phase 1 の示唆: 選手内の期間安定性(前半平均 vs 後半平均の相関)は 0.772 と高く、
公表平均 ST と実測の相関(選手レベル 0.496)を大きく上回る。本仮説の本命度が上がった
- Phase 2 で検証済み → 採用: 半減期 30 日・収縮 k=10(グリッドはフラットで感度低)。
単独でテスト窓 MAE 0.0526→0.0511、先頭艇一致 24.1%→33.2%(+9.1pt)
H2: コース条件付き補正で精度が上がる
- 内容: 予測 ST = 選手ベース ST + コースオフセット。まず全体(場×コース or
コースのみ)のオフセットを推定し、データが許せば選手×コースへ拡張(縮約付き)
- 根拠: ダッシュ勢(4〜6 コース)は助走距離が長く ST が早い、イン起こしは遅い、
というのはドメインの定説。正解データがコース順で記録されるため直接推定できる
- Phase 1 の示唆: 実測のコース別平均は 1c 0.152 / 6c 0.183 と 定説と逆で、
1 コースが最速・6 コースが突出して遅い。補正の主効果は「6 コース +0.02」。
選手の自己選択(イン屋のスタート巧者が 1 コースに集まる)を含む生値なので、
選手効果と分離した上でコース固有効果を推定する必要がある(H1 とセットで検証)
- Phase 2 で検証済み → 採用: 選手効果(H1)分離後もコース固有効果が残る
(1c -0.009 / 6c +0.012)。H1 に積んで ρ 0.261→0.287。なお展示進入コースへの
差し替えは効果なし(枠番ベースで十分 = daily 1 本で運用できる)
- 必要データ: results 履歴。予測時のコースは stt 取得後は実測進入、
取得前は枠番(現行フォールバックと同じ)
- リスク: 前付けレースでは予測進入と実際の進入がずれてノイズになる。
進入変更発生率は低いので全体への影響は限定的と予想(検証で確認)
H3: 展示 ST のブレンドで realtime 時点の精度が上がる
- 内容: realtime 時点では
pred = w × 展示ST + (1 − w) × 事前推定(H1+H2) と
ブレンド。w は全体最適から始め、選手別の「展示→本番バイアス」(展示で合わせない /
本番で踏み込むタイプ)を縮約付きで学習する拡張まで
- 根拠: 展示 ST は当日のモーター起こし・選手の意思を含む唯一の直前実測。
ただし 1 観測でノイズが大きく、意図的に踏み込まない選手もいるため
「そのまま使う(B1)」より縮約ブレンドが勝つはず、が仮説の骨子
- 必要データ:
previews/stt(スタート展示。F=負値、L=空欄)
- 制約: realtime 時点のみ。daily 評価では効かない
- Phase 1 の示唆(警告): 展示 ST と実測 ST の相関はわずか 0.047(展示 F 除外でも
0.072)、展示は平均 0.086 秒早い(展示 F 率 21.7%)。そのままの値はほぼ無情報で、
ブレンド重みは小さくなる見込み。優先度を下げるか、「展示で踏み込んだか」を
攻め気配のシグナルとして使う方向に仮説を修正して検証する
- Phase 2 で検証済み → 棄却: 修正版(展示 F の有無をシグナルにする H3’)でも
残差 +0.0008 とゼロ同然で、テスト窓の全指標が改善しなかった
H4: F 持ち・F 直後の慎重化補正で系統誤差が減る
- 内容:
フライング累積本数(F1/F2)、F 消化前後、当節内 F の有無で
ST を + 側に補正する項を入れる
- 根拠: F 持ちが慎重になるのは定説。特に F2 は即座に長期斡旋停止リスクがあり
影響が大きいはず。公表平均 ST には F 後の慎重化が遅れてしか反映されない
- 必要データ: race_cards(
フライング累積本数)+ results 履歴(F 発生日)
- 設計メモ: H1 の EWMA が F 後の実測を取り込めばある程度自動反映されるため、
H1 導入後の残差に対して F 項が残っているかを確認する(仮説の独立検証)
- Phase 2 で検証済み → 採用: EWMA 後の残差にも F1 +0.010 / F2 +0.038 の系統差が
残存。H1+H2 に積んで全指標が改善。F 消化直後の時限項は未検証(Phase 3 以降の余地)
H5: 風向・風速の補正で精度が上がる(realtime のみ)
- 内容: 向かい風 1m/s あたり +α 秒、追い風 −α 秒の線形補正
(場の
facing_deg 正規化は sui_params の既存ロジックを流用)
- 根拠: 向かい風は艇速が乗らずスリット全体が遅れる、追い風は F が増えるという定説。
ただし ST は「スタートラインまでの時間」なので選手が調整して打ち消す可能性もある
- 必要データ:
previews/sui / results 側の気象列(学習ラベル用)
- 期待効果: レース内の相対順位には効きにくい(全艇に同方向に効くため)。
MAE には効くが隊形指標には中立、という結果を予想。優先度は低め
H6: 欠損(平均 ST=0.00)の補完を級別事前分布にする
- 内容: 実績なし選手の ST を 0.25 固定ではなく「級別(+可能なら養成期別)の
平均 + 大きめの分散」で補完し、描画と距離計算の扱いを統一する
- 根拠: 現状は距離計算側で 0.00 = 最速扱いという明確なバグ寄りの挙動がある
(§1.1)。精度以前に整合性の問題として先行修正してよい
- 必要データ: race_cards の級別。実装コスト極小
- 進捗 (2026-07-20): バグ部分を先行修正済み。fun-site 側に
NO_RECORD_ST_FALLBACK = 0.25 / effectiveAvgST() を追加し
(packages/shared/src/utils/one-mark-distance.ts)、距離計算と描画の両方で
0.00 → 0.25 補完に統一した。「級別事前分布への置き換え」は未実施のまま
本仮説の検証対象として残る
H7: ST を分布(平均 + ばらつき)で推定する
- 内容: 選手別に残差分散も推定し、スリット図に幅(信頼区間)を表示、
距離計算は期待値ベースを維持しつつ確信度を予想根拠に出す
- 位置づけ: 点推定精度というゴールからは拡張枠。H1〜H4 の残差から
ほぼ無料で得られるため、検証ノートブックでは分散も記録しておく
H8: 展示の行き足系指標(直線・まわり足・チルト)は ST を追加説明しない(棄却前提の確認)
- 内容:
original_exhibition の直線/まわり足、tkz のチルトを H1〜H3 の
残差に回帰し、追加の説明力があるか確認する
- 根拠: スリット「隊形」には行き足が効くが、ST(通過時刻)自体は選手の
起こしタイミング調整で吸収される可能性が高い。効かないことを確認して
切り捨てるための仮説(効けば儲けもの)
- 制約: 場により計測項目が異なり欠損が多い。realtime のみ
優先度まとめ
| 仮説 |
情報時点 |
期待効果 |
実装コスト |
優先度 |
| H1 実測 ST EWMA |
daily |
大 |
中(モーター指数の道具流用) |
★★★ |
| H2 コース補正 |
daily |
大 |
小 |
★★★ |
| H6 欠損の級別補完 |
daily |
小(整合性修正) |
極小 |
★★★(バグ部分は 2026-07-20 修正済み) |
| H3 展示 ST ブレンド |
realtime |
大 |
中 |
★★☆ |
| H4 F 補正 |
daily |
中 |
小 |
★★☆ |
| H5 気象補正 |
realtime |
小 |
小 |
★☆☆ |
| H7 分散推定 |
daily |
(拡張) |
小 |
★☆☆ |
| H8 行き足残差確認 |
realtime |
確認のみ |
小 |
★☆☆ |
4. 検証計画
Phase 1: パネル構築(正解データの固定)
results/realtime 全期間から「選手 × レース」の実測 ST パネルを作成
(raceCode, 選手, 枠番, 実進入コース, 実測 ST, F/L フラグ, 気象, 級別, 平均 ST 公表値,
展示 ST, 予想進入コース)
- F の符号・L の表記・欠損率を実データで確認し、§2.1 の前処理ルールを確定
- B0/B1 ベースラインの指標値を確定(この数字が以後の比較基準)
Phase 2: 仮説別 ablation
- H1 → H1+H2 → H1+H2+H4(daily 系)、H1+H2+H3 → +H5(realtime 系)の順に積み、
各段で §2.2 の指標を記録。ハイパーパラメータ(半減期・収縮 k・ブレンド w)は
学習期間内の grid search で決め、評価期間には触らない
- 成果物:
notebooks/st_estimation/report.md(motor_score_tuning のレポート構成に倣う)
Phase 3: 採用判断と設計書化
- 採用基準の目安: daily 時点で B0 比 MAE −10% 以上、または隊形指標
(Spearman / 先頭艇一致率)の一貫改善。realtime 時点では B1(展示そのまま)にも
勝つこと
- Phase 2 の判定 (2026-07-20): MAE -10%(テスト窓 ≤0.0473)は選手平均型の
オラクル床(0.0486)を下回る誤設定であり撤回。M3(H1+H2+H4)は MAE -4.0%
(ペア差 t≈22)+ 隊形指標の一貫改善(ρ +0.065、先頭艇 +10.0pt)で
採用基準クリア → 実装設計へ進む。詳細は
notebooks/st_estimation/phase2_report.md
- 採用構成が決まったら本書を設計書に改稿し、実装 PR と同時に
docs/data/ のスキーマ文書を更新する(CLAUDE.md のドキュメント更新ルール)
アーキテクチャ選択肢(Phase 3 で決定)
| 案 |
内容 |
利点 |
難点 |
| A: BoatraceCSV 側で派生 CSV |
data/estimate/racer_st/YYYY/MM/DD.csv 等を日次 + 直前バッチで生成し、fun-site は読むだけ |
results 履歴・バッチ基盤・時間減衰の実装が全て boatracecsv 側にある。他予想者からも再利用可能 |
ファイル種別追加(docs/data/ 更新、fetcher 追加) |
| B: fun-site バッチ内で計算 |
fun-site が results 履歴を遡って取得し batch 内で推定 |
リポジトリ 1 つで完結 |
fun-site は当日分 CSV しか読まない設計。履歴取得の作り込みが大きい |
案 A を有力とする(fun-site の fetcher / prediction-builder は
「racer_st CSV があれば startTiming をそれに差し替え、無ければ現行フォールバック」
という後方互換の形にできる)。
Phase 3 実装(2026-07-20、案 A で実装済み)
- boatracecsv 側
scripts/boatrace/racer_st.py: M3 の計算ロジック(定数凍結)+ EWMA 状態管理。
状態は data/estimate/racer_st/state.csv(登録番号 / 重み付き和 / 重み計 / 基準日)
に永続化し、日次で前日結果のみ増分取り込み(冪等・過去日再生成はリーク防止で拒否)
scripts/build_racer_st.py --date: 日次 CLI。出力は
data/estimate/racer_st/YYYY/MM/DD.csv(1 レース 1 行 × 6 枠の推定 ST)。
初回のみローカルで --rebuild(全履歴走査 ~30 秒)して state.csv をコミットする
infra/run-daily-sync.sh: sparse パス(results 当月+前月 / race_cards 前月 /
racer_st)+ build-racer-st ステップ + commit 対象を追加
gcs_publisher.py: csv_type=racer_st を日次ミラーに追加
- 検証: 実データ 2026-07-19/20 で Phase 2 ノートブックの M3 予測と一致
(凍結定数の丸めによる差 ≤ 0.0008)。増分更新 = 全再構築、再実行冪等を確認。
ユニットテスト
scripts/tests/unit/test_racer_st.py(12 件)
- fun-site 側(スキーマ・fetcher・ST 差し替え。実装は同日の fun-site 変更を参照)
- fun-site 側:
estimate/racer_st の fetcher / スキーマ (RacerStRow) を追加し、
RaceRacer.estimatedST に取り込み。推定 ST を使うのは 予想者 v5_slit のみ
(PredictorSpec.useEstimatedST)。computeOneMarkDistances は opt-in 引数で
ST ソースを切り替え、スリット図は通常版 (startPrediction) と推定 ST 版
(startPredictionEstimated) を併存させて予想者カード側で選択する。
既存予想者 (v1_basic / v4_motor) の買い目・表示は完全に従来どおり
- 予想者
v5_slit(スリット予想、slot=5、2026-07-21〜): control (v1_basic) と
同一の 5 成分(index / 強さpt は同値)で、1 マーク距離計算とスリット図の予測 ST
だけを AI 推定 ST に差し替えた実験スロット。ST 推定の改善単独の回収率効果を
control と A/B 比較する(§5 で懸念した「全予想者に同時に効く」問題はこの構成で
解消)。weights は成分同一のため v1_basic と同値になり、初月分はコピーで
ブートストラップ済み
- ロールアウト手順: (1) boatracecsv に state.csv 初期化 + v5_slit レジストリ +
weights ブートストラップをコミット(済み)→ (2) 翌朝の daily-sync から racer_st
CSV と v5_slit の index CSV が自動生成・ミラーされる → (3) fun-site は
racer_st CSV があれば v5_slit の予測 ST に使い、無ければ全国平均 ST に
フォールバック(デプロイ順序に依存しない)
5. 未決事項
F 行の ST 表記(負値か、フラグのみか)の実データ確認 → Phase 1 で確定
("F" マーク + 負値 ST。§2.1)
- 1 マーク距離計算の係数(
strengthPt / 50、定数 1.6)は ST スケールの変更に
敏感な可能性がある。ST 推定値の平均水準が現行(公表平均 ST)から大きくずれる場合、
距離式の再キャリブレーションが必要かを Phase 2 で確認
- ST 差し替えは 全予想者の買い目に同時に効く(距離計算は predictor 非依存)。
回収率への影響を予想者 A/B の枠組みで測りたくなった場合、
computeOneMarkDistances
に ST ソースを predictor 別に渡せるようにする改修が要る(本検討のスコープ外だが
ロールアウト時の論点として記録)
- スリット図の凡例文言(「スタートタイミングは選手の全国平均STです」)の差し替え
経緯
- 2026-07-20: 初版(仮説カタログ + 検証計画)。ゴール = 実測 ST への予測精度、
スコープ = ST 推定値そのもの、と合意して起案
- 2026-07-20: H6 のバグ部分(距離計算で平均 ST 0.00 = 最速扱い)を fun-site 側で
先行修正(0.25 補完に統一)。級別事前分布化は未実施
- 2026-07-20: Phase 1 完了。実測 ST パネル(23.4 万艇走)構築、前処理ルール確定、
ベースライン確定(B0: MAE 0.0548 / 隊形一致はチャンス並み。B1 は無情報に近く
採用基準を B0 比のみに変更)。H1/H2/H4 の効果量を確認、H3 は下方修正。
記録は
notebooks/st_estimation/report.md
- 2026-07-20: Phase 2 完了。H1(半減期 30 日 / k=10)+ H2 + H4 を採用、H3 は棄却、
H5 は保留。テスト窓で MAE 0.0526→0.0505・Spearman 0.231→0.296・先頭艇一致
24.1%→34.2%。展示進入への差し替えは不要と判明し daily 1 本で運用可。
次は Phase 3(案 A: boatracecsv 側で選手別推定 ST の派生 CSV を日次生成)の実装設計。
記録は
notebooks/st_estimation/phase2_report.md
- 2026-07-20: Phase 3 実装(§アーキテクチャ「Phase 3 実装」参照)。boatracecsv に
build_racer_st.py + 状態ファイル方式の日次パイプライン、fun-site に取り込みと
ST 差し替えを実装。実データで Phase 2 の M3 予測と一致検証済み。今後の課題:
回収率への影響監視、コース補正の場別化、F 直後の時限項、定数の定期再学習
- 2026-07-20: ロールアウト方針を変更。既存予想者の ST を直接差し替えるのではなく、
新予想者
v5_slit(スリット予想)として投入し、control (v1_basic) と
回収率 A/B で比較する構成に改めた(既存処理への影響ゼロ)。§5 の
「全予想者に同時に効く」論点はこれで解消。判断基準は他の実験スロットと同様
「control に対する有意な回収率差」