モデルの点数から、馬券の結果へ

前回は、競馬データを使って最初の機械学習モデルを動かし、
学習から評価までを一度通したところまでを書きました。
Accuracyは約73%でしたが、複勝圏内の馬を十分に見つけられておらず、
その数字だけでは使えるモデルかどうか判断できませんでした。

そもそも、私が競馬AIで目指していたのは、
分類の評価値を高くすること自体ではありません。
予測結果を払戻と結びつけ、収益性まで評価することでした。

そのためには、予測結果を一定のルールで評価対象にし、
実際の払戻と比べるところまで処理をつなぐ必要があります。
ここから、AUCやAccuracyに加えて、
ROI(回収率)を見ながら開発を進めるようになりました。

ここで使うROIは、総払戻額を総購入額で割った値です。
1なら購入額と払戻額が同じで、1を下回れば差し引きでマイナスになります。
的中した回数だけでなく、いくら買って、いくら戻ったのかを見るための数字です。

予測を保存し、評価ロジックを別に作る

利益を評価するため、最初にOOFという予測結果を用意しました。
データをいくつかに分け、それぞれを学習に使わなかったモデルで予測し、
その結果を一つにまとめたものです。
同じレースの馬が学習側と評価側に混ざらないよう、レース単位で分けました。

学習に使ったデータを同じモデルで予測し、その結果だけで評価すると、
覚えた内容にうまく答えているだけかもしれません。
OOFは、その影響を減らして評価条件を試すための材料になりました。

モデルが出すのは各馬の予測確率です。
そこからどの予測を評価対象にするかは、別のコードで決めます。
予測値の扱いを変えながら、複数の評価方法を試しました。

多数の予測候補を絞り込み、収益曲線へつなげる流れを抽象化したイメージ

開発の途中では、この評価ロジックまでXGBoostに備わっているのか、
こちらで別に作る必要があるのかも確認しました。

作業を進めながら、一度手を止めて、
HTMLからCSVを作り、学習し、過去レースを予測し、
複勝を買ったと仮定して利益を調べているのだと整理し直しました。
コードが増えるだけでは、何を確かめているのか見失ってしまうためです。

保存していたのは、オッズではなく払戻でした

利益評価を進めようとしたところ、複勝オッズの列が空でした。
パーサーを直せば取れるのかと思いましたが、
保存済みのHTMLを確認すると、そもそもレース後の結果・払戻ページでした。

購入前に見るオッズと、レース後に確定する払戻額は違います。
過去データのROIを振り返るには確定払戻を使えますが、
発走前の評価に、未来の払戻額を使うことはできません。

そこで、まずは結果HTMLから複勝払戻を取り出し、
予測結果と結合して事後のROIを計算することにしました。
事前のオッズを取得し、期待値の評価に使う処理は、その次の課題にしました。

最初に最後まで通った単純な条件のバックテストでは、
払戻は購入額を大きく下回りました。

数字としては厳しい結果でした。
それでも、モデルの評価値を眺めるだけだったところから、
この条件では収益が残らない、と確認できるようになりました。

確率の数字を、そのまま信じてよいのか

一定以上の予測確率を持つ馬を選んでも、
実際の的中率は予測値から期待したほど高くありませんでした。
モデルが出す数値を、そのまま実際の的中確率として使ってよいのかが問題になります。

そこで、予測確率を実測の割合へ近づけるため、
Isotonic Regressionによる確率校正を加えました。
以後、校正後の確率を保存し、
収益性を比較する評価材料として使いました。

校正したからといって、すぐに利益が出るわけではありませんでした。
評価条件によってはプラスに見える例もありましたが、
対象が極端に少ない結果では、戦略の良し悪しは判断できません。

また、初期の校正はOOF全体で補正器を学習し、
同じデータへ適用する簡易な方法でした。
校正まで含めて厳密に学習用と評価用を分けた検証にはなっていません。
数値を出す仕組みを作ることと、その数値をどこまで信用できるかは、
分けて考える必要がありました。

予測確率だけでは、安定した評価条件を決めきれませんでした

2025年12月に開発を再開してからは、データの区分を分けたり、
データ期間を増やしたりしながら、学習とバックテストを一組で回しました。
小さな標本で良い数字が出ても、そのまま結論にはせず、
距離や馬場、期間によって結果が変わるかを調べていきました。

その中で、単純に予測確率の高い馬だけを評価対象にしても、
安定した結果にならないことが見えてきました。
対象頭数を増やす方法も試しましたが、改善にはつながりませんでした。

そこで、予測確率だけで決めるのではなく、
別の評価材料も組み合わせて対象をさらに絞る方向へ進みました。
どの予測を評価対象にし、どれを見送るかも分けるようになった形です。

1年分へ広げて見直すと、
単一の条件だけを見るより、複数の評価材料を組み合わせた方が、
当時の結果を説明しやすいことも分かってきました。

年間のROIだけでは見えなかった偏り

1年分の検証では、保存済みモデルを読み込み、
学習し直さず予測する経路を作りました。
OOFと保存モデルの予測は同じではないことも確認しています。
さらに、カテゴリの数値化や特徴量の整合性には課題が残っていました。
この1年分の評価は、未知の期間だけを使った厳密な検証とは言えません。

その前提で1年分を集計すると、
年間ではプラスに見える条件もありました。
ところが月ごとに分けると、特定の時期に利益が偏っていることが分かりました。
年間で一つの数字にすると見えない偏りがあったのです。

評価対象の絞り方を変えると、その偏りが小さく見えるケースもありました。
そこで、過去検証で比較的安定して見えた評価条件を、将来データで検証する暫定条件にしました。

ただし、これは過去データを見て選んだ条件です。
未来でも同じ傾向が続くかは、まだ分かりません。
将来データの記録と比較用の記録を分け、未来のレースで確かめる方針にしました。

この段階では資金配分の検討より、まず未来のレースでも同じ傾向が再現するかを確かめることを優先しました。
評価条件の再現性を確認してから、その先の運用を考える方針でした。

次は、まだ結果のないレースで動かす

予測を保存し、評価条件を適用し、確定払戻と比べる。
この流れができて、ようやく何を変えると収益性の評価がどう変わるのかを、
具体的に考えられるようになりました。

次に必要なのは、過去の完成済みデータではなく、
発走前に手に入る情報で同じ処理を動かすことでした。
当日のレースを見つけ、出馬表を取り、過去履歴と組み合わせる。
次回は、その当日運用の入口を作り始めた時のことを書きます。