競馬AI開発についての第5回です。
今回は収集したデータの保持方法と注意点について記述します。
目次
データベースの選択
前回、データの収集について記述しましたが、収集したデータはソフトからの出力であればテキスト形式(TXTやCSV)などが一般的です。
収集したデータをエクセルでインポートしてデータ分析などすることもできますが、データ量が多い場合や複雑な解析・加工などをする場合はやはりデータベースとしてデータを格納しておくことが望ましいです。
ただし、私の場合そこまでデータベースにはこだわりがなく、第2回で述べているようにデータベース(DB)の中では比較的軽量で扱いやすいSQLiteを用いています。
複数システムからの同時接続やサーバー経由のアクセスなどは想定していないのでDB自体の機能は最低限で良いと考えているからです。
ただし、最低限のDBの知識と扱い(主キーや型、SQL構文)などは知っておいた方が良いですね。
あくまでSQLiteを使用した自分の例ですが具体的には
- 各テーブルの主キーは何なのか?(または複合キーとなるか?)
- 各フィールドの型やデフォルト値は何か?NULLの扱いをどうするか?
- 簡単なSQLクエリが書ける
→よく使うのは、SELECT・UPDATE・WHERE・ORDER BY・INSERT・COUNT・INNER JOINあたりで複雑なサブクエリは使いません。
となります。
従ってデータベースに関する本を丸々読んできちんと理解する必要はないと思います。
そもそも仕事でがっつりシステム開発業務に携わっている人には説明不要でしょう笑
競馬AI開発を機に新しくDBについて学ぶ人がいたとしても個人で使用する分では、必要な知識は最低限ですし、特にデータベースなどは自分で実際に作って使ってみないとイメージが付きづらいので調べつつ手を動かしてみたほうが頭に定着しやすいですね。
実際私も、データベースに関しては簡単な以下のような簡易的な設計書しか作成していません。
データベースの知識やテクニックについては別記事で紹介できればなーと思っています。
データベースの構築
実際AISSで構築しているDBに格納するデータは大きく分けて以下の3つに分かれています。
- レース関連データ
- 集計値関連データ
- その他データ
それぞれ少し詳しく掘り下げます。
1. レース関連データ
下記表のようなデータが格納されています。計250フィールド程度です。
特に各出走馬に関するデータは、出走レースの全出走馬ごとに1フィールドずつ追加されていくので非常に大量のデータとなります。
AISSでは2008年からのデータを保持しているのですが、2021年2月時点で、中央競馬は約65万レコード、地方競馬は約170万レコードが格納されています。
地方競馬は毎日開催されているのでこちらのデータは毎日DBに追加登録されています。
概要 | フィールド例 |
払い戻し情報 |
レースごとの券種・馬番組み合わせ・払戻金・人気 など |
レース共通情報 |
レース開催場所・条件・天候・馬場状態・頭数・賞金・走破時計平均・ラップタイム・気象状況 など |
レース各出走馬個別情報 |
枠番・馬番・馬齢・性別・騎手・調教・着順・着差・人気・レース位置取り・オッズ・人気 など |
2. 集計値関連データ
下記表のようなデータが格納されています。計50フィールド程度です。
集計は1のデータを元に、毎日ではなく、毎週1回・過去n走を対象(n=100がデフォルト)に行うようにしています。
概要 | フィールド例 |
成績集計情報 (競走馬、騎手、調教師、馬主、父、母父について) |
出走回数、勝率、複勝率、掲示板率、平均着順/オッズ/上り順位、単勝回収率、逃げ・先行割合 など |
成績集計情報 (トラックについて) |
トラック条件、上位馬平均着差/最終コーナー位置/走破時計、ラップタイム平均 など |
3. その他データ
下記表のようなデータが格納されています。計50フィールド程度です。
これまでのデータと異なり、更新頻度は低めです。新規に騎手や競走馬が登録された場合や、トラックが追加された場合などに更新します。
概要 | フィールド例 |
トラック情報 |
トラック別の特徴(坂の有無、高速馬場コースか、小回りか、・・・) など |
血統や騎手所属情報など |
対象競走馬と父馬・母父馬との対応情報、 騎手所属(美浦や栗東など)情報 など |
データベース構築に関する注意点
データベースの構築に関して、技術的な観点からはもっとメモリ効率化や最適な設計が考えられますが、ここでは主に競馬データをDBに登録する上で注意が必要な点をいくつか列挙します。
とはいってもだいたいは自分がこれまでデータベースを作っていく中で失敗した内容ですが、それゆえ実用的な情報なのでは?と思います (^^;)
もしかしたら競馬AI開発者にとっては「あるある!」と納得できる点もあるかもしれませんね。
カテゴリ変数について
予測に使用するデータは一般的な統計処理上、主にカテゴリ(質的)変数と量的変数に分かれます。
※変数の尺度については下記サイトを参考にしてください。
データ種類 | 例 |
カテゴリ(質的)変数 |
性別、開催場所、レースクラス、馬場状態、馬番、着順、レース位置取り、人気 など |
量的変数 |
払戻金、距離、走破時計、ラップタイム、気温、オッズ、馬齢 など |
おおざっぱに上記表になるのですが、ほとんどがそのままの数値として扱える量的変数と異なり、カテゴリ変数の加工には注意が必要です。
馬番や着順などは、その数字自体に順序があるので、あまり考えずにそのままデータとして使えます。
また、性別や開催場所などは明らかに順序がない、すなわち東京=1、中山=2などとしても順序関係が成り立たないことが分かります。
しかし、レースクラスや馬場状態などはどうでしょうか?
見方によっては順序関係が考えられます。
例えば、レースクラスは、「上の順位ほどレースレベルが高い」という基準であれば、新馬・未勝利=1、1勝クラス=2、2勝クラス=3、・・・などと置き換えることもできます。
ただ、一口に「オープンレベル」といっても2歳戦と古馬ではレースレベルの基準が異なるのではないでしょうか。
この場合は、レースクラスはそれぞれ別フィールドに値を持ち、レース条件(馬齢限定戦か古馬戦か)と組み合わせてモデルを作るようにする、
などといった対応も考えられます。
以上のように、カテゴリ変数についてはどう値を加工するか考える必要があります。
(ちなみに、AISSではレースクラスは順序変数として1フィールドに入れています。)
注意
上記の説明はデータ加工の一例であり厳密なものではありません。またAISSではモデル構築手法としてディープラーニングを採用しているため上記の検討をしていますが、他の手法(GBDTなど)ではそもそもカテゴリ変数をそのまま投入できる場合もあります。
欠損値の扱いと出走状況について
競馬のデータにおいて特定のフィールドには欠損値がしばしば見られます。
その中でも特に個人的に重要視しており、予測精度にも直結してしまうのは「着順」です。
正常にレースに出走し、何事もなくゴールできたなら最小1、最大18の整数値で与えられるはずです。
しかし、実際には下記の出走状況が存在します(現行ルールで禁止されている「再騎乗」は除く)。
出走状況 | 判明するタイミング |
正常 | レース後 |
取消 | レース前 |
除外 | レース前(直前!) |
中止 | レース後 |
失格 | レース後 |
降着 | レース後 |
「正常」(・「降着」)以外の出走状況では、当然着順が判明しないのでデータベースへ値をどう入れるか検討する必要があります。
もちろん、「正常」(・「降着」)以外の場合はとりあえず「着順」を0や-1など、正常の着順と区別できるようにすべきですが、重要なのは「"出走状況"の情報を持っているか?」です。
データの入力種類やモデル構築の仕方にもよるのですが、AISSの場合は「1頭の予測をするのに他の出走馬の特徴量」も使用しています。
特徴量として、「レース出走馬の前走平均着順」などを投入する場合なども同様ですが、レース出走馬に「出走状況」が「正常でない」馬がいる場合にはその扱いに注意しなくてはなりません。
モデルを構築する際は、すべてのレース結果が分かっている状態ですが、実運用を考えた場合、「中止」「失格」「降着」はもちろんのこと「除外」の状況はほぼ分からず予測に考慮できません(「取消」も予測タイミングによっては怪しい)。
出走取消とは競走馬が装鞍所に入る前に病気やケガ等の理由により、そのレースの出走を取りやめることです。競走除外とは競走馬が装鞍所に入ってから、レースの発走までに病気やケガ等の理由により、出走を取りやめることです。
とあるように、「除外」は装鞍所に入ってから(発走60分前まで)分かる状況であり、経験上ゲートで暴れて故障発生など、馬券購入後に分かる場合が多いです。
ポイントとしては、
出走状況データ利用のポイント
・「中止」「失格」「降着」の馬については、レース出走馬に含める
→例えば「中止」したのが実力馬なのにモデル構築時点でデータから除外していたら、それは情報リークであり、予測精度への影響も大きいです。
・「除外」の馬もレース出走馬に含める。「取消」の馬はレース出走馬に含めない。
→「除外」に関しては、前述の通り運用上判別かほぼ不可能なので、同じ状況を考慮し、モデル構築時点でもデータとして含めます。
→「取消」は基本的に予測時点では判別できる、としてレース出走馬へ含めません。
※「失格」「降着」に関してはレアケースかつ、他馬への影響が大きいと考え実際には学習データからそのレース自体を除外しています。
上記も、あくまで一例ですが、大切なのは「モデル構築時点と運用時点で同じ状況を作り出せているか?」です。
いくらモデル構築時点でシミュレーションして「うまくいきそう!儲かりそう!」となっても、実際予測するときと状況が違う(情報リークによりモデル構築時点の方が有利な状況)、というのはなるべく避けなければ、「思ったより精度が良くない」となってしまいます。

出走状況タイミング
特別な加工が必要なデータについて
その他、値をそのまま格納するのではなく、加工が必要なもの・気を付けなければいけないものをいくつか挙げます。
これ以外にもたくさんあるので状況に合わせて適切な加工が必要です。
○走破時計は1/10秒単位
タイムは通常「2:35:1」や「2分35秒1」の表記になっているので1/10秒単位に直します。
「2:35:1」であれば2*60*10 + 35*10 + 1 = 1551 ですね。
○着差は走破タイムの差
直前の内容に関連しますが、着差については走破時計を1/10秒になおし、1着馬の走破時計との差をとります。
基本的にレース結果表では「クビ」「ハナ」「2.1/2」のように着差が表記されていますがそのままではデータとして扱いずらいためです。
例えば1着馬のタイムが「1:49:3」、対象馬のタイムが「1:50:2」であれば、
1102-1093=9 ですね。
○位置取りはコーナーごとに分割
コーナー順位は「8-7-5-1」のような表記になっているので、それぞれコーナーごとの通過順位に分割する必要があります。
「8-7-5-1」であれば
「1コーナー通過順位」フィールドの値を「8」
「2コーナー通過順位」フィールドの値を「7」
「3コーナー通過順位」フィールドの値を「5」
「4コーナー通過順位」フィールドの値を「1」
として格納します。
ただし、4コーナーでなく2コーナーしかないコースやそもそも新潟1000m直線のようにコーナーの概念がない場合もありますのでその場合は該当の通過順位は0にするなど、対応が必要です。
○呼称が変わっているものに注意
競馬のルールが変更されることにより、データの呼称が変わる場合があります。
代表的なものは「レースクラス」です
2019年夏のルール変更により、
・「500万下」→「1勝クラス」
・「1000万下」→「2勝クラス」
・「1600万下」→「3勝クラス」
とレースクラスの呼称が変更されています。
競馬データを収集するにあたり、このようなデータが混在している可能性があるので、しっかりと分別する必要があります。
あと、データ収集元によっては表記が異なっていたり、半角文字と全角文字が混在している場合もあるので、その場合はプログラム中で対応させてあげましょう。
「500万下」「500万下」「500万円以下」「1勝クラス」「1勝クラス」などはすべて同じレースクラスですが表記が違います。
きちんと対応しないと正しくデータベースに値が登録されません(実体験です笑)
まとめ
今回は、
- 実際の例を元にどうデータベースを構築すべきか
- データベースへ値を格納する際の注意点
について説明しました。
正直今回の内容は、人によって千差万別で、データベースへのデータ登録時点でここまで考慮せず特徴量作成時に加工しても良い場合もあります。
コンピュータの性能やメモリ容量、モデル構築手法などによっても最適な方法が異なりますが、
データベースへある程度加工した状態で登録しておくと、特徴量作成や集計などが楽になる(処理時間短縮)、というメリットがあります。
状況に合わせて適切な選択をしたいですね。
それでは次回は特徴量の設計について解説したいと思います!