[儲からない競馬予想AI] 閑話1 : 血統情報に意味があるのか

Chapter2では特徴量に血統情報として、馬のラベルを入れました。
これについて、少し話をしたいと思います。

まず話したいことは
「競馬において血統情報は重要である」
ということです。
これは、事実です。

現に、競馬はサラブレット競技であり、良い馬と良い馬をかけ合わせて新世代の馬を生み出してきました。もしも、競馬において血統情報が意味を持たないのであれば、牧場や生産者は数百、数千万円をかけて良い馬をかけ合わせる必要はないでしょう。

しかし、今回の話は少し異なります。
今回重要なのは、「私達が得られる血統情報は重要か?」ということです。

例えば、適当な馬の血統情報を見てみます

プロフェット (Prophet)の血統表 | 競走馬データ - netkeiba
プロフェット (Prophet)の血統表の競走馬データです。競走成績、血統情報、産駒情報などをはじめ、50万頭以上の競走馬、騎手・調教師・馬主・生産者の全データがご覧いただけます。

これはどの様な馬でも良いです。

さらに、父親や母親、祖父母といった馬の名前を選択すれば、その馬のデータが見れます。
ここでは、母親のデータを見てみます

ジュモー (Jumeau) | 競走馬データ - netkeiba
ジュモー (Jumeau)の競走馬データです。競走成績、血統情報、産駒情報などをはじめ、50万頭以上の競走馬、騎手・調教師・馬主・生産者の全データがご覧いただけます。

確かに、母親の戦績が表示されます。問題はこの中の何を特徴量として持ってくるか、ということです。実際には、戦績表や獲得賞金等のデータがほしいのだけども、これが残っていない馬も多数存在します。

一つ前の血統表に戻り、適当な祖父のデータを見てみます

Dansili | 競走馬データ - netkeiba
Dansiliの競走馬データです。競走成績、血統情報、産駒情報などをはじめ、50万頭以上の競走馬、騎手・調教師・馬主・生産者の全データがご覧いただけます。

二世代前に遡るだけで、戦績や賞金等のデータは見れなくなりました。つまり、詳細なデータが残っていないのです。

適正レビューデータは使えるか

そして、唯一どの馬にも表示されるデータは、「適性レビュー」と書かれた、謎のデータだけです。
これは、どれほど正確なデータなのでしょう。何しろ、戦績が残っていないので、手元で検証できません。

では、このデータが使い物になるかというと、まったく使い物になりません。
当たり前ですが、数世代前のレビューと現役世代のレビューで、同じ適正値であったとしても、現役世代のほうが強いことが多そうです(競技として進化しているのであれば)。
ましてや、それぞれの馬ごとに、出場しているレースのグレードや回数が違うのだから、同じ長さの物差しで、適正値を測るのはかなり難しいです。

実際に、この適正レビューデータを学習に入れても、1ミリも精度は向上しません。これは裏で検証しました。
個人的な推測になってしまいますが、おそらく、このレビューは人間が肌感で書いているデータだと思われます。

実際、新馬や未勝利馬などは、この適正値は全く変動しない固定値です。

血統データだけを入れて学習してみる

つまり、どの様な血統データが扱えるかと言うと、血統表に書かれた名前だけです。
Chapter2では、この名前をラベル化して学習に入れてきました。

そうしたときに、次に疑問になるのはこのラベルデータが役に立つかです。

では、それを検証したいと思います。

学習データの切り分けをする際に、血統データとレース情報のみを入れてみます
予測対象は、三位以内になるかのBinary Classification

df = df.sort_values(['race_date']).reset_index(drop=True)

not_use_columns = [
    'race_date', 'race_id', 'race_grade', 'name', 'jocky_name', 'odds', 'popular', 'rank', 'time', 'prize', 'tansyo_hit', 'tansyo_payout', 'hukusyo_hit', 'hukusyo_payout'
]

not_use_df = df[not_use_columns]
target_df = df.drop(columns=not_use_columns)

train_df = target_df.loc[not_use_df['race_date'] < '2020-01-01', :].reset_index(drop=True)
test_df = target_df.loc[not_use_df['race_date'] >= '2020-01-01', :].reset_index(drop=True)


from sklearn.preprocessing import StandardScaler

ped_columns = [col for col in target_df.columns if 'ped' in col]
X_columns = ['place_id', 'race_distance', 'race_type', 'race_total_prize', 'weather', 'race_condition', 'horse_count', 'waku', 'horse_number', 'sex', 'age'] + ped_columns
categoriy_columns = ['place_id', 'race_type', 'weather', 'race_condition', 'horse_count', 'waku', 'horse_number', 'sex', 'age'] + ped_columns

train_X = train_df.loc[:, X_columns]
test_X = test_df.loc[:, X_columns]

scaler = StandardScaler()
train_X =  pd.DataFrame(data=scaler.fit_transform(train_X), columns=train_X.columns)
test_X =  pd.DataFrame(data=scaler.transform(test_X), columns=test_X.columns)

for col in categoriy_columns:
    train_X.loc[:, col] = train_df.loc[:, col].astype('int64')
    test_X.loc[:, col] = test_df.loc[:, col].astype('int64')

train_y = not_use_df.loc[not_use_df['race_date'] < '2020-01-01', 'rank'].reset_index(drop=True)
test_y = not_use_df.loc[not_use_df['race_date'] >= '2020-01-01', 'rank'].reset_index(drop=True)
train_y = (train_y <= 3).astype('int64')
test_y = (test_y <= 3).astype('int64')

学習結果は次のようになりました。

train base acc : 0.7875074420784617
train acc      : 0.8480295909707004
valid base acc : 0.7875375375375375
valid acc      : 0.7906469461587572
test base acc  : 0.7829890414949492
test  acc      : 0.781018395551481

テストデータの精度は、ベースラインよりも下である。つまり、この血統情報はあまり役に立つデータでは無いということです。

一応考察を書いておきます。

御存知の通り、競馬は血統競技です。つまり、ほぼすべての馬の血統が良いのです。ということは、血統は意味を持つけども、「他の馬と比較しての良し悪しには使えない可能性が高い」ということが推測されます。もちろん、遺伝子レベルでのデータがあれば役に立つ可能性は残っていますが、血統の名前データだけだと、他の馬の同じような感じなので、役に立たないのではないでしょうか。

ただ、それでもChapter2ではこの血統情報を学習に入れ続けてきました。
その理由は、血統情報だけでは意味を持たないが、他のデータと合わせて意味を持つかもしれないからです。

タイトルとURLをコピーしました