Tech Beans

Web企業で働くエンジニアのBlog


BigQuery ML でボストン住宅価格予測

Cloud Nextで発表されたBigQuery MLを使って、ボストン住宅価格予測をやってみます。

BigQuery ML

Cloud Next’18 のKeynoteで発表された、BigQueryのSQLクエリ内で機械学習モデルの学習/推論ができる機能です。 ドキュメントはこちら

特徴としては、

  • BigQuery内で機械学習モデルの学習&推論ができる機能
  • 学習、推論コードはすべてSQL
    • 学習、推論用のデータはBigQuery内のデータで行う。
  • model_typeとして指定できるのは、今の所下記の2つ
    • linear_reg
    • logistic_reg
  • 学習モデルはBQ内に永続化され、モデル自体のダウンロードは(今の所)できない

モデルが2種類しかないのですが、とりあえず試す分には問題ないでしょう。 今後追加実装もされるようです。

データセットの準備

scikit-learnに含まれる、Boston house-pricesのデータセットを使います。 データセットの詳細はこちらを参考にしています。

今回、下記のPythonパッケージを使います

  • scikit-learn
  • pandas
  • pands-gbq
    import pandas as pd
    
    from sklearn.datasets import load_boston
    from sklearn.model_selection import train_test_split
    
    boston = load_boston()
    
    X = pd.DataFrame(boston.data, columns=boston.feature_names)
    display(X)
    y = pd.DataFrame(boston.target, columns=['price'])
    display(y)

scikit-learnで予測

比較のために、scikit-learnで予測してみます。

    from sklearn.linear_model import LinearRegression
    (X_train, X_test, y_train, y_test) = train_test_split(X, y, test_size=0.1, random_state=0)
    
    model = LinearRegression()
    model.fit(X_train, y_train)
    
    predicted = model.predict(X_train)[:, 0]
    result = y_train.copy()
    result['predicted'] = predicted 
    result.sort_index().plot()

    import sklearn
    result = y_test.copy()
    predicted = model.predict(X_test)[:, 0]
    result['predicted'] = predicted 
    result.sort_index().plot()

    "score: %s"  % (sklearn.metrics.regression.r2_score(result['price'], result['predicted']))
    'score: 0.5151316073718609'

今回は精度とか気にしないのでこれでいいでしょう。

BigQueryへのデータセット投入

先程のデータセットをBQに投入します。特徴量と正解値を同じテーブルに入れたほうがラクなので、 DataFrame上で結合してから、BigQuery APIで入れちゃいます。

また、あらかじめtest_dataset という名前でBigQuery データセットを作成してあります。

    PROJECT_ID = XXXX # GCPプロジェクトID
    
    train_data = X_train.copy()
    train_data['price'] = y_train
    train_data
    test_data = X_test.copy()
    test_data['price'] = y_test
    test_data
    
    train_data.to_gbq("test_dataset.boston_train", PROJECT_ID)
    test_data.to_gbq("test_dataset.boston_test", PROJECT_ID)

BigQuery ML

学習

データの準備ができたので、BigQuery MLで、さっそくモデルを学習させてみます。

以下のSQLを打てば、BQで学習が始まります。

    CREATE OR REPLACE MODEL `test_dataset.boston_predict_model`
    OPTIONS
      (model_type='linear_reg', input_label_cols=['price']) AS
     SELECT
      *
    FROM `test_dataset.boston_train`

CREATE OR REPLACE MODELでモデルを作成します。モデル名は [dataset].[model name] と指定します。

OPTIONS ではモデルの設定が可能です。ここではモデルとしてlinear_reg、また正解データのラベルは作成したデータ・セットのpriceを指定しています。

学習用のデータはSELECT XXX で指定します。テーブルには特徴量と正解ラベルが一緒に入っているので、BQが区別できるよう、input_label_colsで正解データの列を指定しているわけです。

学習が成功すると、モデルが作成されます。

BQのUIから見ると、データセット内に、テーブルと同じように格納されていることがわかります。

評価

ML.TRAINING_INFOで学習の収束状況などを確認できます。

    SELECT
      *
    FROM
      ML.TRAINING_INFO(MODEL `test_dataset.boston_predict_model`)

また、ML.EVALUATEでモデルの評価もできます。

    SELECT
    *
    FROM
      ML.EVALUATE(MODEL `soymsk-gcp.test_dataset.boston_predict_model`,  (
        SELECT
          *
        FROM
          `test_dataset.boston_test`))

結構lossが大きいようですが、iteration回数は調整できるので、今回はこのままいきます。

ちなみに、データを入れる際にラベルを指定していませんが、ラベルはModel側が記憶しているので、特に指定する必要はありません。逆に、学習時と同じラベル(カラム名)でデータを入れる必要があります。

推論

学習済みのモデルを使った推論もSQLクエリで行います。

    SELECT
      price,
      predicted_price
    FROM
      ML.PREDICT(MODEL `soymsk-gcp.test_dataset.boston_predict_model`,
        (
        SELECT
          *
        FROM
          `test_dataset.boston_test`))

ML.PREDICTの第一引数に学習済みモデルをMODEL [model name] の形式で指定し、第2引数に特徴量を入れます。ここでは先程作成したboston_testテーブルのデータを入れています。 カラム名も同じなため、改めて指定し直す必要もありません。

精度はチューニングしていないのでそれほど良くはありませんが、SQLクエリだけでMLモデルを作って推論することができました。

BigQuery MLの価格

ここが気になるところですが、ドキュメントには

Currently, if you use BigQuery on demand, your BigQuery ML charges are based on the data processed by each query. For BigQuery ML queries, the data processed is usually greater than the just the input data for the CREATE MODEL statement. Flat-rate customers can use their existing slots for BigQuery ML until Jul 31, 2019.

とあるので、他のSQLクエリ同様、処理データ量に依存するみたいです。

が、書いてあるとおり、処理データ > 全データ量 となる場合もあるらしく、反復計算するので当たり前といえばそうですが、ここは気にしておいたほうが良さそうです。

なんとなく 「全データ量 x itreration数」が上限だと予想しますが、そもそも価格ロジック自体がまだβであり、将来的に変更となる可能性があります。

まとめ

今回BQで簡単な線形回帰をやってみました。 簡単なモデルしか用意されていないですが、BigQuery上に大規模なデータセットを持っている場合、いちいちSparkML等でやるのも骨が折れますが、BigQueryのマシンリソースを使って誰でも簡単に機械学習が試せる、というのはすごいことだと思います。

個人的には時系列予測や異常検知系のニーズが高いので、そこが実装されると異常検知しつつStackdriver経由でアラートあげるとか、普通にやるといろいろなコンポーネントを組み合わせないと実現できない構成がぐっとラクに作れるようになるのではと期待しています。