BigQuery MLを使ってkmeansでクラスタリングしてみる

# 概要
2019年4月10日に、BigQueryで、k-meansモデルがサポートされ、データのクラスタリングが出来るようになりました。
[参考] [BigQuery ML リリースノート](https://cloud.google.com/bigquery-ml/docs/release-notes)

<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e5/KMeans-Gaussian-data.svg/868px-KMeans-Gaussian-data.svg.png" width=50%>
[File:KMeans-Gaussian-data.svg] From Wikimedia Commons, the free media repository

そこでGoogleが提供しているチュートリアルをなぞる事で、何が出来るか確認したいと思います。

本記事は、チュートリアルの手順を省略しながら日本語訳してるだけです。
内容をちょっとだけ変えている部分と補足している部分があります。

kmeans チュートリアル - Google
https://cloud.google.com/bigquery-ml/docs/kmeans-tutorial

# 事前準備
[チュートリアル - Before you begin
](https://cloud.google.com/bigquery-ml/docs/kmeans-tutorial#before-you-begin)

1. GCPのプロジェクトを選択 or 作成してください。 ([リソースの管理](https://console.cloud.google.com/cloud-resource-manager))
1. GCPのプロジェクトの課金が有効になっている事を確認してください。([プロジェクトの請求設定の変更
](https://cloud.google.com/billing/docs/how-to/modify-project))
1. 新規プロジェクトではBigQueryは自動で有効になります。既存のプロジェクトの場合、BigQuery APIを有効にしてください。([APIを有効にする](https://console.cloud.google.com/flows/enableapi?apiid=bigquery))

# 対象データ
BigQueryのパブリックデータセットとして提供されている、ロンドンのレンタル自転車の利用履歴データを使っています。

https://console.cloud.google.com/marketplace/details/greater-london-authority/london-bicycles

テーブルとしては下記の２つが登場するので、詳しく理解したい方はテーブルの中を覗いてみてください。
- `bigquery-public-data.london_bicycles.cycle_hire`
- `bigquery-public-data.london_bicycles.cycle_stations`

# 手順
## 1. EUリージョンにデータセットを作る

[こちらの手順](https://cloud.google.com/bigquery/docs/datasets#bigquery_create_dataset-console)を元にデータセットを作成してください。

```
名前： bqml_tutorial
リージョン：EU
（パブリックデータがEUリージョンにあるので、EUリージョンで作成してください。）
```

## 2. パブリックデータからクラスタリング用のテーブルに整形する

```SQL
CREATE OR REPLACE TABLE `bqml_tutorial.data` AS
WITH
  hs AS (
  SELECT
    h.start_station_name AS station_name,
    h.duration,
    s.bikes_count,
    ST_DISTANCE(ST_GEOGPOINT(s.longitude, s.latitude),
    ST_GEOGPOINT(-0.1, 51.5))/1000 AS distance_from_city_center
  FROM
    `bigquery-public-data.london_bicycles.cycle_hire` AS h
  JOIN
    `bigquery-public-data.london_bicycles.cycle_stations` AS s
  ON
    h.start_station_id = s.id
  WHERE
    h.start_date BETWEEN CAST('2015-01-01 00:00:00' AS TIMESTAMP)
    AND CAST('2016-01-01 00:00:00' AS TIMESTAMP) ),
  stationstats AS (
  SELECT
    station_name,
    AVG(duration) AS duration,
    COUNT(duration) AS num_trips,
    MAX(bikes_count) AS bikes_count,
    MAX(distance_from_city_center) AS distance_from_city_center
  FROM
    hs
  GROUP BY
    station_name )
SELECT
  *
FROM
  stationstats
ORDER BY
  distance_from_city_center ASC
```

実行すると、新しくテーブルが作成されます。

```SQL
SELECT * FROM `bqml_tutorial.data`
```

内容を参照すると下記のようなデータが見えるはずです。

![cluster-dataset-rows](https://cloud.google.com/bigquery-ml/images/cluster-dataset-rows.png)

### 表の見方
※理解が間違ってたらご指摘ください。

| フィールド名 | 説明 |
|:--|:--|
| station_name | ステーション名 |
| duration | レンタル時間 |
| num_trips | レンタル回数 |
| bikes_count | 自転車ラックの数 |
| distance_from_city_center | シティセンターからの距離 |

## 3. k-meansモデルを作成する

下記のSQLを実行します。50秒くらい待つとモデルが作成されます。

```SQL
CREATE OR REPLACE MODEL
  bqml_tutorial.london_station_clusters
OPTIONS
  (model_type='kmeans',
    num_clusters=4,
    standardize_features = TRUE) AS
SELECT * FROM `bqml_tutorial.data`
```

出来上がったモデルを参照すると下記のような情報が確認できます。

![cluster-schema-info](https://cloud.google.com/bigquery-ml/images/cluster-schema-info.png)

## 4. ML.PREDICTを使って、ステーションをクラスタリングします

下記のSQLを実行します。クラスタリングは数秒で終わります。

```SQL
SELECT
  * EXCEPT(nearest_centroids_distance)
FROM
  ML.PREDICT(MODEL bqml_tutorial.london_station_clusters,
             (SELECT * FROM `bqml_tutorial.data`))
```

結果を見ると、下記のような情報が確認できます。


![kmeans-predict-results](https://cloud.google.com/bigquery-ml/images/kmeans-predict-results.png)

CENTROID_IDが、クラスタリングされた結果のクラスタIDとなります。

## 5. モデルを可視化する

```SQL
WITH
  T AS (
  SELECT
    centroid_id,
    ARRAY_AGG(STRUCT(feature AS name, ROUND(numerical_value,1) AS value) ORDER BY centroid_id) AS cluster
  FROM
    ML.CENTROIDS(MODEL bqml_tutorial.london_station_clusters)
  GROUP BY
    centroid_id
  )
  SELECT CONCAT('Cluster#', CAST(centroid_id AS STRING)) AS centroid,
  (SELECT value FROM UNNEST(cluster) WHERE name = 'duration') AS duration,
  (SELECT value FROM UNNEST(cluster) WHERE name = 'num_trips') AS num_trips,
  (SELECT value FROM UNNEST(cluster) WHERE name = 'bikes_count') AS bikes_count,
  (SELECT value FROM UNNEST(cluster) WHERE name = 'distance_from_city_center') AS distance_from_city_center
FROM
  T
ORDER BY
  centroid
```

結果を見ると下記のような情報が確認できます。

![pivot-results](https://cloud.google.com/bigquery-ml/images/pivot-results.png)

データスタジオで可視化するとこう見えます。
（ホントは、クラスタ毎に色が分かれている方が見やすいと思うけど。。）

![final-chart](https://cloud.google.com/bigquery-ml/images/final-chart.png)

## 6. データドリブンな意思決定を行う（？）

可視化すると下記のようなことが読み取れる・・・らしいです。チュートリアル曰く。
（ここはチュートリアルをGoogle翻訳して、訂正している形です。）

```
クラスター＃4は、比較的少数の旅行、市内中心部からの中程度の距離、および長い乗り物を示しています。
これは、クラスタ＃4のステーションが忙しくない郊外のステーションであることを示しています。

クラスター＃1は、市内中心部からの距離が遠く、長距離走っているが、旅行は少ない。
これは、これらが他の、あまり忙しくない郊外の駅であることを示しています。

クラスター＃2は、より多くの旅行と市内中心部へのより近い距離を示しています。
これは、これらの駅が比較的忙しい都市の駅であることを示しています。

クラスター＃3は、市内中心部への近距離と非常に多数の旅行を示しています。
これは、これらの駅が非常に忙しい都市の駅であることを示しています。
```

このような考察が得られると、何かしらの決定の根拠を示すために「データ」で説明することができます。

```
新しいタイプの「鍵」を試してみる必要があるとします。
この実験の対象として、どのステーションのクラスターを選択しますか？
クラスタ＃4のステーションは、A / Bテストをできるくらい多数の自転車があるステーションであるため、論理的な選択のように見えます。

あなたがレーシングバイクをいくつかのステーションに置きたいと仮定しましょう。
どの駅を選ぶべきですか？
クラスタ＃1は、市内中心部から最も離れた場所にあるステーションのグループです。
これらはおそらくレーシングバイクの候補です。

あなたは追加の資金を調達し、あなたのステーションに自転車を増やすことができます。
追加の自転車をどの駅に置くべきですか？
クラスター＃2は、自転車の数が最も少なく、旅行の数が2番目に多いです。
そのため、これらのステーションは候補になる可能性があります。
```

# BigQueryMLは簡単！
BigQueryMLを使えば、10行にも満たないSQLで、kmeansを試すことが出来ました。
簡単にデータを分析してみたいなら、Pythonでコードを書くより簡単に機械学習が試せます。

皆さんもBigQueryMLで快適な機械学習ライフを！

# 最後に
## 現時点で使えるもの
現時点（2019年6月）では、下記のモデルが使えるようです。
 * Linear regression - 線形回帰
 * Binary logistic regression - 2値分類
 * Multiclass logistic regression - 多値分類
 * K-means clustering - K-meansクラスタリング

# 今後使えるようになりそうなもの
2019年4月のGoogle Cloud Nextでは、下記のようなものが登場されていると予告されているそうです。
[New] 強調フィルタリング (Matrix factorization) の サポート (Alpha)
[New] TensorFlow を用いたディープニューラルネットワークのサポート (Alpha)
[New] BigQuery 上でのプレディクション用のTensorFlow モデルのインポート (Alpha)
[New] Feature pre-processing functions (Alpha)

[参考] [Google Cloud Next 2019 in SF , BigQuery 関連発表まとめ](https://medium.com/google-cloud-jp/bigquery-whats-announced-in-next19-8e09059e830d)