LoginSignup
7
6

More than 3 years have passed since last update.

BQML DNNで競馬予測してみる

Last updated at Posted at 2020-07-03

BQMLでDNNとXGBoostとAutoML Tablesが使えるようになるというニュースがありDNNを試してみました。
なんとなくでそれなりにいい感じにできちゃいます。
https://cloud.google.com/bigquery-ml/docs/release-notes

データを準備する

説明変数と目的変数が1行に含まれているデータをBQに投入する。
私は趣味で集めていた競馬データを使ってみた。
競馬データを集めたい場合は以下の記事が大変参考になる。
データ収集からディープラーニングまで全て行って競馬の予測をしてみた

競馬の場合、以下を横持ちにしているデータを準備する。

  • 説明変数:レースの距離、会場、性齢、斤量、過去レースの結果、該当距離の成績、、等
  • 目的変数:着順(3着以内になるか否か)

明らかな連続値は数値型にして、あとは文字型にしておけばいい感じに学習してくれる。
(本当はもう少し丁寧に前処理した方が良いのだろうが、、)

モデルを作成する

CREATE OR REPLACE MODEL <モデル名>
OPTIONS(MODEL_TYPE='DNN_CLASSIFIER',
        ACTIVATION_FN = 'RELU',
        AUTO_CLASS_WEIGHTS = TRUE,
        BATCH_SIZE = 2048,
        DROPOUT = 0.1,
        EARLY_STOP = TRUE,
        HIDDEN_UNITS = [128, 128, 128],
        INPUT_LABEL_COLS = ['目的変数カラム'],
        LEARN_RATE=0.001,
        MAX_ITERATIONS = 500,
        OPTIMIZER = 'ADAM')
AS SELECT  
  <説明変数カラム>,
  <目的変数カラム>
FROM
  <table name>

大体1時間〜2時間くらいでモデルは作成できた。

各パラメータについて

上記のモデル作成の例は、公式ドキュメントに乗っているパラメータほぼそのままだが、
これでもそれなりのものができるっぽい

MODEL_TYPE

モデルのタイプを回帰(気温等の連続値の予測)か、分類(天気が晴れなのか雨なのか等のカテゴリ値)か選択する

MODEL_TYPE = { 'DNN_CLASSIFIER' | 'DNN_REGRESSOR' }

競馬の着順(3着以内になるか否か)はカテゴリ値の分類なので、DNN_CLASSIFIERにする

ACTIVATION_FN

活性化関数(ニューロンが発火するための計算式)を選択する

ACTIVATION_FN =  { 'RELU' | 'RELU6' | 'CRELU' | 'ELU' | 'SELU' | 'SIGMOID' | 'TANH' }

(どれ使えばいいんだ。。)

以下Wikipediaより

ReLU(ランプ関数)
2011年、Xavier Glorot らは隠れ層の活性化関数としてmax(0, x) を使った方がtanhやソフトプラスよりも改善するということを発表した。一般的にはこれはランプ関数と呼ばれるが、ニューラルネットワークの世界ではReLU (英: Rectified Linear Unit, Rectifier、正規化線形関数)と呼ばれる。Yann LeCunやジェフリー・ヒントンらが雑誌ネイチャーに書いた論文では、2015年5月現在これが最善であるとしている。発想としては区分線形関数を使った1次スプライン補間である。線形補間を参照。
image.png

2015年5月時点では最善だったっぽいので、とりあえずRELUにしてみる。

AUTO_CLASS_WEIGHTS

分類モデルのときだけ有効なパラメータ。
不均衡データ(例えば分類したいラベルにAとBの2種類があってAが90%みたいなとき)を分類するときにいい感じに重み付けしてくれるらしい機能。
今回の競馬分類はそれなりに不均衡データになると思うので、TRUEにする。
デフォルトはFalse。

機械学習における不均衡データの扱い方

AUTO_CLASS_WEIGHTS = { TRUE | FALSE }

BATCH_SIZE

DNNではミニバッチ勾配降下法というトレーニングデータをサブセットに分けて最適なパラメータを見つける手法が使われるようだが、そのときのサブセットのデータサイズ。

機械学習/ディープラーニングにおけるバッチサイズ、イテレーション数、エポック数の決め方

今回のデータの件数は100万件くらいなので、とりあえず2048でやってみる。

BATCH_SIZE = int64_value

DROPOUT

過学習を防止するために、学習を進める際に一定の確率でノードを無視する率。
少し調べてみると、0.5か0.1が多そう。BQML公式ドキュメントの例では0.1になってたので、とりあえず0.1でやってみる。

Dropout:ディープラーニングの火付け役、単純な方法で過学習を防ぐ

DROPOUT = float64_value

EARLY_STOP

学習の結果あまり改善がなかったときに、学習をストップさせるかどうか。
過学習防止や余計な計算リソースを使わないようにする。
デフォルトはTRUE。

EARLY_STOP = { TRUE | FALSE }

HIDDEN_UNITS

DNNの隠れ層における、層の数とユニットの数を指定。
隠れ層と隠れニューロンはいくつにするべきなのか?

(しかし適切な数がよくわからない。。)

BQML公式ドキュメントの例にならって、[128, 128, 128]にしてみる。

HIDDEN_UNITS = int_array

INPUT_LABEL_COLS

目的変数カラムを指定する

INPUT_LABEL_COLS = string_array

LEARN_RATE

学習のたびにどれだけ重み付けパラメータを更新するかという率。
高すぎるとうまく学習しなかったり、小さすぎると収束まで時間がかかりすぎる。

(どのあたりが妥当なのだろうか。。)

BQML公式ドキュメントの例にならって、0.001にしてみる。

LEARN_RATE = float64_value

MAX_ITERATIONS

最大イテレーション回数。
イテレーション回数はバッチサイズに対して、何回学習すると元のデータセット件数になるかという回数。
約100万件のデータに対してバッチサイズを2048にした場合は、500が適切っぽいのかな。

機械学習/ディープラーニングにおけるバッチサイズ、イテレーション数、エポック数の決め方

MAX_ITERATIONS = int64_value

OPTIMIZER

【2020決定版】スーパーわかりやすい最適化アルゴリズム -損失関数からAdamとニュートン法-

「損失をゼロにする」というゴールをなるべく効率よく到達してくれるものが最適化アルゴリズム です。

Adamの正体はモーメンタムとRMSPropの良いとこどり

(良いとこどりとは素晴らしい。とりあえずAdamにしよう)

OPTIMIZER =  { 'ADAGRAD' | 'ADAM' | 'FTRL' | 'RMSPROP' | 'SGD' }

モデルの評価

以下のように、説明変数カラム全部と目的変数カラムをもつWith句を作成し、
ml.evaluate関数にモデル名とWith句のテーブル名を渡す

with eval_table
as
(
select 
  <説明変数カラム>,
  <目的変数カラム>
FROM
  <table name>
)
select
 *
from
ml.evaluate(MODEL `<モデル名>`, table eval_table)

結果

以下のような結果が返ってくる

precision recall accuracy f1_score log_loss roc_auc
0.3089887640449438 0.7638888888888888 0.572736520854527 0.44 0.6899435430022666 0.6968951048951049

【入門者向け】機械学習の分類問題評価指標解説(正解率・適合率・再現率など)
機械学習でLog Lossとは何か
【ROC曲線とAUC】機械学習の評価指標についての基礎講座

どの指標を重視するのかは作るモデルごとに異なるみたいです。
(今回のモデルだと何を重視したらよいのか、、)

  • precision(正解率):正予測の正答率。今回の場合だと、3着以内と予想した馬が実際に3着以内になる確率
  • recall(再現率):正に対する正答率。今回の場合だと、実際3着以内になった馬の中で、3着以内と予想された確率
  • accuracy(正解率):全予測正答率。今回の場合だと全予測の中で、実際に3着以内予想と3着以内外の正解率
  • f1_score:適合率と再現率の調和平均(適合率と再現率のバランスを見るらしい)
  • log_loss:間違ったラベルを高い確率で出しちゃうと高くなるらしい
  • roc_auc:不均衡データを考慮した指標。高いほどいいっぽい

予測する

以下のように、説明変数カラム全部をもつWith句を作成し、ml.predict関数にモデル名とWith句のテーブル名を渡す

予測された分類結果は「predicted_目的変数カラム」の項目になる(予測項目がuriageならpredicted_uriageになる)
確率をもつ項目は「predicted_目的変数カラム_probs」になる(これはネストされた項目なので、UNNESTすることで扱いやすくなる)

with pred_table
as
(
select 
  <説明変数カラム>
FROM
  <table name>
)
select
  ~,
  predicted_目的変数カラム, /* 分類結果 */
  data.prob /*分類確率*/
from
  ml.predict(MODEL `<モデル名>`, table pred_table),
  unnest(predicted_目的変数カラム_probs) as data
where 
  data.label=1 /* 3着以内に入る確率だけ出す */

2020 年宝塚記念を予測するとこんな感じ

先週(2020/6/28)のG1宝塚記念を予測してみた。
並びは3着以内確率上位順。
1着は外しているが何となくいい線いってそうな気がする。
(ちなみにこのレース3連単 183,870円! です)

レース名 馬番 馬名 3着内確率
宝塚記念 5 サートゥルナーリア 0.82017
宝塚記念 16 クロノジェネシス 0.79715
宝塚記念 14 キセキ 0.75275
宝塚記念 12 モズベッロ 0.70728
宝塚記念 11 ラッキーライラック 0.69796
宝塚記念 10 メイショウテンゲン 0.66159
宝塚記念 1 トーセンカンビーナ 0.56264
宝塚記念 3 グローリーヴェイズ 0.55474
宝塚記念 7 ワグネリアン 0.52002
宝塚記念 18 ブラストワンピース 0.48493
宝塚記念 8 レッドジェニアル 0.48320
宝塚記念 15 スティッフェリオ 0.33708
宝塚記念 6 トーセンスーリヤ 0.33540
宝塚記念 13 ダンビュライト 0.32290
宝塚記念 17 カデナ 0.3186
宝塚記念 2 ペルシアンナイト 0.27348
宝塚記念 9 アドマイヤアルバ 0.14271
宝塚記念 4 アフリカンゴールド 0.09236

料金

BigQuery ML の料金によると、
CREATE MODELは毎月10GBの無料枠があり、それ以降は$250.00 per TBかかるようである。

しかし以下の画面のように実行見積もりは150MBだけど、実際は87GB使っちゃいましたみたいなパターンはどのような扱いになるのかイマイチよくわからない。。
(今の所BQMLの請求きてないので、見積もりのほうが優先されるのか。。)

image.png

その他参考

BigQuery MLにAutoML Tables、XGBoost、DNNが来たのでおさらい

The CREATE MODEL statement for Deep Neural Network (DNN) models

7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6