BigQuery MLの使い方についてのまとめ

# これなに?

この記事はBQMLで利用できるアルゴリズム、モデルを作成する方法と、特徴量生成や評価値の確認などを行うための関数についてのまとめです
一応続き物ということになってますが、前回記事と前々回記事はデータセットの作成までしか行っていないので、BQMLの使い方自体については、この記事だけで完結してます  
興味ある方は

- [BigQuery + jupyterで機械学習を行う(その1: データセットの作成)(前々回記事)](https://qiita.com/hiroaki_hase/items/3d7da2166b5bc495879f)
- [BigQuery + jupyterで機械学習を行う(その2: 前処理、EDA、特徴量生成)(前回記事)](https://qiita.com/hiroaki_hase/items/5fcc9f056d44105d186e)

をお読みいただけると嬉しいです  
なお、すべての処理はjupyter上で行い、この記事自体もjupyterで書きました(jupyter上でbqを使用する方法については前々回記事参照)


```python
from google.cloud.bigquery import magics
from google.oauth2 import service_account
credentials = (service_account.Credentials.from_service_account_file('[/path/to/key.json]'))
magics.context.credentials = credentials
```


```python
%load_ext google.cloud.bigquery
```



```python
PROJECT ='自身で作成したプロジェクトID'
```

# BQMLの使いどころ
- 大量のデータセットから、素早く学習、予測を行いたい場合
    - アルゴリズムによりますが数千万レコードのデータセットでも数分で学習が実行できます
- 素早く機械学習システムをプロダクトに導入したい場合
    - 機械学習が実行でき、かつクエリを定期実行することも可能なので、極端に高い精度が求められなければ、低コストで機械学習システムを導入できます
- とりあえず、機械学習を試してみたい場合
    - 上記に似てますが、SQLで完結するのと、パラメータのチューニングや必要最低限の前処理などはよしなにやってくれるので、あまり機械学習に触れたことがない人でも簡単に試せます

# アルゴリズム一覧(2019/7月時点)
## 普通に使える
- linear_reg
    - 線形回帰
- logistic_reg
    - ロジスティック回帰
- kmeans
    - k-平均法
    - 唯一の教師なしアルゴリズム

## betaにだけある
- tensorflow
    - cloud storage に保存してあるmodel fileを呼び出して実行
    
## 発表だけあった
- matrix_factorization
    - 協調フィルタリング
    - 4月のNext19で発表
    - いまのところbetaにもなし

- dnn_classifier
    - いわゆるDeep Learning
    - 4月のNext19で発表
    - いまのところbetaにもなし

# 使用するデータセット

前回の記事で作成したgoogle_analyticsのdatasetから、特定の商品をclickする確率を予測します
(下のはすでに加工済みのデータセットです)


```sql
%%bigquery --project $PROJECT 
SELECT * FROM `google_analytics.ga_dataset_feature` LIMIT 5
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>countryHour</th>
      <th>browserMobile</th>
      <th>priceBin</th>
      <th>isClick</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>{'country_hour': 'Peru_9'}</td>
      <td>{'browser_isMobile': 'Chrome_false'}</td>
      <td>bin_1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>{'country_hour': 'Peru_9'}</td>
      <td>{'browser_isMobile': 'Chrome_false'}</td>
      <td>bin_1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>2</th>
      <td>{'country_hour': 'Peru_9'}</td>
      <td>{'browser_isMobile': 'Chrome_false'}</td>
      <td>bin_1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>3</th>
      <td>{'country_hour': 'Peru_9'}</td>
      <td>{'browser_isMobile': 'Chrome_false'}</td>
      <td>bin_1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>4</th>
      <td>{'country_hour': 'Peru_9'}</td>
      <td>{'browser_isMobile': 'Chrome_false'}</td>
      <td>bin_1</td>
      <td>0</td>
    </tr>
  </tbody>
</table>
</div>



# 特徴量生成(`ML.BUCKETIZE`、`ML.FEATURE_CROSS`)

BQMLには、モデル作成以外にも特徴量生成のための関数も用意されてます。

- `ML.FEATURE_CROSS`
    - 交差特徴量を生成します
- `ML.BUCKETIZE`
    - 連続値を任意のしきい値で分けて、カテゴリにできます

具体的な使い方は[前記事](https://qiita.com/hiroaki_hase/items/5fcc9f056d44105d186e#%E3%82%88%E3%82%8A%E3%82%88%E3%81%84%E7%89%B9%E5%BE%B4%E9%87%8F%E3%82%92%E6%8E%A2%E7%B4%A2)を参考にしてください


```sql
%%bigquery --project $PROJECT 
SELECT
    browser,
    isMobile,
    -- ブラウザとモバイルかどうかの交差特徴量
    ML.FEATURE_CROSS(
    STRUCT(
            browser,
            isMobile
    )) 
    AS browserMobile,
    productPrice,
    -- 商品の値段をbin化
    ML.BUCKETIZE(
    productPrice,
    [2, 5, 16, 17, 20, 50]
    ) AS priceBin
FROM  `google_analytics.ga_dataset_cleansed`  
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>browser</th>
      <th>isMobile</th>
      <th>browserMobile</th>
      <th>productPrice</th>
      <th>priceBin</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>Safari</td>
      <td>True</td>
      <td>{'browser_isMobile': 'Safari_true'}</td>
      <td>37.99</td>
      <td>bin_6</td>
    </tr>
    <tr>
      <th>1</th>
      <td>Chrome</td>
      <td>False</td>
      <td>{'browser_isMobile': 'Chrome_false'}</td>
      <td>55.99</td>
      <td>bin_7</td>
    </tr>
    <tr>
      <th>2</th>
      <td>Chrome</td>
      <td>False</td>
      <td>{'browser_isMobile': 'Chrome_false'}</td>
      <td>20.99</td>
      <td>bin_6</td>
    </tr>
    <tr>
      <th>3</th>
      <td>Chrome</td>
      <td>False</td>
      <td>{'browser_isMobile': 'Chrome_false'}</td>
      <td>1.59</td>
      <td>bin_1</td>
    </tr>
    <tr>
      <th>4</th>
      <td>Internet Explorer</td>
      <td>False</td>
      <td>{'browser_isMobile': 'Internet Explorer_false'}</td>
      <td>11.99</td>
      <td>bin_3</td>
    </tr>
  </tbody>
</table>
</div>




# モデルの構築(`CREATE MODEL`)

モデルを構築してみます。作り方は`CREATE TABLE`によく似ています  
今回は分類問題をときたいので、アルゴリズムはロジスティック回帰(`logistic_reg`)で行います  
モデルに入力する特徴量が順序特徴量として使われるか、カテゴリ特徴量として使われるかはカラムで定義されている型によって決定し、`INT64`、`FLOAT64`等の場合は数値として入力され、`STRING`、`TIMESTAMP`等の場合は、カテゴリ型として、自動でone-hot encodingされた後、モデルに入力されます


```sql
%%bigquery  --project $PROJECT 
CREATE OR REPLACE MODEL google_analytics.ga_model_log  
OPTIONS(
    INPUT_LABEL_COLS=['isClick'], 
    MODEL_TYPE='logistic_reg',
    -- 今回はclickしないレコード数が多い不均衡データのため、
    -- 重み値を頻度の逆数で重み付けする
    AUTO_CLASS_WEIGHTS=True,
    -- 下2つのパラメータはテスト用のデータを多くしたいがために設定。
    -- 何も指定しないと2割のデータが自動でテスト用に使用される
    DATA_SPLIT_METHOD='RANDOM',
    DATA_SPLIT_EVAL_FRACTION=0.4
    )
AS 
SELECT * FROM `google_analytics.ga_dataset_feature` 
```





`INPUT_LABEL_COLS`には正解ラベルとなるカラム名を、`MODEL_TYPE`には使用するアルゴリズムを入力します  
その他のパラメータを指定しないと、l2正則化や学習率などのハイパーパラメータや、trainデータとセットtestデータセットの分割方法などは自動で行われますが、指定したい場合はオプションで設定できます([詳細はドキュメント参照](https://cloud.google.com/bigquery-ml/docs/reference/standard-sql/bigqueryml-syntax-create?hl=ja))   

`ga_model_log`という名前でモデルが生成されました。なお、モデルを修正したり消去したいときは、table作成と同じように`REPLACE MODEL`や`DROP MODEL`で行えます

# トレーニング結果を確認(`ML.TRAINING_INFO`)
イテレーション毎に適用されたパラメータと評価値がわかります。(ただし、コンソール上から見たほうが見やすいです)


```sql
%%bigquery  --project $PROJECT 
SELECT * 
FROM 
ML.TRAINING_INFO(MODEL `google_analytics.ga_model_log`)
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>training_run</th>
      <th>iteration</th>
      <th>loss</th>
      <th>eval_loss</th>
      <th>duration_ms</th>
      <th>learning_rate</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0</td>
      <td>1</td>
      <td>0.690266</td>
      <td>0.690282</td>
      <td>4934</td>
      <td>0.4</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0</td>
      <td>0</td>
      <td>0.692706</td>
      <td>0.692721</td>
      <td>3718</td>
      <td>0.2</td>
    </tr>
  </tbody>
</table>
</div>



# モデルの評価(`ML.EVALUATE`、`ML.ROC_CURVE`、`ML.CONFUSION_MATRIX`)

作成したモデルの性能を確認するための関数です。本来ならば、BQコンソール上からGUIで確認したほうがきれいに可視化されており、そちらで見たほうがわかりやすいのですが、今回jupyterで完結させるため、あえてクエリを実行して確認します

## ML.EVALUATE
基本的なモデルの性能の評価値を確認します。どのアルゴリズム(ただしTensorFlowを除く)で作成したモデルでも実行可能です(アルゴリズムごとに項目は変わります)


```sql
%%bigquery  --project $PROJECT 
SELECT *
FROM ML.EVALUATE(MODEL google_analytics.ga_model_log)
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>precision</th>
      <th>recall</th>
      <th>accuracy</th>
      <th>f1_score</th>
      <th>log_loss</th>
      <th>roc_auc</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.03053</td>
      <td>0.687421</td>
      <td>0.531938</td>
      <td>0.058464</td>
      <td>0.690282</td>
      <td>0.661142</td>
    </tr>
  </tbody>
</table>
</div>



## ML.ROC_CURVE
その名の通り、ROCカーブを確認できます。二値分類のロジスティック回帰のみ実行可能です。可視化を行うため、返り値を`pandas.DataFrame`で受け取ります(受け取り方は、前々記事参照)


```sql
%%bigquery  roc --project $PROJECT 
SELECT *
FROM ML.ROC_CURVE(MODEL `google_analytics.ga_model_log` )
```


```python
roc.head()
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>threshold</th>
      <th>recall</th>
      <th>false_positive_rate</th>
      <th>true_positives</th>
      <th>false_positives</th>
      <th>true_negatives</th>
      <th>false_negatives</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0.780942</td>
      <td>0.001271</td>
      <td>0.000000</td>
      <td>1</td>
      <td>0</td>
      <td>36441</td>
      <td>786</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.594805</td>
      <td>0.035578</td>
      <td>0.009632</td>
      <td>28</td>
      <td>351</td>
      <td>36090</td>
      <td>759</td>
    </tr>
    <tr>
      <th>2</th>
      <td>0.565863</td>
      <td>0.052097</td>
      <td>0.019703</td>
      <td>41</td>
      <td>718</td>
      <td>35723</td>
      <td>746</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0.552784</td>
      <td>0.063532</td>
      <td>0.029500</td>
      <td>50</td>
      <td>1075</td>
      <td>35366</td>
      <td>737</td>
    </tr>
    <tr>
      <th>4</th>
      <td>0.543293</td>
      <td>0.076239</td>
      <td>0.039598</td>
      <td>60</td>
      <td>1443</td>
      <td>34998</td>
      <td>727</td>
    </tr>
  </tbody>
</table>
</div>



このままではよくわからないので、`matplotlib`で可視化します


```python
import matplotlib.pyplot as plt
roc.plot(x='false_positives', y='true_positives', label='ROC')
plt.show()
```

![output_26_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/119585/2610fabe-a285-3704-7f1d-6db21a7f3944.png)


## ML.CONFUSION_MATRIX
その名の通り、混同行列の形で評価値を確認します。(多分類含む)ロジスティック回帰のみ実行可能です


```sql
%%bigquery --project $PROJECT 
SELECT *
FROM ML.CONFUSION_MATRIX(MODEL `google_analytics.ga_model_log` )
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>expected_label</th>
      <th>_0</th>
      <th>_1</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>0</td>
      <td>19262</td>
      <td>17179</td>
    </tr>
    <tr>
      <th>1</th>
      <td>1</td>
      <td>246</td>
      <td>541</td>
    </tr>
  </tbody>
</table>
</div>



# 構築したモデルで予測(`ML.PREDICT`)
構築したモデルで予測を行います


```sql
%%bigquery  --project $PROJECT 
SELECT 
    predicted_isClick_probs,
    predicted_isClick,
    isClick
FROM ML.PREDICT(
    MODEL `google_analytics.ga_model_log`,
    -- 予測を行うためのデータセットを読み込む(今回は学習に使ったものを流用)
    (
    SELECT *
    FROM `google_analytics.ga_dataset_feature` 
    -- 適当にサンプリング
    WHERE RAND() < 0.001
    LIMIT 5
    ),
    -- positiveと判定する閾値を0.55以上とする(デフォルト0.5)
    -- 二値分類のロジスティック回帰のみ適用可能
    STRUCT(0.55 AS threshold))
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>predicted_isClick_probs</th>
      <th>predicted_isClick</th>
      <th>isClick</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>[{'label': 1, 'prob': 0.6119019220195546}, {'l...</td>
      <td>1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>[{'label': 1, 'prob': 0.5540411720040493}, {'l...</td>
      <td>1</td>
      <td>0</td>
    </tr>
    <tr>
      <th>2</th>
      <td>[{'label': 1, 'prob': 0.5093257989158975}, {'l...</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th>3</th>
      <td>[{'label': 1, 'prob': 0.5032699831732143}, {'l...</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th>4</th>
      <td>[{'label': 1, 'prob': 0.4976015874326989}, {'l...</td>
      <td>0</td>
      <td>0</td>
    </tr>
  </tbody>
</table>
</div>



`predicted_isClick_probs`の中には以下のような`STRUCT`を持った配列が入っており、それぞれのラベルを取るときの予測値が入っています  
今回は不均衡データを是正する関係で、予測値は普通の値よりも高く出ていることに注意してください

```
[{'label': 1, 'prob': 0.4582647505087995},
 {'label': 0, 'prob': 0.5417352494912004}]
```

# モデルの重み値の確認(`ML.WEIGHTS`)
重み値の大きさを出力します


```sql
%%bigquery  --project $PROJECT 
WITH weights AS(
SELECT * 
FROM
ML.WEIGHTS(MODEL `google_analytics.ga_model_log`))

SELECT 
    category_weight.category,
    category_weight.weight
FROM weights, UNNEST(weights.category_weights) AS  category_weight LIMIT 5
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>category</th>
      <th>weight</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>Haiti_10</td>
      <td>-0.149425</td>
    </tr>
    <tr>
      <th>1</th>
      <td>Georgia_9</td>
      <td>-0.149508</td>
    </tr>
    <tr>
      <th>2</th>
      <td>Denmark_8</td>
      <td>-0.151037</td>
    </tr>
    <tr>
      <th>3</th>
      <td>United States_10</td>
      <td>0.031624</td>
    </tr>
    <tr>
      <th>4</th>
      <td>United States_1</td>
      <td>-0.100186</td>
    </tr>
  </tbody>
</table>
</div>



# モデルで使用したの特徴量の確認(`ML.FEATURE_INFO`)
使用した特徴量の統計的な情報を確認できます。イメージとしては`pandas.DataFrame.describe()`のような感じです


```sql
%%bigquery  --project $PROJECT 
SELECT *
FROM ML.FEATURE_INFO(MODEL `google_analytics.ga_model_log`)
```




<div>
<style scoped>
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>input</th>
      <th>min</th>
      <th>max</th>
      <th>mean</th>
      <th>median</th>
      <th>stddev</th>
      <th>category_count</th>
      <th>null_count</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>countryHour_country_hour</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>229</td>
      <td>0</td>
    </tr>
    <tr>
      <th>1</th>
      <td>browserMobile_browser_isMobile</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>13</td>
      <td>0</td>
    </tr>
    <tr>
      <th>2</th>
      <td>priceBin</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>None</td>
      <td>7</td>
      <td>0</td>
    </tr>
  </tbody>
</table>
</div>



# まとめ
BQMLで実行できるほぼすべての関数を実行してまとめました　　
BQMLでは、SQLを土台としているため、学習コストが低いことと、本来やらなければ行けない前処理やパラメータの調整など、機械学習になれていない人が本来はまりそうな落とし穴を『よしなに』自動で行ってくれるおかげで、非常に扱いやすいと思いました  
同時に、細かいパラメータの調整や、モデルや特徴量の概要について確認できる関数が用意されているおかげで、モデルをブラッシュアップしてある程度までは精度を向上させることができると思います  
まだ、基本的なアルゴリズムしかありませんが、今後より増えていくことは確実なので、さまざまなプロダクトで使えるようになることを期待します


# 参考文献
- [BQML公式ドキュメント](https://cloud.google.com/bigquery-ml/docs/reference/?hl=ja)

- [スケーラブルデータサイエンス データエンジニアのための実践Google Cloud Platform](https://www.amazon.co.jp/%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%A9%E3%83%96%E3%83%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%A8%E3%83%B3%E3%82%B9-%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E5%AE%9F%E8%B7%B5Google-Platform-Valliappa-Lakshmanan-ebook/dp/B07R39RLSQ/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%A9%E3%83%96%E3%83%AB+GCP&qid=1563756392&s=digital-text&sr=1-1)
    - bqのTech Leadである[Valliappa Lakshmanan](https://www.oreilly.com/pub/au/7304)さんの本。日本語版が最近出た
    
- [Google BigQuery: The Definitive Guide](https://www.amazon.com/Google-BigQuery-Definitive-Warehousing-Analytics/dp/1492044466)
    - 同じくValliappa Lakshmananさんの本。ただし、発売は来年

- [ビッグデータ分析・活用のためのSQLレシピ](https://www.amazon.co.jp/%E3%83%93%E3%83%83%E3%82%B0%E3%83%87%E3%83%BC%E3%82%BF%E5%88%86%E6%9E%90%E3%83%BB%E6%B4%BB%E7%94%A8%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AESQL%E3%83%AC%E3%82%B7%E3%83%94-%E5%8A%A0%E5%B5%9C-%E9%95%B7%E9%96%80/dp/4839961263/ref=sr_1_4?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&keywords=sql&qid=1563756274&s=books&sr=1-4)
    - ここまでやるかというレベルのSQLの黒魔術がいろいろのってます