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次スプライン補間である。線形補間を参照。
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の請求きてないので、見積もりのほうが優先されるのか。。)
#その他参考
BigQuery MLにAutoML Tables、XGBoost、DNNが来たのでおさらい
The CREATE MODEL statement for Deep Neural Network (DNN) models