【競馬AI 13】穴馬を見抜く!人気薄の好走馬を予測するAIモデルの作り方

競馬AI

競馬予想の中でも最も難しいのが「穴馬」の見極めです。人気がなくても上位に食い込む馬を見つけることができれば、回収率は一気に跳ね上がります。この記事では、既存のAIモデルを少し改良して、「人気が低いのに3着以内に入る馬」を予測する「穴馬モデル」を作成する方法を紹介します。初心者でも理解できるよう、ポイントを丁寧に解説していきます。


穴馬モデルの処理概要

通常の競馬予測モデルでは、「1着になるかどうか」「3着以内に入るかどうか」などを目的変数(ラベル)として設定します。
しかし、本記事では人気が低いにもかかわらず好走した馬、いわゆる「穴馬」を狙い撃ちします。

このモデルの目的は次のとおりです。

人気順が6番以降にもかかわらず、3着以内に入った馬を1(=的中)
それ以外を0(=ハズレ)とする二値分類モデルを作成します。

このようにラベル付けを変更することで、AIは「人気薄の中から走る馬」を学習できるようになります。
一般的なモデルが「順当な上位馬」を当てにいくのに対し、このモデルは「穴を狙う」戦略的なモデルです。


プログラム

estimation.pyの以下を書き換えてください。

df['KAKUTEI_CHAKUJUN'] = pd.to_numeric(df['KAKUTEI_CHAKUJUN'], errors='coerce').between(1, 3).astype(int)

このように書き換える。

# 人気が6番以降で3着以内に入った馬を1、それ以外を0
df["KAKUTEI_CHAKUJUN"] = (
    pd.to_numeric(df["KAKUTEI_CHAKUJUN"], errors="coerce").between(1, 3) &
    pd.to_numeric(df["TANSHO_NINKIJUN"], errors="coerce").ge(6)
).astype(int)

この1行を追加・修正するだけで、既存のモデルを「穴馬モデル」に変えることができます。
ここで使われている betweenge、そして astype(int) が重要なポイントです。


解説:コードの仕組み

それでは上の1行を分解して、何をしているのか詳しく見ていきましょう。

① 着順と人気の数値変換

まず、pd.to_numeric() を使って、文字列型の着順や人気順を数値に変換しています。
CSVなどからデータを読み込むと、これらは文字列として扱われることが多いので、数値に直す必要があります。

pd.to_numeric(df["KAKUTEI_CHAKUJUN"], errors="coerce")

errors="coerce" とすることで、変換できない値(例:「取消」「除外」など)は自動的にNaNになります。
これにより、モデルが扱いやすいクリーンなデータになります。


② 着順条件の設定(3着以内)

次に .between(1, 3) で「1着〜3着以内かどうか」を判定します。

pd.to_numeric(df["KAKUTEI_CHAKUJUN"], errors="coerce").between(1, 3)

これにより、3着以内なら True、それ以外は False になります。
この部分が「好走した馬を抽出する条件」です。


③ 人気順条件の設定(6番人気以降)

そして .ge(6) は「人気順が6以上(つまり6番人気以下)」を意味します。

pd.to_numeric(df["TANSHO_NINKIJUN"], errors="coerce").ge(6)

この条件も同様に、該当すれば True、そうでなければ False を返します。
つまり「6番人気より人気が低い」馬を抽出する条件です。


④ 条件の組み合わせ(&演算子)

両方の条件を & で掛け合わせることで、「3着以内 かつ 人気6番以下」の馬を抽出します。

(...between(1, 3)) & (...ge(6))

この2つの条件が同時に True なら、その馬は「穴馬」として 1 に設定されます。
それ以外は 0 です。


⑤ 最後に整数化(astype(int))

論理値(True / False)はそのままでは扱いづらいため、astype(int)1 / 0 に変換します。

これで、目的変数 KAKUTEI_CHAKUJUN が「1(穴馬)」「0(その他)」という形で完成します。
この列を学習データのラベルとして、LightGBMやXGBoostなどのモデルを再学習します。


ポイント:なぜ穴馬モデルを分けるのか?

多くの人が最初にAIを使うとき、「全レース共通で1つのモデル」を作りたくなります。
しかし、**「人気馬が勝つパターン」と「穴馬が好走するパターン」**は全く異なります。

たとえば、人気馬は過去成績が安定していて、上位条件(コース・距離・騎手など)が揃っていることが多いですが、穴馬はそうではありません。
むしろ、「展開」「天候」「馬場状態」などの外的要因が大きく影響します。

そのため、モデルを2つに分けて学習させるのが理想です。

モデル名ラベル条件特徴的な学習傾向
人気馬モデル人気1〜5位 × 3着以内安定重視・能力差の再現
穴馬モデル人気6位以下 × 3着以内展開・馬場・ペースなど変動要因に敏感

このように分けることで、両方のモデルがそれぞれ得意なパターンを学習し、結果的に精度が上がります。


モデル学習の流れ

穴馬モデルを作る流れは、基本的には通常のモデルと同じです。

  1. 特徴量を作成する(過去成績、馬場状態、枠順、騎手など)
  2. 目的変数を「穴馬」定義で作る
  3. LightGBMなどで学習する
  4. テストデータで精度を検証する

このうち変更するのは「②目的変数の作り方」だけです。
つまり、今回紹介した1行を差し替えるだけでOKです。


穴馬モデルの精度を上げるコツ

穴馬モデルは、的中率よりも回収率を重視します。
そのため、「当たらないけど当たったときに大きい」予想を評価する設計が必要です。

① 評価指標を工夫する

一般的なAccuracy(正解率)ではなく、F1スコアや**期待値(オッズ×確率)**を重視しましょう。
たとえば、人気薄でオッズが高い馬を的中させれば、少数でも利益を出せます。

② 特徴量を工夫する

穴馬は「不安定な中に光る条件」が鍵です。
具体的には以下のような特徴量が有効です。

  • 前走と馬場状態が異なるときの成績差
  • 騎手変更後の成績
  • 枠順と距離の相性
  • 過去に同条件で上位に入った回数

これらの特徴は、人気馬モデルではあまり効かないことも多いですが、穴馬モデルでは強力なシグナルになります。

③ 出走頭数の多いレースに絞る

頭数が少ないレースでは、穴馬の出番が少ない傾向にあります。
出走頭数が14頭以上のレースに限定すると、モデルの再現性が高まります。


実運用:人気馬モデルとの組み合わせ

最終的な運用では、「人気馬モデル」と「穴馬モデル」のダブル予測が効果的です。

  1. 人気馬モデルで上位人気の中から勝ち馬を選出
  2. 穴馬モデルで人気薄の中から3着以内候補を選出
  3. 両者を組み合わせて馬連・三連単・ワイドを構築

こうすることで、堅い決着にも荒れた展開にも対応できる柔軟な予想が可能になります。


まとめ

今回は、人気6番以降で3着以内に入る馬を狙う「穴馬予測モデル」の作り方を紹介しました。
わずか1行のコード変更で、AIが学習する目的を大きく変えることができます。

6番人気以降という閾値は自由に調整してみてください。オッズなどで分けても良いかもしれません。

競馬AIの面白さは、「どんな馬を狙うか」でまったく違う世界が見えるところです。
次はこのモデルを学習させて、どのような特徴が「穴馬の兆候」になっているのかを分析してみましょう。

コメント