
【保存版】完成した競馬AIの使い方を徹底解説〜前処理・特徴量作成編〜【#39】
前回に引き続き、完成した競馬AIシステムの使い方に焦点を当てて、実際にソースコードを動かす方法を詳しく解説していきます。
■完成イメージ
1. 今回やること
前回のデータ収集編では、以下の8種類のデータをWeb上から収集する方法を学びました。
results.csv - レース結果テーブル(メインデータ)
race_info.csv - レース情報テーブル(天気、距離、グレード等)
return_tables.csv - 払い戻しテーブル(シミュレーション用)
horse_results.csv - 馬の過去成績テーブル
peds.csv - 血統テーブル
jockey_leading.csv - 騎手リーディングテーブル
trainer_leading.csv - 調教師リーディングテーブル
sire_leading.csv - 種牡馬リーディングテーブル
これらのデータは、common/data/rawdf/フォルダに「生データ」として自動的に保存されます。
今回はそれらの生データを機械学習で使える形に変換する工程を解説していきます。
2. 前処理・特徴量エンジニアリングとは?
機械学習を行う際に、生データをそのまま機械学習モデルに投入することはほぼありません。必ず「前処理」と「特徴量エンジニアリング」という2つの工程を経て、モデルが学習しやすい形にデータを加工する必要があります。
■前処理(preprocessing)
生データの欠損や文字列・形式を整え、機械学習が扱いやすい「きれいな表」にする工程です。例えば、「牡」「牝」という文字列を「0」「1」という数値に変換したり、不要な列を削除したりします。(機械学習モデルは基本的に数値しか入力できません。)
■特徴量エンジニアリング(feature engineering)
「特徴量」とは、機械学習モデルが予測を行う際に参考にする情報のことです。人間が競馬予想をする時に「この馬は過去に似た条件でどんな成績だったか」「この騎手は今年好調か」といったことを考えるのと同じように、AIにも様々な角度からの情報を提供する必要があります。
「特徴量エンジニアリング」とは、予測に役立つ情報を取り出して数値化・加工し、モデルに渡す特徴量を作る工程です。現在の競馬AIでは、馬の過去成績や騎手の実力など、480個近い特徴量を作成して予測精度を高めています。
今回の内容は動画でも解説していますので、併せてこちらもご覧ください。
ソースコードのダウンロード方法については、記事下部の「ソースコードのダウンロード」の章で案内しているので、記事を読んで興味を持った方は是非使ってみてください。
3. 前処理・特徴量作成の実行方法
前処理・特徴量作成処理の実行は、ダウンロードしたソースコードのkeibaAI-v3/v3_x_x/src/create_features.ipynbで行います(v3_x_xは最新バージョンをお使いください)。
上から順番にコードを実行するだけで、最終的に以下のような「全特徴量を結合したテーブル」ができる仕組みになっており、操作自体は非常にシンプルです。

どのような流れでこの処理が実行されるのか、詳しく見ていきましょう。
3.1. 全体的な流れ
今回の処理は大きく3つのステップに分かれています。
母集団作成:学習に使用する期間とレース・馬の組み合わせを決定
前処理:生データを機械学習用に整理・変換
特徴量エンジニアリング:予測材料となる特徴量を作成
■create_features.ipynbの全体的な流れ

v3_x_x/src/のcreate_features.ipynbを実行すると、v3_x_x/data/にデータが保存されていく仕組みになっています。
■出力場所
v3_x_x/
├── data/
│ ├── 00_population/ # 母集団作成結果
│ │ └── population.csv
│ ├── 01_preprocessed/ # 前処理結果
│ │ ├── results.csv
│ │ ├── race_info.csv
│ │ ├── return_tables.pickle
│ │ ├── horse_results.csv
│ │ ├── peds.csv
│ │ ├── jockey_leading.csv
│ │ ├── trainer_leading.csv
│ │ └── sire_leading.csv
│ └── 02_features/ # 特徴量作成結果
│ └── features.csv # 最終出力それでは3つの処理について、具体的に実行内容を見ていきましょう。
3.2. 学習母集団作成
ここからの内容は、動画の2:53〜でも解説しています。
まず、keibaAI-v3/v3_x_x/src/create_features.ipynbを開いて必要なライブラリをインポートします。(ここからは、v3.4.2の例で解説することにします。)
▼v3_4_2/src/create_features.ipynb
import gc
import pandas as pd
import create_population
import preprocessing
from feature_engineering import FeatureCreator
%load_ext autoreload学習母集団作成は、どの期間のデータを学習に使用するかを決めるステップです。ここで指定した期間内のレース・馬の組み合わせが、前処理・特徴量作成の対象となります。
▼v3_4_2/src/create_features.ipynb
# 学習母集団の期間指定(例:6年間のデータを使用)
population = create_population.create(
from_="2019-02-17",
to_="2025-02-16",
output_filename="population.csv",
)
この例では、2019年2月17日から2025年2月16日までの6年間のデータを学習対象としています。
create_population.create()の内部の仕組みとしては、以下のようにレース結果テーブル(results.csv)から対象horse_idを抽出し、レース情報テーブル(race_info.csv)から日付の情報を取得しています。

作成された母集団データは、自動的にv3_4_2/data/00_population/population.csvに保存されます。
💡 注意
母集団を作成する前に、指定した期間のデータがcommon/data/rawdf/に存在している必要があります。もしデータが不足している場合は、前回の記事(#38)を参考に、common/src/main.ipynbでデータ収集を行ってください。
3.3. 前処理
ここからの内容は、動画の4:08〜でも解説しています。
母集団が作成できたら、次は8種類の生データを機械学習用に前処理していきます。create_features.ipynbでは、各データに対応した前処理関数を順番に実行していきます。

この処理によって、8種類の生データがそれぞれ専用の関数で処理され、01_preprocessed/フォルダに保存されます。
▼v3_4_2/src/create_features.ipynb
# レース結果テーブルの前処理
results_preprocessed = preprocessing.process_results()
# 重複チェック
assert results_preprocessed.duplicated(subset=["race_id", "horse_id"]).sum() == 0
# 欠損チェック
results_preprocessed.isnull().sum()実行すると、前処理済みのレース結果テーブルが以下のような形でresults_preprocessedに格納され、さらにdata/01_preprocessed/results.csvにも保存されます。

処理後は、メモリ使用量を抑えるためにデータフレームを削除し、ガベージコレクション(メモリを解放する操作)を実行します。
▼v3_4_2/src/create_features.ipynb
# メモリ解放
del results_preprocessed
gc.collect()同様に、他の7つのテーブルについても前処理を実行していきます。
# 馬の過去成績テーブルの前処理
horse_results_preprocessed = preprocessing.process_horse_results()
# 重複チェック
assert horse_results_preprocessed.duplicated(subset=["horse_id", "date"]).sum() == 0
# 欠損チェック
horse_results_preprocessed.isnull().sum()
# メモリ解放
del horse_results_preprocessed
gc.collect()レース情報テーブルの前処理
race_info_preprocessed = preprocessing.process_race_info()
# 重複チェック
assert race_info_preprocessed.duplicated(subset=["race_id"]).sum() == 0
# 欠損チェック
race_info_preprocessed.isnull().sum()
# メモリ解放
del race_info_preprocessed
gc.collect()# 騎手リーディング
jockey_leading_preprocessed = preprocessing.process_jockey_leading()
assert jockey_leading_preprocessed.duplicated(subset=["jockey_id", "year"]).sum() == 0
# 調教師リーディング
trainer_leading_preprocessed = preprocessing.process_trainer_leading()
assert trainer_leading_preprocessed.duplicated(subset=["trainer_id", "year"]).sum() == 0
# 種牡馬リーディング
sire_leading_preprocessed = preprocessing.process_sire_leading()
assert sire_leading_preprocessed.duplicated(subset=["sire_id", "year", "race_type"]).sum() == 0
# 血統テーブル
peds_preprocessed = preprocessing.process_peds()
assert peds_preprocessed.duplicated(subset=["horse_id"]).sum() == 0
# 払い戻しテーブル
return_tables_preprocessed = preprocessing.process_return_tables()
これで8種類のデータの前処理が完了し、全てv3_4_2/data/01_preprocessed/フォルダに保存されます。
3.4. 特徴量エンジニアリング
ここからの内容は、動画の13:30〜でも解説しています。
前処理が完了したら、特徴量エンジニアリングに進みます。
前処理したテーブルから特徴量を作成し、最終的に1つのテーブルに結合します。

特徴量の作成は、例えば「特定条件(天気・距離など)における直近5レースの過去成績」など、複数のテーブルを組み合わせてデータを処理するため、上記の図のようにやや複雑になっていますが、使用する際は以下のコードを実行するだけで自動的に全ての処理が走るように作ってあります。
▼v3_4_2/src/create_features.ipynb
fc = FeatureCreator()
features = fc.create_features()
# 重複チェック
assert features.duplicated(subset=["race_id", "horse_id"]).sum() == 0
約28万行、559列の大規模なデータセットが完成し、最終的に機械学習モデルが直接学習できる形式のデータセットがv3_4_2/data/02_features/features.csvに保存されます。
これで、前処理・特徴量作成の全工程が完了しました。
4. ソースコードのダウンロード
ソースコードは以下の記事に最新版が添付されているので、そこからダウンロードすることができます。
上記記事には、今回の記事で紹介したソースコード含め、データ収集・機械学習モデル構築・サーバー上での自動投票など、本格的な競馬AIの運用に必要な全工程のコードが含まれています。
また、ソースコードを1から作成する方法を以下のマガジンで詳しく解説しているので、興味がある方はぜひ覗いてみてください。
5. 実際に採用した特徴量の詳細解説
以上が前処理・特徴量作成の実行方法となりますが、ここからは、各特徴量について筆者が過去に試行錯誤してきた内容を踏まえ、「どんな考え方で作成したか」「どう実装したか」などをまとめておきます。