競馬予想に機械学習を使う方法|問題の立て方と評価の仕方、JRDBのデータと論文の探し方

競馬に機械学習を使うときは、「1頭ずつ勝つかどうかを当てる分類」「レース内の順位を当てるランキング学習」「タイムを当てる回帰」のどれで解くかを決め、出した確率をレース内で合計1にそろえ、未来のレースで評価するのが基本です。的中率や回収率ではなく、確率の当てはまり(log loss)と単純な基準との比較で良し悪しを判断します。

この記事は、競馬を題材に機械学習の「問題の立て方」と「評価の仕方」を整理し、研究論文の探し方とデータ(JRDBなど)の条件をまとめたものです。お金を増やす方法を示すものではありません。JRAの設定払戻率は単勝・複勝で80%、3連単で72.5%で(JRA「馬券のルール」(2026年10月15日確認))、機械学習を使っても利益が出るとは限りません。また、20歳未満の人は競馬法第28条により勝馬投票券を購入できません(競馬法(e-Gov法令検索))。

開発手順やデータの利用規約を順に知りたい方は競馬予想AIの作り方、scikit-learnでの実装を一通り試したい方はPythonで競馬予想AIを作る方法を先に読むと流れがつかめます。

競馬は機械学習で何を予測するのか:3つの問題設定

問題設定 予測するもの 手法の例 注意点
二値分類 その馬が1着(または3着以内)になるか ロジスティック回帰、勾配ブースティング 1頭ずつ独立に予測するので、レース内の確率の合計が1にならない
ランキング学習 レース内の馬の並び順 LightGBMのlambdarank など 出力はスコアで確率ではない。確率に直す工夫が要る
回帰 走破タイム、着差 線形回帰、勾配ブースティング 距離・馬場・競馬場でタイムの基準が違うので、補正しないと比べられない

競馬の特徴は、「同じレースの馬どうしが競争している」ことです。ある馬が勝つ確率は、その馬の能力だけでなく、同じレースに誰が出ているかで決まります。そのため、1頭ずつ予測する方法でも、最後はレース単位で確率をそろえる必要があります。統計学ではこの構造を扱う方法として条件付きロジットモデルが古くから研究されていて、後半で紹介する論文にも競馬への適用例があります。

Pythonで確かめる:レース内で確率をそろえる

実在のレースではなく、プログラムで作った架空のデータで試します。見えない「実力」から結果を作り、実力をノイズ入りで反映した指標f1と、結果と無関係な指標f2を用意しました。前の1,500レースで学習し、後の500レースで評価します。Python 3.12・pandas 3.0.6・scikit-learn 1.9.1・LightGBM 4.7.0で実行しています。

# 架空のレースデータ(実在のレース・馬とは無関係)
import numpy as np
import pandas as pd

rng = np.random.default_rng(0)
rows = []
for race in range(2000):
    n = int(rng.integers(8, 17))                  # 出走頭数 8〜16
    ability = rng.normal(0, 1, n)                 # 見えない実力
    feat1 = ability + rng.normal(0, 1.0, n)       # 実力を反映した指標(ノイズ入り)
    feat2 = rng.normal(0, 1, n)                   # 結果と無関係な指標
    finish = (-(ability + rng.normal(0, 1.2, n))).argsort().argsort() + 1
    for i in range(n):
        rows.append([race, n, feat1[i], feat2[i], finish[i]])
df = pd.DataFrame(rows, columns=["race", "tosu", "f1", "f2", "rank"])
df["win"] = (df["rank"] == 1).astype(int)
train = df[df["race"] < 1500]          # 前の1500レースで学習
test = df[df["race"] >= 1500].copy()   # 後の500レースで検証
print(df.shape, train["race"].nunique(), test["race"].nunique())

実行結果:

(24009, 6) 1500 500

1頭ずつの分類は、レース内の合計が1にならない

from sklearn.linear_model import LogisticRegression

X_cols = ["f1", "f2"]
clf = LogisticRegression().fit(train[X_cols], train["win"])
test["p_raw"] = clf.predict_proba(test[X_cols])[:, 1]

# レースごとに確率を足すと1にならない
s = test.groupby("race")["p_raw"].sum()
print("1レース内の確率の合計:", round(s.min(), 2), "〜", round(s.max(), 2))

# レース内で合計1になるように割り直す
test["p_norm"] = test["p_raw"] / test.groupby("race")["p_raw"].transform("sum")
print("割り直した後の合計:", test.groupby("race")["p_norm"].sum().round(6).unique())

実行結果:

1レース内の確率の合計: 0.37 〜 2.22
割り直した後の合計: [1.]

1頭ずつ「勝つ確率」を出すと、レースによって合計が0.37になったり2.22になったりします。1レースで勝つのは1頭だけなので、レース内の合計で割って1にそろえます。

3つの方法を同じ物差しで比べる

import lightgbm as lgb

def evaluate(name, p):
    t = test.assign(p=p)
    winners = t[t["win"] == 1]
    logloss = -np.log(winners["p"]).mean()                 # 勝ち馬に付けた確率の対数損失
    top = t.loc[t.groupby("race")["p"].idxmax()]
    hit = top["win"].mean()                                 # 確率1位の馬が勝った割合
    print(f"{name:<22} 勝ち馬のlog loss {logloss:.3f}  本命の的中率 {hit:.1%}")

# 1) 何も学習しない基準:全馬に 1/頭数
evaluate("基準(1/頭数)", 1 / test["tosu"])
# 2) 1頭ずつの二値分類 → レース内で割り直し
evaluate("ロジスティック回帰", test["p_norm"])
# 3) ランキング学習(LightGBM lambdarank)→ レース内でソフトマックス
rk = lgb.LGBMRanker(objective="lambdarank", n_estimators=200, learning_rate=0.05,
                    verbose=-1, random_state=0)
rel = (train["tosu"] - train["rank"]).clip(lower=0)    # 着順が良いほど大きい関連度
rk.fit(train[X_cols], rel, group=train.groupby("race").size().to_numpy())
score = pd.Series(rk.predict(test[X_cols]), index=test.index)
ex = np.exp(score - score.groupby(test["race"]).transform("max"))
evaluate("ランキング学習", ex / ex.groupby(test["race"]).transform("sum"))

実行結果:

基準(1/頭数)               勝ち馬のlog loss 2.473  本命の的中率 6.8%
ロジスティック回帰              勝ち馬のlog loss 2.179  本命の的中率 21.8%
ランキング学習                勝ち馬のlog loss 2.195  本命の的中率 22.0%

読み取れることは次のとおりです(架空データでの結果で、実際の競馬の精度を示すものではありません)。

  • 基準と比べて初めて意味がわかる:全馬に同じ確率を付けただけの基準(本命は事実上ランダムに選ばれる)より、学習した2つは明らかに良くなっています。
  • 的中率と確率の当てはまりは別物:ランキング学習は本命の的中率がわずかに高い一方、log lossは少し悪くなりました。ランキング学習のスコアは「並び順」を学んだもので、ソフトマックスで確率に直しても目盛りが合っているとは限らないためです。
  • 無関係な特徴量が混ざっていても動く:f2は結果と無関係ですが、学習で影響が小さくなります。ただし、実データでは「たまたま過去に効いて見えた特徴量」に注意が必要です。

評価の仕方:的中率と回収率だけで判断しない

指標 何を見るか 注意点
log loss(対数損失) 付けた確率の当てはまり。勝ち馬に高い確率を付けるほど小さい 単純な基準(1/頭数、オッズから計算した確率)と比べる
本命の的中率 確率1位の馬が勝った割合 人気馬を選ぶだけでも一定の値になるので、単独では判断できない
回収率 その予想で馬券を買った場合の払戻の割合 少ないレース数では偶然に大きく左右される。払戻率の存在で長期的には不利

評価で最も気をつけるのは、予想する時点では分からない情報(そのレースの確定オッズや結果)を学習に混ぜないこと、そしてデータをランダムではなく日付で分けることです。この2つを守らないと、検証の成績だけが良く見えます。手順の詳細は競馬予想AIの作り方で説明しています。株価予測でも同じ落とし穴があり、Pythonで株価予測を始める方法でも扱っています。

オッズを特徴量に入れるかどうか

オッズは多くの購入者の予想が集まった数字なので、勝つ確率の手がかりとしてとても強い特徴量です。一方で、オッズを入れたモデルは「市場の予想をなぞる」だけになりやすく、払戻率の分だけ不利な状態から抜け出せません。研究でも、オッズの扱いは論点の1つになっています(下の論文一覧の「オッズに応じた競馬の勝馬予測手法」など)。学習用に使うなら、予想する時点で手に入るオッズ(前日や締め切り前の時点)に限ることが前提です。

データの入手先:JRDBとJRA-VAN

「jrdb 機械学習」と検索されることが多いJRDBは、株式会社ジェイ・アール・デー・ビー(大阪市)が運営する有料の競馬データサービスです。公式サイトのデータ仕様書によると、プログラム分析用に固定長テキスト形式のデータを、lzhまたはzipの圧縮ファイルで提供しています。成績データ、競走馬データ(独自の能力指数IDMなどの指数を含む)、騎手・調教師データ、直前情報、払戻情報などがあり、データごとに更新曜日・時刻が決まっています。

項目 内容(公式サイトより)
料金 JRDB会員 月2,380円(初回のみ3,000円:登録手数料+月会費)。パドック・返し馬コメントの直前情報は別料金で月500円(通信販売法に基づく表示(2026年10月15日確認))
データ形式 固定長テキスト、lzh/zip圧縮。ファイル名は「データ識別名+版数+年月日」
利用範囲 会員規約第5条で、情報は利用者自身の競馬予想など私的利用が目的とされ、複製・改変・公開・販売などは不可。個人であってもホームページ等での公開はできないと定めている(会員規約)。第3条では法人等の利用はすべて営利目的とみなすとしている

JRA公式のデータを使うJRA-VAN データラボも選択肢です。料金・動作環境・規約の要点は競馬予想AIの作り方で公式ページをもとに整理しています。どちらも私的利用が前提なので、学習済みモデルの予想やデータを公開・販売したい場合は、必ず事前に提供元に確認してください。

「競馬 機械学習 論文」の探し方

競馬の予測は、統計学・情報学・経済学(市場の効率性)など複数の分野で研究されています。日本語の論文は国立情報学研究所のCiNii ResearchやJ-STAGEで、「競馬 機械学習」「競馬 予測」「競馬 ニューラルネットワーク」のような語で探せます。英語では「horse racing prediction」「conditional logit」などで探すと、競馬を「予測市場」として扱う研究が見つかります。

実際にCiNii Researchで検索して見つかった論文の一部です(タイトルと掲載誌の情報のみ。内容の評価はここではしていません)。

学会の全国大会の予稿は数ページの速報であることが多く、査読付き論文とは位置づけが違います。読むときは、データの期間、学習と検証の分け方、比較した基準、回収率を出しているなら何レースで計算したかを確認すると、結果をどこまで信じてよいか判断しやすくなります。

学ぶ順番

  1. Pythonの基本と、pandasで表を集計する方法
  2. 分類・回帰の基本と、学習用・検証用の分け方(機械学習とは?)
  3. 確率の評価(log loss)と、基準との比較
  4. 勾配ブースティングやランキング学習(バギングとブースティングの違い)
  5. 論文を読み、問題設定や評価方法を自分のモデルと比べる

テックジムは東京本校(永田町)などに通う対面型のプログラミングスクールで、自分のペースで進めながら、詰まったところをトレーナーに直接質問できます。Pythonの基礎から始めたい方は無料講座で試せます。

よくある質問

Q. 競馬の予想に機械学習は使えますか?

使えます。過去のレースのデータから、各馬が勝つ確率や順位を予測するモデルを作れます。ただし偶然の要素が大きく、JRAの設定払戻率は単勝で80%なので、機械学習を使っても利益が出るとは限りません。

Q. 競馬の機械学習では分類とランキング学習のどちらがよいですか?

目的によります。確率を使いたいなら、1頭ずつの分類で出した確率をレース内で合計1に割り直す方法がわかりやすいです。ランキング学習は並び順を学ぶ方法で、出力はそのままでは確率ではない点に注意が必要です。

Q. JRDBのデータは機械学習に使えますか?

JRDBはプログラム分析用の固定長テキスト形式のデータを提供しており、会員になれば自分の予想のために使えます。会員規約では私的利用が前提で、データの公開や販売はできないと定められています。

Q. 競馬と機械学習の論文はどこで探せますか?

CiNii ResearchやJ-STAGEで「競馬 機械学習」「競馬 予測」などの語で探せます。英語ではhorse racing predictionやconditional logitで探すと、競馬を予測市場として扱う研究が見つかります。

Q. モデルの良し悪しは回収率で判断すればよいですか?

回収率だけでは判断できません。少ないレース数では偶然に大きく左右されるためです。log lossなど確率の当てはまりを測る指標を使い、1/頭数やオッズから計算した確率といった単純な基準と、日付で分けた未来のレースで比べてください。

Pythonで作るの関連記事

同じテーマの記事をまとめています。あわせて読むと理解が早くなります。

機械学習の全体像は機械学習とは?仕組み・種類・できることを簡単に解説|Pythonで動く例つきにまとめています。

プログラミング学習に興味がある方は、まずは無料体験会へ

無料体験会・お申し込みはこちら