競艇AIが展示前に5・6号艇を本命にした話|本命の的中率は12.9%に低下

このサイトの裏側 公開

このサイトで使っている予想AIは、作っている途中でおかしな本命を出していた。実際の1着率が6%ほどの5号艇と、3%ほどの6号艇を、本命に選ぶことが多かったのである。

きっかけは学習データの作り方の1行の誤りだった。結果が記録されていないレースを、「6艇全員が負けたレース」として覚えさせていた。ただ、その1行を直しただけでは終わらなかった(後半で書く)。

今のデータベースで、その誤りをわざと戻して作り直した。結論から言うと、誤りを戻したAIは、展示タイムが無い状態で予想させると本命の51.3%が5号艇、27.5%が6号艇になった。本命の的中率は12.9%まで落ちた。 展示タイムがある状態では、誤りの有り無しで成績はほとんど変わらない。だから全体の指標では気づけなかった。そして、誤りを直しても、回収率は100%に届かない。


結論: 同じAIでも、入力が欠けると的中率は56.6%から12.9%に落ちた

再現の条件は次のとおり。

  • 学習: 2024-01-01〜2025-12-31 のレース
  • 答え合わせ: 2026-01-01〜2026-10-26 の 46,213レース(検証期間)
  • モデル: 今の予想AIと同じ特徴量・同じ設定の LightGBM(決定木をたくさん重ねて確率を出す機械学習の手法)
  • 「展示なし」: 展示タイム・展示タイム順位・風速・波高の4列を空にして予想させた。締切前や前日、まだ展示が終わっていない時点の状態をまねている
作り方 推論時の入力 本命が1号艇 本命が5号艇 本命が6号艇 本命の的中率 単勝 本命1点の回収率
修正前(結果なし=全艇負け) 展示なし 12.2% 51.3% 27.5% 12.9% 67.5%
修正後(結果なしは除く) 展示なし 77.7% 2.4% 3.0% 52.9% 88.2%
修正前 展示あり 92.1% 0.3% 0.1% 56.6% 90.8%
修正後 展示あり 92.0% 0.3% 0.1% 56.6% 90.9%
直前4列を外して学習(今の前日予想の作り方) ― 93.9% 0.2% 0.1% 56.1% 90.2%
1号艇を機械的に買う ― ― ― ― 54.9% 89.8%

修正前のモデルは、展示が無いと5・6号艇を本命にした

2026-01-01〜2026-10-26の検証期間の46,213レース。赤が「修正前のモデルが展示なしで本命に選んだ割合」、灰色が実際の1着率。5号艇の1着率は5.9%なのに本命の51.3%、6号艇の1着率は3.0%なのに本命の27.5%だった。

数で一番多かったのは5号艇(23,708レース)で、6号艇(12,710レース)が2番目だった。どちらも実際にはめったに勝たない枠である。


何が起きていたか: 結果の無いレースが1,970あった

2024-01-01〜2026-10-26 の158,724レースを数えると、出走表はあるのに結果が1行も無いレースが1,970あった(1.24%)。艇の行で数えると 951,841行のうち 11,796行(1.24%)である。

公式の成績ファイルを日ごとに開いて確かめると、内訳はこうだった。

  • 1,958レース: 成績ファイルに「中止」と書かれている。1日12レースが丸ごと中止になった会場・日が106あった
  • 12レース: 2026-05-16の尼崎。成績ファイルの尼崎の欄が空のままで、中止とも結果とも書かれていない

中止の理由はデータに無いので、ここでは数えていない。

なお、このプロジェクトの検証記録(docs/03_findings.md §5-1)と build.py のコメントは、これらを「成績ファイルが丸ごと欠落しているレース」と書いている。上の内訳は、今回成績ファイルを開いて「中止」の記載を数えた結果である。2つの書き方は食い違っているが、当時どのファイルを見てそう書いたのかは記録に無い。どちらが正しいかは、ここでは決めていない。

1行の誤り: 「データが無い」が「負け」になった

予想AIの学習では、1艇ごとに「1着だったか(1か0か)」を答えとして持たせる。修正前のコードは、着順を数字にしてから「1と等しいか」を見ていた。

結果の無いレースは、着順が全艇とも空(NaN、数値が無いことを表す値)になる。Python や pandas では、空の値を「1と等しいか」と比べると、答えは「いいえ」になる。だから結果の無いレースは、6艇とも「1着ではなかった」=0として学習に入った。

1レースには必ず1着がいる。全員が負けるレースは、現実には起きない。

学習期間(2024-01-01〜2025-12-31)には、結果が1行も無いレースが1,432あった。ほかに、全艇がフライングや失格で着順が付かなかったレースが4あった(202401040511 / 202503022303 / 202505010206 / 202512220905)。こちらは結果の行はあるが、着順の数字が無いので、同じく全艇0として学習に入っていた。合わせて1,436レース(1,432+4)を、「全員が負けたレース」としてAIに見せていたことになる。

今のコード(src/features/build.py の129〜139行)は、レースの中に着順が1つも無ければ、そのレースの答えを「不明」にして学習から外している。

has_result = df.groupby("race_id")["finish_rank"].transform(lambda s: s.notna().any())
df["is_win"] = pd.array([pd.NA] * len(df), dtype="Int64")
df.loc[has_result, "is_win"] = (df.loc[has_result, "finish_rank"] == 1).astype("Int64")

なぜ5・6号艇になるのか: 「展示が無い艇は勝たない」と覚えた

結果の無いレースは、着順だけでなく展示タイムも空になる。展示タイムは成績ファイルに載る値なので、成績が無ければ展示タイムも無い。成績ファイルから取る風速と波高も無い。

そのため修正前の学習データでは、展示タイムが空の行が9,890行あり、その1着率は0.0%だった。うち8,439行が、結果の無いレースから来ていた。AIから見ると、「展示タイムが無い艇は、1度も勝っていない」という強い規則がデータの中にあった。

そこへ、まだ展示が終わっていないレースを入れる。6艇とも展示タイムが空である。AIは6艇すべてを「勝たない側」に振り分けた。

  • 修正前のモデルが展示なしで出した1着確率は、6艇の合計で平均0.06%だった(本来は合計100%前後になる)
  • 展示ありなら、同じモデルの合計は平均99.1%である

全員がほぼ0%なら、その中の順番はほとんど意味を持たない。その「意味の無い順番」の1位が、5号艇や6号艇に偏った。なぜ5・6号艇に偏ったのか、その中身までは確かめていない。 測れたのは、展示なしでは1号艇の平均確率(0.009%)が5号艇(0.013%)や6号艇(0.012%)より低くなっていたことである。


誤りを直しても、展示なしで予想すると崩れる

ここで話は終わらなかった。誤りを直したモデルでも、展示なしで予想させると、本命の6号艇は、展示ありの0.14%から展示なしの2.97%へ約21倍に増えた。本命の的中率は52.9%で、1号艇を機械的に買う54.9%を下回った。

理由は同じ形をしている。直した後の学習データにも、展示タイムが空の行が1,451行残っていた。これは欠場や出遅れの艇で、やはり1着率0.0%である。数は7分の1に減ったが、「展示が無い艇は勝たない」という規則は残っていた。合計確率は平均1.17%で、まだ100%から遠い。

このサイトの記録(src/models/train_preday.py の冒頭、2026-08-30測定)にも同じ症状が残っている。当時は「本命が6号艇」が0.2%から3.1%へ15倍、本命の的中率は53.1%で、1号艇を機械的に買う54.3%を下回った。今回の再現(21倍)とは期間もレース数も違うが、向きは同じだった。

対策は、展示タイム・展示タイム順位・風速・波高の4列を最初から使わずに学習することだった。今の前日予想はこの作り方である。上の表のとおり、本命が1号艇の割合は93.9%、的中率は56.1%に戻る。

この作り方だと、修正前の誤り(結果なし=全艇負け)を残しても成績はほぼ同じだった(的中率56.2%・回収率90.2%)。今回の測り方の範囲では、誤りの害が出たのは「展示が空」の入力と重なったときだった。


展示がそろっていると、誤りは見えなかった

いちばん厄介なのはここである。展示ありで予想させると、修正前と修正後の成績はほぼ同じだった。

展示ありで予想 本命が1号艇 本命の的中率 単勝 本命1点の回収率
修正前 92.1% 56.6% 90.8%
修正後 92.0% 56.6% 90.9%

本命の1着的中率:大きく崩れたのは修正前・展示なし

2026-01-01〜2026-10-26の検証期間の46,213レース。展示ありの2本は修正前後で同じ56.6%。大きく崩れたのは「修正前・展示なし」(12.9%)。「修正後・展示なし」も52.9%で、1号艇を機械的に買う54.9%を下回った。

過去のレースで成績を測ると、展示タイムは欠場の艇を除いてそろっている。だから過去データでの検証は合格する。崩れたのは、展示の4列を空にして予想させたとき(締切前や前日をまねたもの)だった。検証の数字が良くても、本番の入力が検証のときと同じ形とは限らない。


AI予想の回収率は、直しても100%に届かない

誤りを直すと、成績は「まともな水準」に戻る。ただ、その水準は100%の下である。

単勝 本命1点の回収率:どの作り方も100%未満

2026-01-01〜2026-10-26の検証期間の46,213レース。単勝の本命1点を100円ずつ買った回収率(払い戻し÷購入額)。いちばん良い「修正後・展示あり」でも90.9%。

  • 修正後・展示あり: 90.9%
  • 直前4列を外して学習: 90.2%
  • 1号艇を機械的に買う: 89.8%

モデルと1号艇を買うだけの差は、0.4〜1.0ポイントしかない。この記事が直したのは「明らかにおかしい本命」であって、儲かる予想ではない。


当時の記録との関係

当時の記録(docs/03_findings.md §5-1 と、build.py の「2026-08-28実測」というコメント)は「1,811レース・10,866行・学習データの1.35%」としている。今のデータベースで数え直すと、2024-01-01〜2026-10-26 で1,970レース・11,796行・1.24%である。

  • レース数が増えたのは、主に期間が延びたためである。2026-08-28以前に限ると、今は1,898レース
  • 割合が下がったのは、分母が増えたためと考えられる。2026-08-31に出走表の読み込み漏れを直し、9,264レースが戻った(docs/03_findings.md §5-6)。直す前に取っておいた退避コピーで2026-08-28以前を数えると、割合は1.35%で当時と同じになる
  • ただし、その退避コピーでもレース数は1,894で、当時の1,811とは合わない。1,811という数そのものは再現できなかった

当時の記録には「6号艇(実際の1着率3.1%)が本命27.7%で選ばれていた」ともある。今回の再現は27.5%で近いが、当時の測り方(どのレースを対象にしたか)は記録に残っていない。同じ測り方での一致とは言えない。


測り方の詳細数え方と対象の決め方。読まなくても本文の結論は変わりません
  • データ: 自前のデータベース(公式の番組表・競走成績を取り込んだもの)。集計の終端は2026-10-26。
  • 結果の無いレース: 出走表(race_entry)にあって、着順の表(race_result)に1行も無いレース。中止かどうかは、公式の成績ファイルの「中止」の記載で日ごとに照合した。
  • モデル: 今の予想AIと同じ特徴量(枠・級別・勝率・モーター・展示タイム・直近の成績など)と同じ設定の LightGBM。乱数の種も同じ。4通り(修正前/修正後 × 直前4列あり/なし)を同じ学習期間で作った。
  • 本命: 1レースの中でモデルの1着確率がいちばん高い艇。同じ値なら内側の枠。
  • 展示なし: 展示タイム・展示タイム順位・風速・波高を空にして予想させた。
  • 回収率: 単勝の払い戻し÷購入額。1レース100円。単勝の払い戻しが記録されていないレースは、外れとして数えた。
  • 確かめていないこと: 修正前のモデルが5・6号艇に偏った理由。中止の理由。当時の1,811レースの数え方。結果の無いレースが「中止」か「成績ファイルの欠落」か、検証記録との書き方の違い。
  • 集計のスクリプトと結果は計算ログ(articles/_work/b3_calc.md)に残した。

まとめ

  • 結果の無いレースが1,970あった: 2024-01-01〜2026-10-26 の158,724レースのうち1.24%。成績ファイルでは、ほぼすべてに「中止」の記載があった(検証記録は「成績ファイルの欠落」と書いており、どちらが正しいかは決めていない)。
  • それを「全艇負け」と覚えると: 展示なしで予想させたとき、本命の51.3%が5号艇、27.5%が6号艇になった。的中率は12.9%。
  • 原因の形: 「展示タイムが空の艇は勝たない」という規則を学んでいた。展示前のレースでは6艇とも空なので、全員がほぼ0%になった。
  • 直しただけでは足りない: 欠場の艇が同じ規則を残す。展示なしだと本命の6号艇は約21倍。展示の4列を使わずに学習して、ようやく戻った。
  • 過去データの検証では見えない: 展示ありなら、修正前後とも的中率56.6%で同じだった。
  • 直しても100%未満: 単勝 本命1点の回収率は90.2〜90.9%。1号艇を買うだけで89.8%。

AIの予想がおかしいとき、疑うべきはモデルより先に、学習データの答えの作り方と、本番で入る入力の形だった。


免責

  • 本記事に記載した数値はすべて過去データに対する実測であり、将来の的中や利益を保証するものではありません。
  • 本記事および言及したツールは分析の補助を目的としており、舟券の購入を勧誘するものではありません。
  • 舟券は20歳未満の方は購入できません。
  • 舟券の購入は自己責任で、生活に必要な資金とは別の、余裕資金の範囲で行ってください。のめり込みには十分ご注意ください。

関連記事

同じ分類(このサイトの裏側)の記事と、公開日の前後の記事です。

分析記事の一覧へ