v10_kimarite本命予想(v1_basic 系)とは別の生成原理で「的中率は低いが配当が大きい買い目」を
出す予想者の設計。決まり手を多クラス予測し、決まり手条件付きの着順分布から出目を作る
方式(検討時の案C)を採用する。
data/results/payouts/(2025-11-01〜2026-08-11, 42,090 レース)、
data/results/realtime/(決まり手・進入コース)、data/previews/{stt,sui,tkz,od3}/、
data/programs/race_cards/、data/estimate/v1_basic/現在の状態: 設計レビューで挙がった Blocking 3 件は §11、 High / Medium 7 件は §12 で解消済み。 最終構成は **A案
v9_suji(スジ予想)を control、B案v10_kimarite(穴予想)を 実験スロットとする 2 スロット運用(§13)。 判定は回収率ではなく log-loss で行う(回収率では 8.2 ヶ月かかる / §13.3)。決まり手は買い目 1 点ごとの注釈 として両案で表示する(§14)。**【2026-08-22】A案
v9_sujiは退役。2 スロット運用は 10 日で終了し、 B案v10_kimariteの 1 スロットに戻した。理由は成績の劣化ではなくスロットの 重複 — 本番 1,511 レース(両案同一レース)で買い目の重なりは平均 2.70 点 / 5 点 ある一方、回収率は 70.7%(A)vs 68.8%(B)で §13.3 のとおり区別できない。 A案は確率モデルを持たず主判定の log-loss に載らないため、B案を残した。 体験指標では A案が上(平均配当 3,949 円 vs 3,035 円、万舟/1万円 0.133 vs 0.066) で、「穴らしさ」を優先するなら判断は逆になりうる。検定値はscripts/boatrace/predictors/registry.py冒頭コメント。 なお B案は A案の資産(build_suji_table.pyの決まり手注釈テーブルとbuild_suji_picks.pyのヘルパー)に依存しているので、A案のコードは残置。【2026-08-13】両案とも実装・投入済み(A案 2026-08-12 / B案 2026-08-13)。 本番のファイル構成・スキーマ・定数は
docs/data/estimate.mdを正とすること。 本書は設計と検証の記録で、§8 の実装計画は計画時のまま残してある (実際のパスとの差分は §8.1 に明記)。 ハイパラは本番構成で選び直しており、§4 以前に出てくる γ / β / w は古い値 (§4.3 の更新注記を参照)。
| # | 内容 | 状態 |
|---|---|---|
| B1 | 展開予想の 94.5% が「イン逃げ」。top-1 セル的中率 52.3% は「常に逃げと言う」ベースライン 52.4% を下回る。買い目は「1着≠1」のハード制約で作るため、予想者が自分の主予測と矛盾した買い目を出す | 解決(§11.1)。ただし当初案の abstain は実測で有害と判明し、別解に差し替え |
| B2 | control との比較がペアになっていない。95%CI [74, 97] は control を含み、差がないことすら示せていない | 解決(§11.2)。同一レース 3,527 でのペア比較で -1.3pt / 95%CI [-13.1, +11.0] / p=0.817 |
| B3 | 買い目ルール(1着≠1 / top5)を test の一覧表を見て選んでいる |
解決(§11.3)。valid だけで選び直しても同じルールが選ばれ、test も 84.9% で不変 |
結論として、3 件とも解消できた。 ただし解決の過程で新しい事実が 2 つ出た:
回収率 100% 超えは目標にしないこと。 市場(オッズ)の log-loss は 3.7965 で、 ブレンド後の 3.9364 でもまだ 0.14 nat 負けている。控除率 25% を破るには
log(1/0.75) = 0.288 nat分の優位が要る。
「毎レース人気 k 位の 3連単を 1 点買う」戦略の回収率(42,090 レース):
| 人気帯 | 点数 | 的中率 | 回収率 |
|---|---|---|---|
| 1〜3 位 | 3 | 26.4% | 76.8% |
| 4〜10 位 | 7 | 28.6% | 77.9% |
| 11〜20 位 | 10 | 17.7% | 73.7% |
| 21〜40 位 | 20 | 15.1% | 69.2% |
| 41〜80 位 | 40 | 10.0% | 60.8% |
| 81〜120 位 | 40 | 2.3% | 43.6% |
控除率 25% は全帯共通なので、この落ち方は投票者が人気薄を買いすぎているぶんの損。 穴に寄ること自体が回収率にマイナスで、大穴(100 倍超)は選別しても勝てない (§5.4)。狙うのは中穴(2,000〜30,000 円)に絞る。
| 3連単 配当 | レース割合 | 払戻総額に占める割合 |
|---|---|---|
| 〜1,000 円 | 19.2% | 1.9% |
| 1,000〜3,000 円 | 36.9% | 9.3% |
| 3,000〜10,000 円 | 27.5% | 21.1% |
| 10,000〜30,000 円 | 11.7% | 27.3% |
| 30,000〜100,000 円 | 4.0% | 27.0% |
| 100,000 円〜 | 0.6% | 13.3% |
1 万円以上はレースの 16.3% にすぎないが、払戻総額の 67.6% を占める。
v1_basic の 1枠_強さpt − 他5艇の最大強さpt で 5 分位に分けた実測(14,333 レース):
| 分位 | 1号艇1着率 | 万舟率 | 1着が4〜6号艇 |
|---|---|---|---|
| 1(拮抗) | 35.0% | 17.9% | 24.7% |
| 3 | 56.5% | 17.3% | 17.9% |
| 5(1号艇圧倒) | 73.2% | 14.3% | 13.1% |
1 号艇の勝敗はよく分ける(35% ⇔ 73%)のに、万舟率は 17.9% ⇔ 14.3% しか動かない。 「誰が強いか」と「どう決まるか」は別軸で、現行 5 成分は後者を持っていない。 この穴を埋めることが、買い目を出すより先に価値がある(§8)。
| 決まり手 | 割合 | 平均配当 | 中央値 | 万舟率 | 平均人気 |
|---|---|---|---|---|---|
| 逃げ | 52.5% | 2,565 | 1,430 | 3.6% | 8.1 |
| 差し | 11.9% | 10,569 | 4,790 | 26.9% | 25.0 |
| まくり | 15.6% | 12,374 | 6,170 | 33.4% | 28.9 |
| まくり差し | 11.8% | 14,728 | 6,500 | 36.7% | 31.2 |
| 抜き | 6.2% | 10,689 | 3,730 | 24.3% | 22.9 |
| 恵まれ | 0.7% | 10,657 | 2,470 | 20.4% | 18.7 |
逃げか否かで配当が 4〜6 倍違う。
⚠ 定義のずれ(実装時に作り直す): この節の集計は
results/realtimeの 実進入コース({c}コース_艇番)で作っている。一方 §4 のモデルは 賭け時点で使えるpreviews/sttの展示進入コースで並べ替える。 1着コースの一致率は 96.8%(全 6 艇の進入まで見ると 87.9%)なので大枠は変わらないが、 この表の数値はモデル内部のテーブルとは厳密には一致しない。 §12.2 の結論どおり stt 基準に統一するので、実装時に作り直すこと。
| 決まり手 | 1c | 2c | 3c | 4c | 5c | 6c | 計 |
|---|---|---|---|---|---|---|---|
| 逃げ | 53.32 | — | — | — | — | — | 53.32 |
| 差し | — | 8.07 | 1.46 | 1.88 | 0.38 | 0.20 | 11.99 |
| まくり | — | 3.78 | 5.09 | 4.98 | 1.30 | 0.61 | 15.77 |
| まくり差し | — | — | 4.78 | 2.64 | 3.69 | 0.82 | 11.93 |
| 抜き | 2.29 | 1.15 | 1.26 | 0.71 | 0.60 | 0.24 | 6.23 |
| 恵まれ | 0.11 | 0.16 | 0.15 | 0.18 | 0.12 | 0.04 | 0.77 |
「逃げ = 1コース」「まくり差しは 2コースからは出ない」といった制約が自然に入る。
n < 60 のセルは その他_{コース} に畳んで 25 クラスにする。
| セル | n | 2着コース分布 | 上位2-3着ペア |
|---|---|---|---|
| 逃げ 1着1c | 22,398 | 2c:34.3 3c:29.2 4c:18.9 5c:12.2 6c:5.4 | 2-3(13%) 3-2(10%) 2-4(10%) |
| 差し 1着2c | 3,389 | 1c:59.8 3c:16.3 4c:12.0 5c:7.9 6c:4.0 | 1-3(22%) 1-4(17%) 1-5(13%) |
| まくり 1着3c | 2,140 | 1c:22.6 2c:20.5 4c:27.1 5c:20.9 6c:8.9 | 4-5(8%) 4-1(7%) 4-2(7%) |
| まくり 1着4c | 2,094 | 1c:22.4 2c:19.4 3c:10.6 5c:33.0 6c:14.6 | 5-1(11%) 5-6(8%) 5-2(8%) |
| まくり差し 1着3c | 2,006 | 1c:63.4 2c:16.1 4c:9.9 5c:7.4 6c:3.2 | 1-4(21%) 1-5(18%) 1-2(15%) |
| まくり 1着2c | 1,588 | 1c:10.8 3c:36.6 4c:27.0 5c:17.7 6c:7.9 | 3-4(12%) 3-5(11%) 4-3(9%) |
| まくり差し 1着5c | 1,550 | 1c:41.4 2c:14.6 3c:14.3 4c:22.6 6c:7.0 | 1-2(12%) 1-4(10%) 1-3(10%) |
| まくり差し 1着4c | 1,110 | 1c:38.1 2c:15.3 3c:24.7 5c:14.8 6c:7.1 | 1-5(14%) 1-2(10%) 3-5(9%) |
セルが当たっている前提での 2-3 着カバー率(条件付き的中率):
| セル | n | top3 | top5 | top8 | top12 |
|---|---|---|---|---|---|
| 逃げ 1着1c | 22,398 | 33.4% | 49.0% | 67.2% | 83.5% |
| 差し 1着2c | 3,389 | 52.3% | 68.0% | 81.3% | 90.1% |
| まくり差し 1着3c | 2,006 | 54.5% | 68.9% | 81.9% | 91.5% |
| まくり 1着4c | 2,094 | 27.9% | 42.2% | 59.0% | 76.6% |
| まくり 1着3c | 2,140 | 21.7% | 34.6% | 52.9% | 75.0% |
差し・まくり差しは 2-3 着が絞れる(top5 で 68%)が、まくりは散る(top5 で 35〜42%)。
┌ Stage1: P(決まり手, 1着コース) … 25 クラス多項ロジスティック回帰
│ ↓
出目 ← ┤ Stage2: P(2着コース, 3着コース | セル) … 収縮済み経験表 × 強さpt 変調
│ ↓
└ 合成: P(120通り) = Σ_セル P(セル) × P(2着,3着|セル) → 艇番に写像
↓
ブレンド: w × 決まり手モデル + (1-w) × Plackett-Luce(強さpt)
決まり手 + "_" + 1着コース。n < 60 のセルは その他_{コース} に畳む → 25 クラスC = 0.006 を valid で選択)race_cards: 級別・F本数・全国勝率・当地勝率・モーター2連対率・全国平均ST(各 6 コース分)previews/stt: スタート展示 ST(6)、進入が枠なりか、前付け艇数previews/tkz: 展示タイム・チルト・体重(各 6)previews/sui: 風速・風向・波高・天候・気温・水温estimate/v1_basic の 強さpt / 各成分pt(6 コース分)なぜ GBM ではないか: 同じ特徴量・同じ分割で比較すると、
| Stage1 モデル | valid log-loss | test log-loss | ベースレート比 改善 |
|---|---|---|---|
| ベースレート(定数) | 1.9983 | 1.9874 | — |
| HistGradientBoosting | 1.9665 | 1.9456 | 0.042 nat |
| 多項ロジスティック回帰 (C=0.006) | 1.8465 | 1.8306 | 0.157 nat |
学習データが 7,000 レース × 25 クラスしかないため GBM は過学習する。
さらに 回帰係数を CSV に落とせるので、sui_params.csv / course_win_rate.csv と
同じ「学習済みテーブルを月次で再生成してコミットする」運用に素直に乗る。
エンコーディングは試したが効かなかった。場コード(1〜24)・風向・天候を 数値のまま線形モデルに入れているのは本来おかしい(順序を仮定してしまう)。 one-hot 化 + 風向の sin/cos + 1コース比の差分特徴を足して測り直した結果:
| 構成 | valid | test |
|---|---|---|
| 現行(数値のまま) | 1.8465 | 1.8306 |
| one-hot 場/天候 + 風向 sin/cos | 1.8513 | 1.8334 |
| + 1コース比の差分特徴 | 1.8465 | 1.8284 |
有意に動かない。 C = 0.006 の強い正則化で場次元がほとんど使われていないためで、
「エンコーディングを直す」より「学習データを増やす」ほうが効く、という診断になる。
学習窓を伸ばせる(Stage1 のみ): index 由来の特徴量(強さpt / 各成分pt、36 列)を
落としても test log-loss は 1.8306 → 1.8489 としか悪化しない。index 系を外せば
estimate/v1_basic が始まる 2026-05 ではなく、results/realtime が始まる
2025-11 から学習できる(7,000 → 約 30,000 レース)。上のエンコーディング検証から、
この 4 倍の学習データが最優先の改善策。実装では index 系を外した構成を第一候補とする。
ただし Stage2 の γ 変調と最終ブレンドの Plackett-Luce は 強さpt を使うため、 そちらは 2026-05 以降でしか学習・調整できない。 Stage1 = 長期履歴 / Stage2・ブレンド = 短期履歴という非対称なパイプラインになる (§10 H3)。
セルごとの 6×6 経験カウント表 pair[cell][c2][c3] を、周辺分布の積へ収縮:
tab[cell] = (pair[cell] + k · m2[cell] ⊗ m3[cell]) / 正規化 … k = 1000 (valid で選択)
これに 強さpt による変調を掛けてレースごとに個別化する:
P(c2, c3 | cell) ∝ tab[cell][c2][c3] · exp(γ·z_{c2}) · exp(γ·z_{c3}/2) … γ = 1.0 (valid で選択)
z_c = (コース c に入る艇の 強さpt − 50) / 10
1着コースと重複する枠、および c2 == c3 は 0 にして正規化する。
γ が「同じ決まり手なら強い艇が上に来やすい」を表し、tab が「決まり手の型」を表す。
P(c1,c2,c3) = Σ_cell:1着=c1 P1[cell] · P(c2,c3 | cell)
最終 = w · P(決まり手モデル) + (1−w) · P(Plackett-Luce, β)
【2026-08-13 更新】ハイパラは本番構成で選び直した。 本節に元々あった k=1000 / γ=1.0 / β=1.4 / w=0.8 は 28 クラス・実験窓 (train 6,956 レース)の値で、32 クラス・全履歴の本番構成では選ばれない。 §12.1 で見つけた 28→32 クラスの食い違いと同じ構造の問題なので、 同じ手続き(valid の 3連単 log-loss)で取り直した。
実験時(28 クラス) 本番(32 クラス・全履歴) k(Stage2 の収縮) 1000 150 γ(強さpt 変調) 1.0 0.5 β(PL の強さ倍率) 1.4 2.4 w(決まり手モデルの重み) 0.8 0.7 valid (2026-06-25〜07-17, 3,696 レース) の感度は平坦で、γ は 0.25〜0.75 / β は 1.8〜2.8 / w は 0.6〜0.8 の範囲なら差 0.005 nat 以内。 k は Stage2 側の条件付き log-loss で選んだ 150 を据え置く (120 通りの log-loss は k に対してほぼ平坦 —— 50〜2000 で 0.0034 nat しか 動かない —— のに対し、条件付き log-loss は Stage2 の質そのものを直接測る)。
β には注意: PL 単体の最良は実験時と同じ 1.4 (valid 4.0258) だが、 ブレンド成分としての最良は 2.4。混合の相手として使うときは PL を 鋭くしたほうが効く。
kimarite_blend.pyではBETA(2.4)とPL_BASELINE_BETA(1.4、A/B の対照用)を別定数にしてある。実装は
scripts/boatrace/kimarite_blend.py、再選定はnotebooks/ana_prediction/kimarite_backtest.py。 選定記録はnotebooks/ana_prediction/report.md。
両者は補完的で、混ぜると単独より良くなる(§5.1)。 最後にコース → 艇番へ写像して出目にする。
強さpt への依存: Stage2 の γ 変調とこのブレンドで 強さpt を使うので、
v10_kimariteは index を必要とする(§12.3)。【実装時に解消】 当初は「
data/estimate/v1_basicを直接読む = 予想者間の 依存」を想定していたが、実装ではv10_kimariteが自分の index (data/estimate/v10_kimarite/)を持つ形にした。成分・重みが control と同一 なので値は v1_basic と一致するが、参照は自分のファイルに閉じているため 「予想者どうしは独立」という既存の前提を崩していない。A案v9_sujiも同じ形。残る依存は運用上の 1 点だけ: 初月の weights を v1_basic からコピーして ブートストラップする(翌月以降は monthly-weights が自前で生成する)。
学習 2026-05-01〜06-24(6,956)/ 検証 06-25〜07-17(3,689、ハイパラ選択のみに使用)/ テスト 2026-07-18〜08-11(3,527、報告用)。
| 予測 | test log-loss(3連単 120 通り) |
|---|---|
| 一様(1/120) | 4.7875 |
| Plackett-Luce(強さpt, β=1.4) | 4.0325 |
| 決まり手モデル単体 | 3.9522 |
| ブレンド(決まり手 0.8 + PL 0.2) | 3.9364 |
| (参考)市場 = od3 の正規化逆オッズ ※別サンプル 3,352 レース | 3.7965 |
PL 比 0.096 nat の改善。市場との差は 0.236 → 0.140 nat に縮まったが、まだ負けている。
この節の数字は当初 B2 / B3 の疑いがあったが、§11 で検証し直して据え置きとなった。 買い目ルールは valid だけで選び直しても 同じ
1着≠1 / top5が選ばれ(§11.3)、 control とは同一レースでペア比較して -1.3pt / p=0.817(有意差なし)を確認した (§11.2)。 以下の control 列は別期間の参考値なので、判定には §11.2 の表を使うこと。
「最終確率の上位 k 点を 1 点 100 円で買う」戦略:
| 戦略 | 点数 | 的中率 | 回収率 | 95%CI | 平均配当 | 万舟的中 |
|---|---|---|---|---|---|---|
| 確率top3(制限なし) | 3.0 | 20.98% | 79.3% | [73, 86] | 1,134 | 0 |
| 確率top12(制限なし) | 12.0 | 51.40% | 78.9% | [74, 84] | 1,842 | 19 |
| 穴: 1着コース≠1 の top5 | 5.0 | 12.39% | 84.9% | [74, 97] | 3,427 | 21 |
| 穴: 1着コース≠1 の top3 | 3.0 | 8.70% | 82.5% | [71, 95] | 2,842 | 7 |
| 穴: 1着コース≠1 の top12 | 12.0 | 20.78% | 81.6% | [74, 90] | 4,709 | 78 |
| 逃げ確率 下位35% のレース × 1着≠1 top8 | 8.0 | 23.32% | 93.4% | [80, 108] | 3,206 | 10 |
参考値としての control(v1_basic ≒ 86%・11.7 点・的中率 46%・平均配当 ≒ 2,200 円)
との対比。同一レースでの比較ではないので、方向感の確認までにとどめること:
control v1_basic(別期間) |
v10_kimarite(1着≠1 top5) |
|
|---|---|---|
| 回収率 | ≒ 86% | 84.9%(95%CI [74, 97]) |
| 的中率 | ≒ 46% | 12.4% |
| 点数/レース | 11.7 | 5.0 |
| 購入額/レース | 1,170 円 | 500 円 |
| 平均配当 | ≒ 2,200 円 | 3,427 円 |
95%CI の幅が 23pt ある。この期間の長さでは control との差はどちら向きにも示せない。
Stage1 を GBM にした版でも同じ評価をした。安定するものと、しないものがある:
| 戦略 | GBM 版 | ロジスティック版 | 判定 |
|---|---|---|---|
| 1着≠1 top5 | 85.2% | 84.9% | 安定 |
| 1着≠1 top12 | 83.0% | 81.6% | 安定 |
| 逃げ確率 下位35% × top8 | 98.4% | 93.4% | 方向は同じだが振れる |
| 逃げ確率 下位20% × top8 | 107.9% | 92.1% | 振れる。信用しない |
| 「最有力セルが逃げでない」レース(5.5%)× top3 | 122.4% | 78.8% | ノイズ。n≈200 |
100% を超えて見えたセルはいずれもサンプルが小さく、モデルを替えると消えた。 「安定」に分類したものも B3(test を見て選んだ)の影響を受けているので、 採用前に valid で選定ルールを固定してから test を引き直すこと。
od3(集計中オッズ)が取れる 3,352 レースで、オッズ帯フィルタを重ねた結果:
| 戦略 | 回収率 | 平均配当 |
|---|---|---|
| 1着≠1 top5(オッズ不使用) | 85.5% | 3,462 |
| 1着≠1 top5 × 30〜100 倍 | 76.3% | 4,762 |
| 1着≠1 top5 × 20〜200 倍 | 82.1% | 4,299 |
オッズ帯で絞ると、モデルが上位に置いた出目を捨てて下位を買うことになるため悪化する。
v10_kimarite はオッズを使わない。オッズを主役にする案(検討時の案A: EV = 確率 × オッズ
の上位を買う)は 30〜100 倍帯で 87.4% を出したが、生成原理が「市場との乖離」で
人間に説明しづらいため今回は採らない。将来 od3 の履歴が貯まったら別スロットで再検討する。
この節が B1 の根拠。 test 3,527 レースでの Stage1 の挙動:
| 指標 | 実測 |
|---|---|
| top-1 セル的中率 | 52.3% |
| 「常に 逃げ_1 と言う」ベースライン | 52.4% |
| top-2 セルに正解が入る率 | 62.3% |
| 決まり手のみ(コース無視)の top-1 的中率 | 52.9% |
モデルの argmax は「常にイン逃げ」と言うのに勝てていない。 top-1 に選ばれるセルの分布:
| top-1 セル | レース数 | 割合 |
|---|---|---|
| 逃げ_1 | 3,332 | 94.5% |
| 差し_2 | 71 | 2.0% |
| まくり_2 | 66 | 1.9% |
| まくり_3 | 42 | 1.2% |
| まくり_4 | 9 | 0.3% |
| まくり差し_5 | 6 | 0.2% |
| まくり差し_3 | 1 | 0.0% |
一方で P(逃げ_1) の校正は良い:
| P(逃げ) 予測帯 | n | 予測平均 | 実測 | 差 |
|---|---|---|---|---|
| 0.0–0.2 | 167 | 15.6% | 15.0% | -0.6pt |
| 0.2–0.3 | 320 | 25.7% | 27.5% | +1.8pt |
| 0.3–0.4 | 423 | 35.1% | 31.4% | -3.7pt |
| 0.4–0.5 | 526 | 45.1% | 38.4% | -6.6pt |
| 0.5–0.6 | 609 | 55.0% | 57.0% | +2.0pt |
| 0.6–0.7 | 671 | 65.0% | 67.4% | +2.4pt |
| 0.7–0.8 | 596 | 74.8% | 71.8% | -3.0pt |
| 0.8–1.0 | 215 | 83.4% | 80.9% | -2.4pt |
使えるのは argmax ではなく確率値。この一点が §6 と §8 の方針を決めている。
当初は「セル確率の上位 3〜4 件を『{コース}コースが{決まり手}』の文で表示する」設計だった。 実データからの出力例:
=== 202608112310 (2026-08-11) ===
展開予想:
73.7% 3コースがまくる (= 3号艇)
6.4% 3コースがまくり差す (= 3号艇)
5.8% 1コースがそのまま逃げる (= 1号艇)
穴の買い目: 3-1-5 (6.2%) 3-4-5 (5.8%) 3-5-2 (5.6%) 3-5-1 (5.3%) 3-5-4 (5.1%)
結果: 3-4-5 ★的中
⚠ この例は代表的な出力ではない。 上の 2 例は P(逃げ) が最小の 5 レース(3,527 中の下位 0.1%)から選んだもの (
narrative.pyがargsort(P1[:, 逃げ])の先頭を出す)。 §5.5 のとおり、 典型的な出力は 94.5% の確率でこうなる:
展開予想:
68.0% 1コースがそのまま逃げる (= 1号艇) ← 主予測
...
穴の買い目 (1着が1コース以外):
3-1-5 / 3-4-5 / 3-5-2 / 3-5-1 / 3-5-4 ← 主予測と矛盾
「1着コース≠1」が確率モデルの外側にあるハード制約なので、 予想者が自分の主予測を否定する買い目を出す状態になる。 案C の目的(人間が理解しやすい)を正面から損なうため、この形では出せない。
検証の詳細は §11.1。結論として 「最有力の決まり手を宣言する」のをやめ、当たる要素だけで語る。
| 要素 | 出す / 出さない | 根拠 |
|---|---|---|
荒れ度 = 1 − P(逃げ_1) |
主表示 | 校正誤差が概ね ±3pt(§5.5) |
| 「荒れるなら何コース頭か」 | 出す | 45.1% 対ベースライン 30.1%(+15pt) |
| 「荒れるならどの決まり手か」(レース単位) | 出さない | 33.7% 対ベースライン 33.7%(情報量ゼロ)。1着コースを与えた条件下でもベースレート -1.1pt(§14.2) |
| 買い目 1 点ごとの「この出目はこういう決まり手」 | 出す | 63.2% 対ベースライン 48.7%(§14.1) |
| セル(決まり手 × 1着コース)の argmax | 出さない | 52.3% 対ベースライン 52.4%(ベースライン以下) |
| abstain(荒れそうなレースだけ買う) | 入れない | 回収率が下がる。valid で 79.6% → 65.6% |
矛盾は「隠す」のではなく「立場を明示する」ことで解く。 穴予想は定義上、本命が来ない方に賭けるものなので、次のように書けば 「イン逃げ 68%」と「1着が 1 コース以外の買い目」は両立する:
このレースは イン逃げが 68% 濃厚です。
穴予想は、残り 32% ——「1 コースが飛ぶ側」に賭けます。
荒れ度 32% 荒れるなら 3コース頭 (荒れた場合の 41%)
買い目: 3-1-5 / 3-4-5 / 3-5-2 / 3-1-2 / 3-5-1
決まり手は レース単位の予測としては出さないが、買い目 1 点ごとの注釈としては出す。 「このレースはまくりになる」とは言えないが、「この出目 3-1-4 はまくり差しの形」とは 言える(§14)。前者は当てられておらず、後者は当たるため。
補足: 買い目 5 点の 1着コースが 1 種類に収まるのは 12.4% のレースだけで、 2 種類が 62.4%、3 種類以上が 25.2%。「3コース頭の 5 点」と言い切れることは少ないので、 「荒れるなら 3コース頭(41%)、次点 4コース頭(22%)」のように確率つきで 2 つまで出す。
進入コース順で語るので、前付けがあったレースでは「4コース(= 2号艇)がまくる」のように コースと艇番がずれる。UI では必ず両方出すこと。
回収率だけを control と比べる現行の検定では、この予想者の価値が測れない。 平均配当・平均点数・万舟的中数(賭け金あたり)を集計に足す必要がある。 abstain は採用しないため(§11.1)、 発火率は KPI に入れない(全レースで買い目を出す)。
| KPI | 目標 | test 実測(§11) |
|---|---|---|
| 回収率 | control と同等(有意に下回らない) | 84.9% vs 86.2%、差 -1.3pt、p=0.817 → 合格 |
| 平均配当 | control の 1.5 倍以上 | 3,427 円 vs 2,166 円 = 1.58 倍 → 合格 |
| 購入点数 | control の 半分以下 | 5.0 点 vs 11.5 点 = 43% → 合格 |
| 万舟的中数 / 1 万円賭け | control より多い | 0.12 vs 0.10 = 1.2 倍 → 合格 |
| 買い目パターンの異なり数 | control と同水準(下記) | A案は 1 日 20 種類(上位 4 つで 83%)→ 要観察 |
| P(逃げ) の校正誤差 | 各予測帯で実測との差が ±5pt 以内 | 8 帯中 7 帯で ±3pt 以内、最大 -6.6pt → ほぼ合格 |
買い目パターンの異なり数について(実装時に判明): A案は 2-3 着がスジ表由来の 定数で、per-race の情報は「1着に誰を選ぶか」だけ。実データ(2026-08-10、 156 レース)では 20 種類しか出ず、上位 4 パターンが 83% を占めた (残りは前付けで艇番が入れ替わったもの)。設計どおりの挙動だが、 「毎回同じ買い目が出る」とユーザーには見える。
B案(
v10_kimarite)はレースごとに確率分布が変わるのでこの性質は無い。 A案 vs B案 の体験上の差として、log-loss や回収率とは別に観察する。 集計は fun-sitepredictor-stats.tsのaveragePayoutYen/averageBetCount/bigHitPer10kYenで見る。
万舟 KPI は「本数」ではなく「1 万円賭けあたりの本数」で見ること。 素の本数では 穴予想 21 本に対し control 40 本で負けているが、これは control が 2.3 倍の金額を賭けているため。賭け金あたりに直すと逆転する。 点数を増やすほどこの指標は伸びる(穴 top5 0.12 / top8 0.14 / top12 0.18)一方、 回収率は k に対してほぼ横ばい(valid で 73〜85% を上下、test で 80〜85%)なので、 k は回収率ではなく「体験」の KPI で決めるべき。ただし top12 は点数 KPI を落とす。
v1_basic との比較は既存の退役判定と同じ方式に揃えること(直前買い目が組めた
確定レースのみ、control と同一レースで突き合わせたペア bootstrap 20,000 反復 +
ペア並べ替え検定)。ただし A案 と B案 の比較は回収率では決着しないため、
主判定を log-loss に置き換える。§13.3 を参照。
この方式での実測は §11.2 に載せた。
判定は 3 ヶ月以上のデータが貯まってから行う。テスト期間 3,527 レースでも ペア差の 95%CI が [-13.1, +11.0] と幅 24pt あり、それより短い期間では何も言えない。
レビューによる順序変更: 当初は Step 1〜6 を通しで進める計画だったが、
BettingPicksの型変更(fun-site 側で最も重い改修)を、まだ差が示せていない 予想者のために先に払う構造になっていた。B1 の再設計も未了。 そこで Phase 1 として「荒れ度メーター」だけを先に出す順序に変更する。
Stage1 の 1 − P(逃げ_1) だけを既存のレース詳細に表示する。
BettingPicks の型変更が不要(買い目を出さない)【2026-08-13】Phase 1 / 1’ / 2 とも実装完了。 以下の表は計画時のもので、 実際に作られたファイル名・パスは一部違う。現物は
docs/data/estimate.mdを正とすること (この節は「なぜその順で作ったか」の記録として残す)。
| Step | 内容 | 実際の成果物 |
|---|---|---|
| 1-1 | Stage1 の学習スクリプトと係数テーブル | scripts/build_kimarite.py / data/estimate/kimarite/tables/cell_coef_{daily,realtime}.csv |
| 1-2 | 直前バッチでの推論と CSV 出力(セル確率のみ) | scripts/build_kimarite_probs.py(推論)+ scripts/boatrace/kimarite.py(特徴量を学習と共有)。preview-realtime.py から呼ぶ |
| 1-3 | UI 表示 | fun-site UpsetMeter.astro |
| 1-4 | 校正モニタリング(予測帯別の実測ずれ) | scripts/build_kimarite_calibration.py → data/estimate/kimarite/tables/calibration.csv(fun-site ではなく boatracecsv 側で月次集計) |
v10_kimaritePhase 1 の校正が確認でき、B1〜B3 が解消してから着手する。
| Step | 内容 | 対象 |
|---|---|---|
| 2-1 | 1着≠1 / top5 / abstain なし に固定 |
notebooks/ana_prediction/ |
| 2-2 | 同上 | |
| 2-3 | H2 の対応: §3 の条件表と Stage2 のペア表を stt 基準で作り直す(§12.2) | 同上 |
| 2-4 | averagePayoutYen / averageBetCount / bigHitCount / bigHitPer10kYen) |
fun-site predictor-stats.ts |
| 2-5 | Stage2 テーブルの生成と出目出力 | 完了。scripts/build_kimarite_pairs.py → data/estimate/kimarite/tables/pair_table.csv、scripts/build_kimarite_picks.py → data/estimate/kimarite/picks/ |
| 2-6 | レジストリ登録 | 完了。predictors/registry.py(slot=10)、fun-site predictors.ts、infra/run-*.sh |
| 2-7 | 出目リスト型への対応 | 完了(Phase 1 で先行実装)。fun-site one-mark-distance.ts / bet-hit.ts / bet-payout.ts / BettingPicks.astro |
| 2-8 | ハイパラの再選定 | 完了。notebooks/ana_prediction/kimarite_backtest.py。γ=0.5 / β=2.4 / w=0.7(§4.3) |
| 2-9 | log-loss の A/B 集計(主判定) | 完了。scripts/build_kimarite_logloss.py → data/estimate/kimarite/tables/logloss.csv |
| 2-10 | 3 ヶ月 A/B | 運用中(2026-08-13〜) |
以下は実装後の実際のパス(計画時から 2 点変わっている。理由は表の下)。
| パス | 内容 | 更新頻度 | Phase |
|---|---|---|---|
data/estimate/kimarite/tables/cell_coef_daily.csv |
Stage1(daily 版・race_cards のみ)の多項ロジスティック係数(凍結した 32 クラス × 特徴量)+ 補完 median と標準化の中心 / 尺度 |
月次 | 1 |
data/estimate/kimarite/tables/cell_coef_realtime.csv |
Stage1(realtime 版・preview 込み)の同上 | 月次 | 1 |
data/estimate/kimarite/YYYY/MM/DD.csv |
レース 1 行。状態 + 荒れ度 + セル確率 32 列 |
日次 + 直前 | 1 |
data/estimate/kimarite/tables/calibration.csv |
荒れ度の校正監視(予測帯別の実測ずれ) | 月次 | 1’ |
data/estimate/kimarite/tables/pair_table.csv |
セル × 2着コース × 3着コース の収縮済み確率(32 × 20 = 640 行) | 月次 | 2 |
data/estimate/kimarite/picks/YYYY/MM/DD.csv |
レース 1 行。買い目 5 点 + 決まり手注釈 | 日次 + 直前 | 2 |
data/estimate/kimarite/tables/logloss.csv |
主判定。PL 対 ブレンドの 3連単 log-loss(月次 + 累計) | 月次 | 2 |
data/estimate/v10_kimarite/YYYY/MM/DD.csv |
既存形式の index(N枠_強さpt ほか)。買い目はここではなく picks/ |
日次 + 直前 | 2 |
計画時からの変更点:
tables/ サブディレクトリに置いた。 日次 CSV と同階層に
置くと Cloud Run の cone-mode sparse-checkout が日次ファイルの全履歴まで
checkout してしまうため。suji 側も同じ構造にしてある。data/estimate/v10_kimarite/ ではなく data/estimate/kimarite/picks/。
data/estimate/{predictor_id}/ は index(強さpt)の置き場という既存の規約が
あり、そこに別形式のファイルを混ぜない。A案 v9_suji(index は
estimate/v9_suji/、買い目は estimate/suji/)と同じ分け方。
abstain フラグは実測で有害と判明したため列自体が無い(§11.1)。月次再生成は既存の monthly-weights ジョブに相乗りする(build_course_rate.py と同じ扱い)。
BettingPicks = {first[], second[], third[]}
(フォーメーション)で、isFormationHit / countFormationCombinations /
BettingPicks.astro がすべてこの型に依存している。決まり手モデルの出目は
3-1-5, 3-4-5, 3-5-2 のようにどんな窓でも作れない集合なので、
BettingPicks を「フォーメーション | 出目リスト」の直和型に広げるか、
別型を足して bet-payout / bet-hit を分岐させる必要がある。
バッチ(prediction-builder.ts)と web(BettingPicks.astro)が必ず同じヘルパー経由で
解決する現行の規約は維持すること(片側だけ変えると表示と集計が食い違う)。isSettledResult かつ買い目が組めたレース)は
既存予想者と同じでよい。「意図的に買わないレース」を後から入れる場合は、
取得失敗と区別するフラグが必要になる。data/estimate/v10_kimarite/
は 既存の N枠_{成分}pt / N枠_強さpt 形式のままにした(成分は control と
同一)。ブレンドの Plackett-Luce 側と Stage2 の γ 変調が 強さpt を要るので、
どのみち index が必要だったため。おかげで UI の寄与バーもそのまま出る。
買い目だけを別ファイル(estimate/kimarite/picks/)に分けている。v1_basic に依存する(§4.3)。予想者どうしを独立に扱ってきた
既存の前提と異なる。race_cards + 枠なり仮定だけでも
realtime の情報利得の 79% が取れるため、既存予想者と同じく 状態=daily から出して
直前バッチで差し替える。特徴量セットが違うので daily 用と realtime 用の
2 本のモデルを持つ。v10_kimarite。
表示名は「穴予想」。【2026-08-13】下の表のスクリプトは検討時の scratchpad にあったもので、 リポジトリには入っていない。 実装で必要になった部分は
notebooks/ana_prediction/とscripts/に書き直してある(対応は下記)。 数値を引き直すときは下の「現在のスクリプト」を使うこと。
| パス | 内容 |
|---|---|
notebooks/ana_prediction/dataset.py |
進入コース順のパネル(results × stt × payouts × estimate/v1_basic) |
notebooks/ana_prediction/suji_backtest.py |
A案の買い目バックテストと条件付き log-loss(§13.2) |
notebooks/ana_prediction/kimarite_backtest.py |
B案の γ / β / w 再選定と test 報告(§4.3 / §5) |
notebooks/ana_prediction/report.md |
選定記録(スジ表の窓・収縮、B案のハイパラ) |
scripts/build_kimarite.py --report |
Stage1 のホールドアウト log-loss |
scripts/build_kimarite_calibration.py |
荒れ度の校正(§14.3) |
scripts/build_kimarite_logloss.py |
運用中の A/B(§13.3 の主判定) |
実行には .venv(scikit-learn / pandas / numpy)が要る。ただし scripts/ 配下の
推論・集計系は意図的に stdlib のみで、venv 無しでも動く。
以下は「どう検証したか」の記録。再実行はできない。
| スクリプト | 内容 |
|---|---|
kimarite.py |
決まり手の分布・配当プロファイル・条件付き 2-3 着表(§2.4, §3) |
build_dataset.py |
index × stt × sui × tkz × race_cards × results を進入コース順に結合 |
model_c3.py |
Stage1(ロジスティック回帰)+ Stage2 + ブレンド。3 分割で valid 選択 → test 報告(§4, §5.1) |
model_c2.py |
同上の GBM 版(§5.3 の比較用) |
eval_c3.py |
買い目戦略の回収率 / 的中率 / 平均配当 / 万舟数(§5.2, §5.4) |
narrative.py |
人間向け出力例(§6)。P(逃げ) 昇順で並べた先頭を出すので代表例ではない |
probe_ev.py / probe_strategy.py |
検討時の案A(オッズ EV 型)の検証(§5.4 の参考値) |
resolve_blockers.py |
B1/B2/B3 の検証。control の買い目再現とペア bootstrap を含む(§11) |
b1_options.py |
B1 の代替案と 万舟 KPI の再定義(§11.1, §7) |
build_long.py |
index 非依存の拡張データセット(2025-11-01〜、38,227 レース)を作る(§12) |
exp_h_m.py |
H1/H2/M1〜M4 の比較実験(§12) |
kimarite_display.py |
決まり手の表示可否の検証(§14) |
| (§13 の比較) | A案の再現・両案のペア比較・検出力の逆算。resolve_blockers.py のヘルパーを再利用したワンライナーなので、実装時に notebooks/ana_prediction/ へ関数化して移すこと |
lightgbm は libomp.dylib が無いため現状このマシンでは動かない
(そのため Stage1 は sklearn の多項ロジスティック回帰にした。§5.3)。
2026-08-11 の設計レビューで挙がった指摘。Blocking は Phase 2 着手の前提条件。
| # | 重大度 | 指摘 | 対応 |
|---|---|---|---|
| B1 | Blocking | 展開予想の 94.5% が「イン逃げ」。top-1 セル的中率 52.3% は「常に逃げ」の 52.4% を下回り、買い目と主予測が矛盾する | 解決 §11.1。荒れ度 + 1着コース + 立場の明示。abstain は有害と判明したため不採用 |
| B2 | Blocking | control との比較が非ペア。95%CI [74, 97] で差が示せていない | 解決 §11.2。同一レース 3,527 で -1.3pt / p=0.817 |
| B3 | Blocking | 買い目ルールを test を見て選んでいる | 解決 §11.3。valid 固定でも同じルール、test 84.9% で不変 |
| H1 | High | make_cells を分割前の全期間で呼んでおりクラス集合が test に依存。月次再学習でスキーマが変わる問題も同根 |
解決 §12.1。train で凍結(32 クラス)。実害は 0.0007 nat で無かったが運用上必須 |
| H2 | High | §3 の条件表は results の実進入コース、モデルは stt の展示進入コース。定義がずれている | 解決 §12.2。stt に統一。1着コース一致率 96.8%、情報利得も stt が上 |
| H3 | High | Stage2 の γ とブレンドが 強さpt を使うため、v1_basic への依存と、Stage1 だけ学習窓を伸ばせる非対称が生じる |
解決(依存は受容) §12.3。index 非依存の代理は 0.35 nat 劣化。非対称を仕様として明記 |
| H4 | High | 全レースで必ず 5 点買う設計。P(逃げ)=85% のレースでも穴を買う | 指摘が誤り。abstain は回収率を下げる(§11.1)。全レース発火のまま据え置く |
| M1 | Medium | 欠損補完が全場共通の median | 指摘が過大。§12.4 現行が最良。欠損率は最大 3.14%。別途、死に特徴量のバグを発見 |
| M2 | Medium | 学習窓の長さが未定義 | 解決 §12.5。全履歴。ローリングにしない |
| M3 | Medium | 場/風向/天候のエンコーディングが不適切 → 7k レースでは効かず | 解決(採用に反転) §12.6。31k レースでは -0.007 nat 効く |
| M4 | Medium | daily 非対応の波及(母数定義・UI・集計)が未決 | 解決(方針変更) §12.7。daily でも realtime の 79% の情報利得。朝から出す |
§1.2 の B1 / B2 / B3 について、
実データで解決案を検証した記録。スクリプトは resolve_blockers.py / b1_options.py。
当初の解決案(abstain)は実測で否定された。 「荒れそうなレースだけ買う」ために
P(逃げ_1) ≤ しきい値 のレースに絞ると、valid の回収率は下がる一方だった:
| abstain しきい値 | 発火率 | 回収率 | 平均配当 |
|---|---|---|---|
| なし | 100.0% | 79.6% | 3,438 |
| P(逃げ) ≤ 0.70 | 80.6% | 73.9% | 2,989 |
| P(逃げ) ≤ 0.60 | 61.3% | 76.4% | 2,841 |
| P(逃げ) ≤ 0.50 | 44.0% | 74.5% | 2,685 |
| P(逃げ) ≤ 0.40 | 28.1% | 65.6% | 2,392 |
理由は明快で、「予測しやすく荒れるレース」は配当も安い(平均配当が 3,438 → 2,392 円へ 単調に低下)。市場も同じことを知っているため、絞るほど妙味が消える。 §2.1 の favourite-longshot bias が 別の形で出たもの。H4 の指摘(全レース買いは意図的な負け筋)は誤りだった。
さらに abstain では矛盾自体が解けない。しきい値を厳しくしても、 発火したレースの top-1 セルは依然として大半が 逃げ_1 のままだった:
| abstain | 発火率 | 発火レースで top-1 が逃げ | 実際に 1着≠1 だった率 |
|---|---|---|---|
| なし | 100.0% | 94.5% | 45.3% |
| ≤ 0.60 | 58.0% | 90.5% | 58.9% |
| ≤ 0.45 | 33.1% | 83.3% | 69.2% |
| ≤ 0.35 | 19.8% | 72.1% | 74.2% |
採用する解: 「何が起きるか」を宣言するのをやめ、当たる要素だけを表示し、 穴予想という立場を明示する(§6.2)。 表示要素の候補を、実際に荒れた 1,598 レースでの的中率で比較した:
| 表示候補 | 的中率 | 同じことを常に言うベースライン | 差 |
|---|---|---|---|
| セル(決まり手 × 1着コース) top1 | 21.7% | 18.0% | +3.7pt |
| セル top2 のどちらか | 38.4% | — | — |
| 決まり手のみ(コース無視) | 33.7% | 33.7% | ±0.0pt |
| 1着コースのみ(決まり手無視) | 45.1% | 30.1% | +15.0pt |
決まり手は当てられていない(常に「まくり」と言うのと同じ)。 当てられているのは「荒れるなら何コース頭か」。したがって展開の文言は コース主体で書き、決まり手は内部構造として使う。
fun-site の computeBettingPicks(1マーク走行距離 1 − 予測ST + 強さpt/50 − 1.6、
全着 ±0.10 窓、デッド候補の除去)を Python で再実装し、同じ test 期間に適用した。
再現の妥当性は既存ドキュメントの control 実績とよく一致する:
| 既存ドキュメントの control | 本再実装(test 3,527 レース) | |
|---|---|---|
| 点数/レース | 11.7 | 11.5 |
| 的中率 | 46.1〜46.6% | 45.6% |
| 回収率 | ≒ 86% | 86.2% |
同一レース 3,527 でのペア比較(ペア bootstrap 20,000 反復):
| 穴予想(1着≠1 top5) | control v1_basic |
|
|---|---|---|
| 回収率 | 84.9% | 86.2% |
| 点数/レース | 5.0 | 11.5 |
| 的中率 | 12.4% | 45.6% |
| 平均配当 | 3,427 円 | 2,166 円 |
| 万舟的中 | 21 本 | 40 本 |
| 万舟 / 1 万円賭け | 0.12 | 0.10 |
差 -1.3pt、95%CI [-13.1, +11.0]、p = 0.817。
v6_course / v7_aggregate / v8_aionly が退役した -6.9〜-10.6pt(p < 0.01)とは
明確に違い、control と有意差なし。KPI の「回収率は control と同等」は満たす。
ただし CI の幅が 24pt あるので、「同等」と言えるのは「有意に劣らない」までの意味。 §7 のとおり判定には 3 ヶ月以上の運用データが要る。
valid(2026-06-25〜07-17、3,689 レース)だけで、
1着制約 {あり, なし} × top-k {3,5,8,12} × abstain {なし, 0.70, 0.60, 0.50, 0.45, 0.40}
の 48 通りを評価し、KPI(平均配当 ≥ 3,300 円 / 点数 ≤ 6 / 発火率 ≥ 15%)を満たすものから
回収率最大を選んだ。
KPI を満たしたのは 1 通りだけだった: 1着≠1 / top5 / abstain なし(valid 回収率 79.6%)。
1着制約なしの系は平均配当が 1,187〜2,079 円で 3,300 円に届かず、top8 以上は点数 KPI で落ちる。
選定の自由度がほとんど無かったため、B3 の懸念(test を見て選んだ)は結果として
実害が無かったことになる。
この 1 ルールで test を 1 回だけ評価した結果が 回収率 84.9% / 的中率 12.39% / 平均配当 3,427 円 / 万舟 21 本 / 5.0 点で、 §5.2 の値と一致する。当初の数字は据え置いてよい。
k の選び方について: 回収率は k にほぼ依存しない(valid: top3 84.6% / top5 79.6% / top8 75.9% / top12 73.1% / top16 75.7% / top20 78.8%、test: top5 84.9% / top8 79.9% / top12 81.6%)。順序が valid と test で入れ替わるのでノイズと見るべき。 一方 万舟 / 1 万円賭けは k に対して単調に増える(valid 0.05 → 0.20、test 0.12 → 0.18)。 よって k は回収率ではなく体験で決める。top12 は点数 KPI(control の半分以下)を落とすため、 Phase 2 は top5 で開始し、3 ヶ月の運用後に k を再検討する。 なお top12 の test 値は既に見てしまっているので、k を変える判断は valid と実運用データで行うこと。
H1〜H3 / M1〜M4 を実測で検討した記録。検証は estimate/v1_basic に依存しない
拡張データセット(2025-11-01〜2026-08-11、38,227 レース。build_long.py) で行い、
分割は既存と同じ(valid 2026-06-25〜07-17 / test 2026-07-18〜08-11)。
評価は Stage1(セル多クラス)の test log-loss。スクリプトは exp_h_m.py。
min_n = 60 を train 期間だけで判定してクラスを凍結する。実測コストはゼロ:
| クラス決定 | クラス数 | valid | test |
|---|---|---|---|
| 全期間で決定(リークあり) | 29 | 1.8344 | 1.8208 |
| train で凍結 | 28 | 1.8342 | 1.8201 |
差分は 恵まれ_4 の 1 クラスだけで、log-loss 差は 0.0007 nat(誤差)。
リークの実害は無かったが、運用上は凍結が必須(月次再学習でクラスが増減すると
cell_coef.csv のスキーマが変わるため)。凍結したクラス集合をコードに定数として持ち、
サンプルが減ったクラスは収縮で潰す。
実装時の確定値は 32 クラス(2026-08-12)。上の 28 はこの検証で使った train 窓(約 7,000 レース)で
n >= 60を判定した数。本番の学習窓 (全履歴 41,740 レース)では逃げ_2(n=188)/抜き_6(122)/差し_6(104)/恵まれ_2〜5(60〜71)も閾値を超えるため、26 セル + 受け皿のその他_{1着コース}6 個 = 32 になった。一覧はscripts/boatrace/kimarite.pyのCELLS。逃げ_2は 1着コースを展示進入基準で取っていることの帰結で (§12.2)、展示と本番で進入が変わる 12% のレースで定義どおり起きる。
賭け時点で使えるのは展示進入だけなので stt 基準に統一する。ずれの実測:
| 指標 | 一致率 |
|---|---|
| 全 6 艇の進入が一致 | 87.9% |
| 1着コースだけの一致 | 96.8% |
| うち 枠なりレース(85.3%) | 98.4% |
| うち 前付けありレース(14.7%) | 88.0% |
ターゲット定義を変えたときの Stage1 の情報利得:
| ターゲットの 1着コース | test log-loss | ベースレート | 改善 |
|---|---|---|---|
| 展示進入(stt)基準 | 1.8201 | 2.0009 | +0.1809 nat |
| 実進入(results)基準 | 1.8037 | 1.9578 | +0.1541 nat |
実進入基準は絶対値の log-loss こそ低いが、ベースレートも低い(実進入のほうが 分布が集中している)ため、モデルが稼いでいる情報量は展示進入基準のほうが大きい。 賭け時点で使えるかどうかを差し引いても展示進入が有利。 §3 の集計表は stt 基準で作り直す(実装時)。
v1_basic への依存は残す(除去を試して断念)強さpt を index 非依存の代理指標に置き換えられれば、パイプライン全体を 全履歴で学習できて依存も消える。実際に試した結果:
| ブレンド相手の Plackett-Luce | test log-loss(3連単 120 通り) |
|---|---|
PL(強さpt) — v1_basic 依存 |
4.0323 |
| PL(全国勝率 0.7 + 当地勝率 0.2 + M2率 0.1) — index 非依存 | 4.3814 |
代理指標では 0.35 nat も劣化する。 強さpt は場別に学習した重みで枠番pt
(コース強度)を含んでおり、選手成績だけの線形結合では代替できない。
代替を作り込むことは v1_basic を作り直すことに等しいので、依存は受け入れる。
したがって学習窓は次の非対称になる。これを仕様として明記する:
| 構成要素 | 学習に使えるデータ | 依存 |
|---|---|---|
| Stage1(セル多クラス) | 2025-11-01〜(全履歴) | results / previews / race_cards のみ |
| Stage2(γ 変調)・最終ブレンド | 2026-05-01〜(index のある期間) | estimate/v1_basic |
運用上の含意: v1_basic の月次 weights が更新されると v10_kimarite の出目も動く。
v1_basic を退役させる場合は v10_kimarite も同時に見直しが要る。
レジストリと docs/data/estimate.md に依存関係を明記すること。
レビューで「全場共通 median は無意味」と指摘したが、実測では現行が最良だった:
| 補完方式 | valid | test |
|---|---|---|
| 全体 median(現行) | 1.8342 | 1.8201 |
| 場別 median + 欠損フラグ | 1.8365 | 1.8240 |
| レース内 z 化 | 1.8471 | 1.8308 |
前提が違っていた。欠損はほとんど起きない:
| 特徴量 | 欠損率 |
|---|---|
| ボート2連対率 | 3.14% |
| モーター2連対率 | 2.89% |
| 展示タイム | 1.29% |
| スタート展示 ST | 0.01% |
| 何か 1 つでも欠損があるレース | 5.22% |
「preview 欠損が本番で常時起きる」という想定が誤りで、補完方式を凝る価値は無い。
ただし 1 件バグを発見: 検証コードで
race_cardsの体重列名を取り違えており、c{1..6}_体重cが 100% 欠損の死に特徴量になっていた。実装時は列名を確認して 復活させるか、特徴量から外すこと。
train の開始日だけを振った実測(index 非依存の特徴量、test は固定):
| train 開始 | レース数 | valid | test | ベースレート比改善 |
|---|---|---|---|---|
| 2025-11-01(全履歴) | 30,865 | 1.8342 | 1.8201 | +0.1809 nat |
| 2026-01-01 | 22,526 | 1.8370 | 1.8277 | +0.1731 |
| 2026-03-01 | 14,695 | 1.8475 | 1.8387 | +0.1628 |
| 2026-05-01 | 7,639 | 1.8612 | 1.8558 | +0.1463 |
| 2026-06-01 | 3,316 | 1.8934 | 1.8890 | +0.1144 |
単調に改善し、古いデータで劣化する兆候が無い。 既存 weights の 6 ヶ月ローリングとは 違い、Stage1 は全履歴で学習する。季節性は特徴量(気温・水温・風)で吸収されている とみなす。データが増えて過学習が問題になったら改めて窓を検討する。
7,000 レースでは差が出なかった one-hot 化が、31,000 レースでは効くようになった:
| 構成 | 学習データ | valid | test |
|---|---|---|---|
| 数値のまま | 7,000 | 1.8465 | 1.8306 |
| one-hot 場/天候 + 風向 sin/cos | 7,000 | 1.8513 | 1.8334 |
| 数値のまま | 31,000 | 1.8342 | 1.8201 |
| one-hot 場/天候 + 風向 sin/cos | 31,000 | 1.8301 | 1.8132 |
正則化が緩められるようになり、場ごとの差を使えるようになったため。 §12.5 の全履歴学習とセットで採用する。
「preview が必須だから daily では出せない」としていたが、preview 抜きでもかなり効く:
| 特徴量セット | test log-loss | ベースレート比改善 | realtime 比 |
|---|---|---|---|
| realtime(展示進入順・全特徴) | 1.8201 | +0.1809 nat | 100% |
daily(枠なり仮定 + race_cards のみ) |
1.8586 | +0.1423 nat | 79% |
| daily + 気象 | 1.8603 | +0.1406 nat | 78% |
朝の時点で realtime の情報利得の 79% が取れる。 枠なり率が 85.3% あるため、
枠なり仮定のコストが小さいのが効いている。気象を足しても改善しない
(朝は previews/sui が未取得なので、そもそも使えない)。
したがって:
状態=daily でも荒れ度と買い目を出す。既存予想者と同じ挙動になり、
§8.2 に挙げた「朝は非表示」という
特殊扱いが不要になるcell_coef.csv を cell_coef_daily.csv / cell_coef_realtime.csv に分ける状態 列の規約どおり| 項目 | 決定 |
|---|---|
| Stage1 の学習窓 | 全履歴(2025-11-01〜)。ローリングにしない |
| Stage1 のクラス | train 期間で凍結した 32 クラス。定数としてコードに持つ |
| 進入コースの定義 | 展示進入(previews/stt)に統一。§3 の表も作り直す |
| エンコーディング | 場・天候は one-hot、風向は sin/cos |
| 欠損補完 | 全体 median のまま。死に特徴量(体重)は修正または削除 |
| Stage2・ブレンド | v1_basic 依存を受け入れる(学習窓は 2026-05〜) |
| daily 対応 | daily 版モデルを別に持ち、朝から出す |
これらを反映した Stage1 の想定 test log-loss は 1.8132(ベースレート 2.0009 比で +0.188 nat)。当初設計の 1.8306 から 0.017 nat の改善。
§12 までの設計(B案)に対し、
「1着 = 1コース以外で強さpt 最大」+「スジ表 P(R2,R3 | R1)」だけの簡易版(A案)が
どこまで迫るかを実測したところ、回収率では区別できなかった。
そこで A案を control、B案を実験スロットとして 2 つ並行運用する。
A案 v9_suji「スジ予想」(control) |
B案 v10_kimarite「穴予想」(実験) |
|
|---|---|---|
| 1着の決め方 | 1コース以外で 強さpt 最大の艇(決め打ち 1 つ) | Stage1 の P(決まり手, 1着コース) から 120 通り確率を作り、その中で決まる |
| 2-3着の条件 | P(R2,R3 \| R1)(1着コースだけで条件付け。6 × 20 = 120 行) |
P(R2,R3 \| 決まり手, R1)(セルで条件付け。32 × 20 = 640 行)+ 強さpt 変調 |
| 強さpt の使い方 | 1着の選定に直接使う | Stage2 の γ 変調と最終ブレンドのみ |
| 学習が要るもの | スジ表 1 枚だけ | Stage1 の係数(daily / realtime 2 本)+ ペア表 |
| 依存 | estimate/v1_basic のみ |
v1_basic + 新規学習パイプライン |
A案は §12 で決めた実装項目のほとんどを不要にする — クラス集合の凍結(H1)・欠損補完(M1)・学習窓(M2)・エンコーディング(M3)・ daily/realtime の 2 本モデル(M4)がすべて消え、残るのは条件付きテーブル 1 枚。
A案の数値は本番構成のもの(全履歴・収縮なし)。当初この節に載せていた 82.0% は 「train 窓のみ・収縮なし」の暫定値だった。学習窓と収縮の選定記録は
notebooks/ana_prediction/report.md。
| 戦略 | 的中率 | 回収率 | 95%CI | 平均配当 | 万舟 | 万舟/1万円 |
|---|---|---|---|---|---|---|
| A案(スジ予想) | 10.25% | 80.6% | [68, 94] | 3,931 円 | 27 | 0.153 |
| B案(穴予想) | 12.39% | 84.9% | [74, 96] | 3,427 円 | 21 | 0.119 |
【2026-08-13 更新】本番構成で引き直した B案の実測(test 3,614 レース、 A案と同一レース。γ=0.5 / β=2.4 / w=0.7、Stage1 は 32 クラス・全履歴):
戦略 的中率 回収率 95%CI 平均配当 万舟 万舟/1万円 A案(スジ予想) 10.27% 80.6% [69, 93] 3,927 円 27 0.149 B案(穴予想) 12.65% 87.1% [76, 98] 3,442 円 20 0.111 傾向は実験時と同じ(B案が回収率と的中率で上、A案が平均配当と万舟率で上)。 どちらも CI が 22pt あり、この差では優劣を言えないのも同じ。 3連単 log-loss では PL 単体 4.0382 に対しブレンド 3.9196、 改善 +0.1186 nat [+0.1003, +0.1376](実験時は +0.0961)。
買い目 5 点のうち 両案で共通するのは平均 2.62 点(完全一致は 1.2%)。 別物の買い目を出しているが、成績は区別できない:
| 比較 | 差 | 95%CI | p |
|---|---|---|---|
| 回収率(B − A) | +4.2pt | [-9.4, +17.2] | 0.518 |
| 万舟/1万円(B − A) | -0.045 | [-0.108, +0.017] | — |
| 3連単 log-loss(PL単体 → B案ブレンド) | +0.0961 nat | [+0.0795, +0.1126] | <0.0001 |
これが本節の要点。 上の CI から必要サンプル数を逆算すると:
| 判定指標 | 現在の CI 半幅 | 観測された効果 | 効果を検出するのに必要なレース数 | 所要期間(141 レース/日) |
|---|---|---|---|---|
| 回収率 | ±13.3pt | +4.2pt | 34,700 | 約 8.2 ヶ月 |
| 回収率(2pt を検出) | 同上 | 2.0pt | 153,000 | 約 36 ヶ月 |
| log-loss | ±0.0166 nat | +0.0961 nat | 418 | 約 0.1 ヶ月 |
回収率で A案 と B案 を比べても、8 ヶ月かけないと差が出ない。 一方 log-loss は n = 3,527 で既に効果の 5.8 倍の精度があり、決着済み。
したがって A/B の判定は次のように役割を分ける:
| 指標 | 役割 | 判定 |
|---|---|---|
| 3連単 log-loss | 主判定。決まり手レイヤーが確率の質を上げているか | 数週間で決着。B案が有意に上回らなければ B案を退役 |
| 回収率 | ガードレール。破滅的な劣化の検知のみ | control(v1_basic)比で -7pt 級の劣化が出たら退役。A案 vs B案 の微差は判定に使わない |
| 万舟/1万円賭け・平均配当 | 体験指標。穴予想として成立しているか | 目標値を置くが、有意差判定はしない(検出力が足りない) |
| P(逃げ) の校正誤差 | B案のみ。荒れ度メーターの品質 | 各予測帯で ±5pt 以内 |
既存の退役判定との違い:
v6_course/v7_aggregate/v8_aionlyは回収率で -6.9〜-10.6pt という大きな劣化だったため 13〜20 日で検出できた。 A案 vs B案 の +4.2pt はその半分以下なので、同じ方法では決着しない。 予想者レジストリに log-loss を主判定とするスロットが登場するのは初めてなので、docs/data/estimate.mdに判定方法の違いを明記すること。
§8 の Phase 構成を次のように読み替える。
| Phase | 内容 | 備考 |
|---|---|---|
| 1 | A案 v9_suji を先に出す |
スジ表 1 枚 + 既存の強さpt だけ。§12 の実装項目は不要。買い目まで出せる |
| 1’ | B案の Stage1 を 荒れ度メーターとして出す | 買い目は出さない。校正を 3 ヶ月モニタする。§12.8 の構成一式が要る |
| 2 | B案 v10_kimarite を買い目つきで投入し、A案と log-loss で比較 |
2026-08-13 実装完了。build_kimarite_picks.py / build_kimarite_logloss.py |
A案が先になるのは、Stage1 の学習パイプラインが要らず実装が軽いため。
BettingPicks の出目リスト型対応は A案(Phase 1)の時点で入れる。
A案は 1着が 1 艇に決まるのでフォーメーションでも近似できるが、
スジ表の「上位 5 ペア」を厳密に表すにはフォーメーションでは足りず、
最小フォーメーションで近似すると実際に買う点数が 5 点より増えて回収率が変わる。
§13.2 の A案の実測値(80.6% / 5.0 点)は
厳密な 5 点前提なので、近似すると数字が再現しない。B案でも同じ型が要るため、
先に入れておくほうが二度手間にならない。
型変更の内容(§8.2 も参照):
// 現行
type BettingPicks = { first: number[]; second: number[]; third: number[] };
// 拡張後 (直和型)
type BettingPicks =
| { kind: "formation"; first: number[]; second: number[]; third: number[] }
| { kind: "combos"; combos: [number, number, number][] };
countFormationCombinations / isFormationHit / computeBetPayout /
BettingPicks.astro を kind で分岐させる。既存 4 予想者は kind: "formation" の
まま挙動を変えないこと(回収率の連続性を壊さない)。バッチと web が
必ず同じヘルパー経由で解決する現行の規約は維持する。
予想者 ID:
v9_suji(表示名「スジ予想」)/v10_kimarite(表示名「穴予想」)。kimariteは「決まり手でセル分解した確率モデル」という内部構造を指す。 決まり手の表示は §14 のとおり両案共通のレイヤーで行う ので、ID が表示内容を約束するものではない点に注意。
§11.1 では「決まり手は
情報量ゼロ」と判定したが、あれはレース単位の周辺 argmax を測ったもの。
表示形式を変えて測り直したところ、出せる形と出せない形がはっきり分かれた。
検証は kimarite_display.py(test 3,527 レース)。
買い目 1 点ごとに「この出目はどの決まり手を想定した形か」を添える。
穴の買い目
3-1-4 3コースの まくり差し ── 内を差して 1コースが残る形
3-4-5 3コースの まくり ── 内を全部かぶせて外が続く形
3-1-5 3コースの まくり差し
的中した買い目 437 点で、注釈の決まり手が実際の決まり手と一致した率:
| 注釈の作り方 | 一致率 |
|---|---|
| 1着コース別の最頻決まり手(コースしか見ない) | 48.7% |
| 出目(3 コースの並び)別の最頻決まり手 — 静的テーブル | 63.2% |
モデルの P(セル \| その出目) の最有力セル |
60.6% |
+14.5pt の情報がある。 効く理由は明快で、決まり手を特定しているのは 事前情報ではなく「2着・3着の並び」だから。1コースが 2着に残っていれば まくり差し、外が続いていればまくり —— 出目の形そのものが決まり手を語っている。
重要: この注釈にモデルは要らない。 静的テーブル(
出目 → 最頻決まり手、120 行)が モデルのP(セル|出目)と同等以上(63.2% vs 60.6%、n=437 なので差は誤差範囲)。 したがって 決まり手の注釈は A案v9_sujiでも同じように出せる。 予想者ごとの差別化要素ではなく、両案共通の表示レイヤーとして実装する。
「このレースはまくりになる」という予測は、1着コースを与えた条件下でも 経験ベースレートを上回らない:
| 実際の 1着コース | n | モデルの的中率 | ベースレート(そのコースの最頻決まり手) | 差 |
|---|---|---|---|---|
| 1コース | 1,929 | 95.9% | 95.9%(逃げ) | ±0.0pt |
| 2コース | 478 | 58.2% | 60.0%(差し) | -1.9pt |
| 3コース | 481 | 37.8% | 37.8%(まくり) | ±0.0pt |
| 4コース | 356 | 39.3% | 44.9%(まくり) | -5.6pt |
| 5コース | 212 | 57.5% | 57.5%(まくり差し) | ±0.0pt |
| 6コース | 71 | 45.1% | 57.7%(まくり差し) | -12.7pt |
| 加重平均 | 3,527 | 73.8% | 74.9% | -1.1pt |
どのコースでもベースレートを超えない。 レース単位で「まくりが来ます」と 言い切る表示は、事実として当てられていないので出さない。
argmax は使えないが、確率値としての校正は良好なので、 「まくり 16% / まくり差し 10% / 差し 12% …」という分布の提示は成立する:
| 決まり手 | 予測平均 | 実測 | 差 | 予測上位20%での 予測 / 実測 |
|---|---|---|---|---|
| 逃げ | 53.6% | 52.4% | -1.2pt | 78.0% / 74.6% |
| まくり | 16.0% | 15.3% | -0.7pt | 30.2% / 27.5% |
| まくり差し | 10.4% | 12.7% | +2.3pt | 18.9% / 19.7% |
| 差し | 11.9% | 12.2% | +0.3pt | 22.8% / 22.4% |
| 抜き | 6.2% | 5.9% | -0.3pt | 9.1% / 7.8% |
ずれは -1.2〜+2.3pt。「まくりが来る」ではなく「まくりの確率は 30%」なら正しい。 ただし §14.2 のとおり argmax を取ると ベースレートに負けるので、必ず全決まり手の確率を並べて出し、1 つを強調しないこと。
| 表示 | 可否 | 根拠 | どちらの予想者で |
|---|---|---|---|
| 買い目 1 点ごとの決まり手注釈 | ○ | 63.2% 対 48.7% | A案・B案とも(静的テーブル) |
| 決まり手の確率分布(全種類を並べる) | ○ | 校正誤差 -1.2〜+2.3pt | B案のみ(Stage1 が要る) |
荒れ度 1 − P(逃げ) |
○ | 校正誤差 概ね ±3pt | B案のみ |
| 「荒れるなら何コース頭か」 | ○ | 45.1% 対 30.1% | B案のみ |
| 「このレースは○○が決まる」(argmax) | × | 条件付きでもベースレート -1.1pt | — |