【競馬AI開発#13】特徴量追加で押さえるべき4つのパターンとは?
この【競馬AI開発】シリーズでは、競馬予想AIを作ることを通して、機械学習・データサイエンスの勉強になるコンテンツの発信や、筆者が行った実験の共有などを行っていきます。
■今回やること
今回からは、新しい特徴量(=機械学習モデルにインプットする予測材料)を作成し、競馬予想AIの精度向上を目指していきます。
現バージョンの競馬AI(v3.0.1)では、netkeiba.comから取得した5年分のデータから以下の特徴量を作成し、着順を予測しています。
horse_id ・・・ 馬ID
jockey_id ・・・ 騎手ID
trainer_id ・・・ 調教師ID
umaban ・・・ 馬番
wakuban ・・・ 枠番
tansho_odds ・・・ 単勝オッズ
popularity ・・・ 人気
impost ・・・ 斤量
sex ・・・ 性別
age ・・・ 年齢
weight ・・・ 体重
weight_diff ・・・ 体重差
race_type ・・・ レース種別(芝・ダート)
around ・・・ 周り(右回り・左回り)
course_len ・・・ コース長
weather ・・・ 天候
ground_state ・・・ 馬場状態
race_class ・・・ レース階級
place ・・・ 開催場所
rank_3races ・・・ その馬の過去3レースの平均順位
prize_3races ・・・ その馬の過去3レースの平均賞金
rank_5races ・・・ その馬の過去5レースの平均順位
prize_5races ・・・ その馬の過去5レースの平均賞金
rank_10races ・・・ その馬の過去10レースの平均順位
prize_10races ・・・ その馬の過去10レースの平均賞金
rank_1000races ・・・ その馬の過去1000レースの平均順位
prize_1000races ・・・ その馬の過去1000レースの平均賞金
今回はここに80個の特徴量を追加し、学習材料を増やすことで競馬AIの精度を上げていきます。
特徴量の作成(「特徴量エンジニアリング」といいます)は、競馬AIに限らず、機械学習の精度を最も左右すると言っても過言ではないくらい重要な工程となります。
この特徴量エンジニアリングですが、どのような方針で追加していくのが良いでしょうか?
機械学習における特徴量追加は、大きく分けて以下の4つのパターンがあります。
■パターン1: 機械学習で一般的に使われる手法を試す
まず、機械学習には、どんな問題に対してもよく適応される汎用的な手法が存在します。
例えば基本的な特徴量エンジニアリングとして、ざっと思い付くものを挙げただけでも以下のような手法があります。
スケーリング(Scaling)
標準化(Standardization):平均を0、標準偏差を1にする
正規化(Normalization):特定の範囲にデータを収める(例:0から1)
エンコーディング(Encoding)
ワンホットエンコーディング(One-Hot Encoding):カテゴリ変数0or1のベクトルに変換
ラベルエンコーディング(Label Encoding):カテゴリ変数を整数に変換
ターゲットエンコーディング(Target Encoding):カテゴリ変数をそのカテゴリごとのターゲット変数の平均値や中央値でエンコード
バケット化(Binning/Bucketing)
等幅ビニング:データを等しい幅でビンに分割
等頻度ビニング:データを等しい頻度でビンに分割
欠損値処理(一般的にLightGBMでは不要)
交互作用:複数の特徴量の積を計算
次元削減
主成分分析(PCA):主成分に基づいて次元を削減
テキスト特徴量の処理
TF-IDF:単語の出現頻度と逆文書頻度を使って特徴量化
非線形変換
対数変換:データの対数をとる
平方根変換:データの平方根をとる
これらの手法は、ドメイン知識(今回であれば、競馬の知識)が一切必要なく、機械的に特徴量を作成して精度を上げることができることが大きな利点となります。
ドメイン知識の無い分野で機械学習モデルを作成する時などに、特徴量追加の第一歩として試してみるのが良いでしょう。
特徴量エンジニアリングについては、以下二つの書籍がとても有名なので、気になる方は読んでみることをお勧めします。
■パターン2:新しいデータを取得して追加する
競馬予想AIだと、例えば血統データの追加や騎手の過去成績の追加などです。
利点としては、全く新しい特徴が加わる分、精度向上に貢献する可能性が高いことです。
ただし、管理するデータが増える分、運用が大変になるというデメリットもあるので、その運用コストとのトレードオフで判断するのが良いでしょう。
■パターン3:ドメイン知識を使う
パターン1とは反対に、競馬の知識を使った特徴量設計ももちろん有力です。
例えば、「芝とダートどちらが得意な馬か考慮する」「逃げ馬か差し馬かを考慮する」などは機械学習の技術的な発想からは出てこない、競馬ドメイン特有の特徴量設計です。
この方法の利点としては、その分野の特性をうまく捉えた本質的な特徴量設計ができるため、うまく行くと精度が大きく向上することです。
その反面、特徴量作成ロジックが複雑になりがちなことと、経験的な知識がデータの上では実態と反していて試してみたら効かない場合が多々あることがデメリットとして挙げられます。
■パターン4:分析によって意味のありそうな特徴量を発想する
データの分布や目的変数との相関などを分析することで、特徴量作成のための示唆を得る方法です。
これは機械学習の用語で「探索的データ分析(EDA)」呼ばれ、ブラックボックスになりがちな機械学習において「データの解釈」をすることができるという意味でも、有力な手法になります。
また、定量分析の他にも、予測スコアと実績を見比べながら一つずつデータをチェックしていく「定性分析(N1分析)」も何かしらの発見に繋がる場合が多いです。
以上のパターンを認識した上で、本シリーズでは以下のような順で一通り実装することを目指します。
パターン1の中で簡単に、機械的に実装できるものを作ってしまう
パターン2の中で、管理コストのかからないものを追加する(今のところ、血統データが有力かなと思っています)
パターン3の中で、ロジックが複雑でないものを実装
パターン4の分析に基づき、ロジックが複雑でないものを実装
今回の記事では、まずパターン1の特徴量を実装していきます。
使用するソースコードは、下に進んでダウンロード・解凍すればそのまま使うことができるので、適宜手元で参照・実行しながらお読みください。
定期購読をすると、今月更新される記事に加えて#1〜#4の記事が980円で全て読めるので、単品購入より圧倒的にお得です。
ソースコードのダウンロード
■ファイルに含まれるソースコード一覧
main.ipynbを実行することで、データ取得〜予測まで全て行うことができます。
.
├── requirements.txt ・・・必要なライブラリを記載
├── README.md ・・・ディレクトリ構成を記載(本ファイル)
├── common
│ ├── data
│ │ ├── html
│ │ │ ├── race
│ │ │ │ └── {race_id}.bin ・・・スクレイピングしたraceページのhtml
│ │ │ └── horse
│ │ │ └── {horse_id}.bin ・・・スクレイピングしたhorseページのhtml
│ │ ├── rawdf
│ │ │ ├── results.csv
│ │ │ ├── horse_results.csv
│ │ │ ├── horse_results_prediction.csv
│ │ │ ├── return_tables.csv
│ │ │ └── race_info.csv
│ │ ├── mapping ・・・カテゴリ変数から整数へのマッピング
│ │ │ ├── around.json
│ │ │ ├── ground_state.json
│ │ │ ├── race_class.json
│ │ │ ├── race_type.json
│ │ │ ├── sex.json
│ │ │ └── weather.json
│ │ └── prediction_population ・・・予測母集団(開催日, race_id, horse_id)
│ │ └── population.csv
│ └── src
│ ├── create_rawdf.py ・・・htmlをDataFrameに変換する関数を定義
│ ├── main.ipynb ・・・コードを実行するnotebook
│ ├── dev.ipynb ・・・開発用notebook
│ ├── create_prediction_population.py ・・・予測母集団を作成する関数を定義
│ └── scraping.py ・・・スクレイピングする関数を定義
├── v3_0_0
│ ├── data
│ └── src
├── v3_0_1
│ ├── data
│ └── src
├── v3_exp2
│ ├── data
│ │ ├── 00_population ・・・学習母集団を保存するディレクトリ
│ │ │ └── population.csv
│ │ ├── 01_preprocessed ・・・前処理済みのデータを保存するディレクトリ
│ │ │ ├── horse_results.csv
│ │ │ ├── horse_results_prediction.csv
│ │ │ ├── return_tables.pickle
│ │ │ ├── results.csv
│ │ │ └── race_info.csv
│ │ ├── 02_features ・・・全てのテーブルを集計・結合した特徴量を保存するディレクトリ
│ │ │ └── features.csv
│ │ ├── 03_train ・・・学習結果を保存するディレクトリ
│ │ │ ├── model.pkl ・・・学習済みモデル
│ │ │ ├── evaluation.csv ・・・検証データに対する予測結果
│ │ │ ├── importance.csv ・・・特徴量重要度(一覧)
│ │ │ └── importance.png ・・・特徴量重要度(上位を可視化)
│ │ └── 04_evaluation ・・・検証データに対する精度評価結果を保存するディレクトリ
│ └── src
│ ├── dev.ipynb ・・・開発用notebook
│ ├── main.ipynb ・・・コードを実行するnotebook
│ ├── create_population.py ・・・学習母集団を作成する関数を定義
│ ├── preprocessing.py ・・・/common/rawdf/のデータを前処理する関数を定義
│ ├── feature_engineering.py ・・・機械学習モデルにインプットする特徴量を作成するクラスを定義
│ ├── config.yaml ・・・学習に用いる特徴量一覧
│ ├── train.py ・・・学習処理を行うクラスを定義
│ ├── evaluation.py ・・・モデルの精度評価を行うクラスを定義
│ └── prediction.py ・・・予測処理を行う関数を定義
└── v3_0_2 ・・・実験結果を反映した新しいバージョン
├── data
└── src
この記事が気に入ったらサポートをしてみませんか?