KaggleのHousePredictionを題材にしてAutoML Tablesがどんなもんか見てみる

# TL;DR
- AutoML TableがGoogle Cloud Next'19で発表されたよ
- もう触れるみたいなので、KaggleのHousePricePredictionで試してみたよ、手軽だったよ
- 一応LightGBMと比較してみたら、チューニングすれば良い成績を出せたよ

# 前置き
Google Cloud Next'19でAutoMl Tableが発表されましたね〜

[automl-tables](https://cloud.google.com/automl-tables)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/233c5422-2a91-8dca-a7ab-cf6a41317970.png)

LPがいつもすこ

早速使えるようなので(現在はβ版)、使ってみました。題材はKaggleから取ってきます。

# Titanicでやろうとしてみた

Kaggleのいつものやつ、ということでTItanicでやろうとしてみました。→ [Kaggle Titanic](https://www.kaggle.com/c/titanic) 

csvを取得してきて、BigQueryにテーブルとしてインポートします。GCSにcsv直接置くのでもよいんですが、今回は AutoMl **Table** なので、テーブルにしていきたいですよね(?)

![78a946dc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/ec3b4c8a-6a6c-56fc-8829-318e3bdb5fe2.png)

カラムはこんな感じです
![d4b1f22c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/df960e65-b165-f210-f345-da9736c79c6f.png)

[automl-tables](https://cloud.google.com/automl-tables) のリンクからコンソールに飛ぶと以下のような画面になります。

さっそくデータセットを作成していきましょう。

![4796f282.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/15dbd6c6-ad43-46f9-f5c7-1b395aab0464.png)

![961dd8d2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/ef46e66e-fa97-fcbb-ad02-c8fdd6de1160.png)

しかしここで新たな事実が発覚。

![2723b58c-cea7-e578-b76f-cdd8ce8978b7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/5adf9e95-8866-01c9-cefc-2596cc6c3acf.png)


最低1000行ないとトレーニングできない...

違うのにしましょう。

# House Price Predictionでやる
Titanicと同じくらい初心者向けで有名なのでこちらのHousePricePredictionです。
[House Prices: Advanced Regression Techniques \| Kaggle](https://www.kaggle.com/c/house-prices-advanced-regression-techniques/)

こっちはデータ数が1400件で制限をクリアしているのでこちらでやっていきます。

## データの準備

同じようにｃｓｖをダウンロードしてきてBigQueryに用意します。

![ee83d71f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/43faac18-832a-d87c-7aff-17442e7e5b05.png)

![262cb33e-c20f-20be-7615-2827674a4148.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/7b0cb563-fab7-5816-7436-50cd0eab10e0.png)


必要な情報を入力していってポチポチしていけばデータセット作成できます。かんたん。

![75979810.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/358fd34d-864f-9bf7-74fa-ccd684fa5a77.png)

この画面になったら一旦待ちです。と思いきや

![1567b317.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/1cda1411-245c-936b-ccf9-f8a35c537391.png)

なんかの理由でインポートできない...

数字から始まるカラムがあると悪いみたいなので、 1stとかなってるやつを変えておきます。(1stFlrSF,2ndFlrSF,3SsnPorch→firstFlrSF,secondFlrSF,threeSsnPorch)

さらにnullがある状態を避けておきたいので、あらかじめnaを埋めておいたりします。こちらのkernelを参考にどうぞ。
[Easy prediction using lightgbm model \| Kaggle](https://www.kaggle.com/jens0306/easy-prediction-using-lightgbm-model)

これをやった上で、改めてBigqueryのテーブルをimportしましょう。ここまで何回も読み込むくらいなら、小さいデータならGCSでやるほうがいいのでは...？と思い始めてきましたがめげません。 AutoML **Tables** なので(?)

## スキーマ確認と目的変数決定

で、無事にインポートできると次のような画面になります。ここではスキーマを確認します。

![cd35a6f5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/6ae3f11e-e7d1-1a28-b0e4-db3c5bcb5e4f.png)

Nullableかどうかはすごく重要です。今回は前もってNAを埋めておりましたので考える必要ないですが、ここが間違ってて実はNULL入るよー入らないよーがあったりすると、予測時にエラーになってしまいます。運用のことを考えるとあやしいやつは全部NULLABLEにするほうがいいのかもですね。
今回は全てnull許容なしで大丈夫。

![9a9d3ec2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/8f0e0e6d-0b07-1955-0f1c-eaf4ebcc5e62.png)

目的変数は横のパネルで決定します。このとき、各レコードごとに重みを決めたいならそのカラムがあれば重みとして加えられるようです。
更に時間に関わるカラムがあればそれを使うことでリークを防ぐようにできるっぽいです。過去のデータで未来を予測するvalidation setにしてくれてそうな気がする。

## 分析タブ
以上を終えてボタンを押せば分析タブに移行します。各種指標を計算してくれているみたいで、少し時間がかかります。

![be435762.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/4d62e564-5cf2-7427-a14b-3b312af3d525.png)

いろんな指標を計算してくれて気分がいいですね。

項目を押すと、横にグラフが出て傾向を確認できます。

![10d5eda6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/1bcc5d83-1c61-b2e1-a510-788635090779.png)

目的変数との相関を計算してくれていて、それでソート・フィルタが可能です。

![2cec8d4e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/f4300cbe-082c-49b6-d972-016782d3b3c1.png)

キッチンのクオリティが家賃との相関高めと出てますね。たしかにそれっぽい。

## トレーニングタブ
では早速学習してみましょう。

![4fe4f8f3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/5f3de655-d663-7c8a-9ffb-acf1ad359140.png)

budgetは並列数みたいなものと解釈してます。データ数が多いならここを予算が許す限り増やせば良さそう。ちなみに、割と大きな数にしないと多分あんまり早くならないです。1~3を試したんですが、あまり大差がなかった。

このタブでは特徴量の選択ができます。
ここでIdは弾いておかないと当然リークするので外します。このへんはちゃんと機械学習、というか思考停止でポチポチするだけだと死ぬポイントですね。
![a1b1a974.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/3c3ae0ab-a31c-b021-9162-28835c4122dd.png)

ちゃんと回帰であることを認識してそのモデルになっていますね。安心。
またmetricsもいくつかから選べます。今回のHouse PredictionのmetricsはRMSEなので、それを選択しておきます。

![3a6daad3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/83a2e9b9-b744-c503-e3f0-001766acac2c.png)

トレーニングしているときはこういう表示。終わるとemailが飛んできます。

![ad981cba.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/4bce3693-cc07-c128-ea30-e513436cdd4b.png)

## 評価タブ

しばらくコーヒーブレイク(node数が少ないときはコーヒーブレイクどころでは済まないと思います。僕は3nodeで1時間くらい待ちました。)していれば、メールが届きます。
コンソールを見に行けば、学習終わった風の画面になっていますね。

![db76620e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/999301ad-5cb0-5e5e-16ed-f10b5dfa3bab.png)

クリックすると評価タブに移行します。いろんな数字が並んでて気持ちいいですね。

![0c1039d6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/5f3c522f-86e3-7929-9cfc-72c0eef9b8dc.png)

Feature Importanceまで出てます。ってことはやっぱり中身は勾配Boostingなんでしょうか...

![b1935ddc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/295e38d8-8177-7d08-3ef9-c54851fac9f4.png)

実は評価タブ内で、どのモデルを使っててこのモデルが良かったよ！的なのが見れるんじゃないかと期待していました。それこそデータロボットみたく。まあでもその機能は残念ながらないっぽいっすね。しょうがなし。

## 予測タブ
ではでは、予測やっていきましょう。今回はバッチ予測をやります。予め前処理しておいたtestデータをBigQueryに突っ込んでおき、最初にデータセットを作成したときと同じようにそのターブルを指し示す情報を入力していきます。

結果を格納したものはBigQueryのデータセットで吐き出されます。指定するのはプロジェクトIDだけで大丈夫です。

![94600080-5ce7-397f-e7d5-33a191fe73ce.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/ca09a37a-cb9d-9cb8-c1c9-9087182d328f.png)


では、また再びコーヒーブレイクしましょう。おそらくここでもnode数が効いてきます。

終わると再びメールが届きます。BigQueryを見に行けば、こんな感じで指定したプロジェクト配下にテーブルができてます。

![8d4ffcd9-cf59-b5e8-724b-b3c1f0be262e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/2ad04de1-ed66-7d77-56c0-03b7f7f00641.png)

予測結果とエラーがデータセット内には格納されています。エラーテーブルを見て、0件なら安心。そうじゃなかったらnullableなどを確認しましょう。もう一回スキーマの確認からです...

![4eb43bbf-2cb9-30f0-7e7c-f22acfeab448.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/8c472def-f9a1-3e8e-b70e-b5e0c39653a4.png)

全件予測が完了していることを確認しつつ、中身の値を見てみましょう。今回は最後のvalueだけ見れば良さそうですね。

![1bda4052.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/a2d7f223-a84c-0d3c-7b01-297b719218d9.png)

注意すべきことなのですが 予測結果 `predicted_{target_name}` は `RECORD` 型で返ってきます。jsonみたいなものなのですが微妙に違います。

普通に `select predicted_{target_name}.tables.value from ...` でアクセスしようとしてもだめです。 `UNNEST` を使って配列構造から開放する必要があります。 
 
```sql
WITH
  tmp AS (
  SELECT
    *
  FROM
    `{your_project_id}.prediction_house_prediction_20190411105013_2019_04_11T07_59_52_140Z.predictions`)
SELECT
  CAST(Id as int64) as Id,
  tables.value AS SalePrice
FROM
  tmp,
  UNNEST(tmp. predicted_SalePrice)
order by Id
```

上記のSQLで抽出すれば、あとはもうsubmitするだけです。submit!

![e1971c5b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/295a0d67-d28e-e0e4-c347-9b0c694be2c4.png)

0.15ということのようです。LeaderBoard的には、2900位くらい。4500チーム中でということを考えるとそこそこくらいですかね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/c480ddad-f71b-2f7d-40c6-bf031448e4af.png)

# 自分でコードを書いたLightGBMと比較する

さて、では僕が失職するかどうかを占うために、同じ特徴量でLightGBMにかけてみてチューニングでどれくらい伸ばせるかをやってみます。

コードはこんな感じです。見づらいですがご容赦ください。

```python
# 既にna処理をしたあとで、 train_fillna、test_fillnaというデータが有るところから。

predictors = [v for v in train_fillna.columns if v not in ['Id', 'SalePrice', 'train']]
categorical = ["Utilities", "Street", "SaleType", "SaleCondition", "RoofStyle" ,"RoofMatl"
,"PavedDrive","Neighborhood","MSZoning",'MasVnrType','LotShape',"LotConfig","LandSlope","LandContour","KitchenQual",'KitchenAbvGr',"HouseStyle","HeatingQC","Heating","HalfBath"
,"GarageType","GarageQual","GarageFinish","GarageCond","GarageCars","Functional","FullBath",'Foundation',"Fireplaces","ExterQual","Exterior2nd","Exterior1st","ExterCond","Electrical"
,"Condition2","Condition1","CentralAir","BsmtQual","BsmtHalfBath","BsmtFullBath","BsmtFinType2","BsmtFinType1","BsmtExposure","BsmtCond","BldgType"]

for c in categorical:
    train_fillna[c] = train_fillna[c].astype('category')
    test_fillna[c] = test_fillna[c].astype('category')

from sklearn.model_selection import train_test_split
dev, _eval = train_test_split(train_fillna)

import lightgbm as lgb

lgb_params = {
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric':'rmse'
}

xgtrain = lgb.Dataset(dev[predictors], label=dev['SalePrice'].values,
                      feature_name=predictors
                      )

xgeval = lgb.Dataset(_eval[predictors], label=_eval['SalePrice'].values,
                      feature_name=predictors
                      )

bst = lgb.train(lgb_params,
                 xgtrain,
                 valid_sets=[xgtrain, xgeval],
                 valid_names=['train','valid'],
                 num_boost_round=2000,
                 early_stopping_rounds=100,
                 verbose_eval=1)

# Submission
y_pred = bst.predict(test_fillna[predictors])
submission = pd.DataFrame(test_fillna['Id'])
submission['SalePrice'] = y_pred
submission.to_csv('lightgbm_submission.csv', index=None)
```

では、まず上記のパラメータ通りにほぼ何もしない状態でLightGBMをやってみて、Submitしてみましょう。どりゃー

![7566f60f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/3cbad421-d843-cd2b-aa4b-a8d22d721fc9.png)

な、なるほど。さすがにチューニング無しで勝てるほどあまくはなかったですね...

ではここから、手で頑張ってチューニングしていきます。30分くらいかけて一番良さそうになったのがこちらです。 

```python
lgb_params = {
    'boosting_type': 'gbdt',
    'objective': 'regression',
    'metric':'l1',
    'learning_rate': 0.01,
    'feature_fraction': 0.6,
#     'max_bin':10,
    'num_leaves': 128,
#     'min_data': 500,
#     'min_hessian': 0.05,
#     'bagging_fraction': 0.85,
#     'bagging_freq': 5
}
```

で、結果がこちらです！

![6dbd04b9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/174bdd0e-b745-43b0-a99f-9707a44a73a4.png)

おー、というわけで無事に AutoMLTables(0.15)よりも良い成績になりました。まだ僕は失職せずに済みそうです。

`optuna` をつかえばもっと良いパラメータになっていることだと思いますので、もうちょっと差は広げられそうですかね。

## 両者の結果を比較する

最後に両者の予測結果をいろいろと比較してみたいと思います。まずはFeature Importanceから。

### Feature Importance
1. LightGBM
![8b4f328e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/b2abfbe1-e979-3d74-55bf-a9a4640c1e36.png)

2. AutoML Tables
![197ab229.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/9159e4b7-7f76-4eef-ae0e-79d042e90d7e.png)

AutoML Tables、細かいところはカーソル当てないとみえない...
GrLivAreaやLotArea・garageAreaが上位という傾向は似てました。

### ヒストグラム・基本統計量

ヒストグラムを書いてみると、automlの方が中央によっているようなきがする...
ちょっとoverfit気味なのかもしれないですね。LightGBMチューニング時も結構早くリフトしちゃってたのでなるべくそれを抑えるようにしてました。

![f8b3e174.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/70d34f79-2a4a-c6fb-0007-782ce0f88771.png)

とおもって基本統計量を出してみましたが、そういう感じでもない。stdがlightgbmのほうが大きいってことくらいですかね。

![2b5a0f35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/a9e9cbcd-20da-46a4-aae5-2dc4e9e6d84f.png)

# 結論と感想
さて、というわけでAutoML Tablesを早速やってみました。

個人的に一番嬉しいのは BigQueryをインプットにできる点ですね。巨大なデータをBigQueryに保有している企業にとって、そのデータをすぐに予測に回せる(csvにわざわざ戻してインスタンスに送ったりする必要がない)のは大きな利点だと思います。

node数を割と大きくしないと時間がかかるってのは何回か回してみて肌感がつかめるところだと思います。お金に気をつけつつ試すとよいのかなと。

そういえば気になるお支払情報ですが、上記のことをいろいろ試行錯誤してみて、だいたい4000円でした。ま、まあこんなもんですかね...
![94d09006-f2b1-03ce-edf6-b1fe61558cfe.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109201/f6b9c080-5696-7326-dbd6-d007e661f0ae.png)

特徴量は依然として人間が頑張って考えたものに依存すること、チューニングはまだ頑張りようがあること、という点などを確認できました。
運用の手間を減らせること、BigQueryからすぐにデータを取り寄せて予測ができるところなど、良い点とうまく付き合いつつもうしばら失職せずに機械学習エンジニアとしてやっていきたいと思います。