BigQuery ML で単語をクラスタリングしてみる

Data Science

2020.3.12

Topics

こんにちは。データサイエンスチームの t2sy です。
Google BigQuery は、Google が提供する高スケーラビリティでコスト効率に優れたサーバーレス型のクラウド データウェアハウス (DWH) です。BigQuery ML を使用すると標準 SQL クエリを用いて機械学習モデルを作成・実行できます。

BigQuery ML は2020年3月現在、以下のアルゴリズムをサポートしています。

  • 線形回帰
  • 二項ロジスティック回帰 (二値分類)
  • 多項ロジスティック回帰 (多値分類)
  • k-means

また、訓練済みの TensorFlow モデルを Google Cloud Storage (GCS) から BigQuery にインポートし実行することもできます。

この記事では、BigQuery ML の k-means を用いて GloVe の事前学習済み単語ベクトルをクラスタリングしてみます。BigQuery を操作する方法は Cloud Console の Web UI、bq コマンドラインツール、REST API、クライアントライブラリの4つがあります。この記事では、bq コマンドラインツールで BigQuery とやり取りを行います。

今回、使用する Google Cloud Platform (GCP) のサービスは GCS と BigQuery です。

全体の流れは以下です。

  1. データの取得
  2. データセットとテーブルの作成
  3. データの読み込み
  4. モデルの作成
  5. クラスタリング結果の確認

BigQuery はクエリとストレージに対して課金が発生しますが、特定の上限までの無料枠があります。詳しくは、BigQuery の料金をご覧下さい。また、BigQuery ML でモデルを作成して使用すると、モデルのトレーニングで使用したデータ量とデータに実行したクエリに基づいて料金が発生します。BigQuery ML の料金については、BigQuery ML の料金をご覧下さい。

データの取得

今回は、Wikipedia 2014 + Gigaword 5 データセットを用いて訓練された GloVe の単語ベクトルを用います。Wikipedia 2014 + Gigaword 5 (共に英語) には60億のトークンと40万の語彙が含まれます。また、事前に単語は小文字 (lowercased) に統一化されています。

最初に、GloVe (Wikipedia 2014 + Gigaword 5) をダウンロードし、GCS を操作するためのコマンドラインツールである gsutil を使用し GCS にアップロードします。

GCP のコンソールを開き、Cloud Shell を起動します。GloVe (Wikipedia 2014 + Gigaword 5) をダウンロードします。

$ wget http://nlp.stanford.edu/data/glove.6B.zip

$ md5sum glove.6B.zip
056ea991adb4740ac6bf1b6d9b50408b  glove.6B.zip

$ unzip -t glove.6B.zip
Archive:  glove.6B.zip
    testing: glove.6B.50d.txt         OK
    testing: glove.6B.100d.txt        OK
    testing: glove.6B.200d.txt        OK
    testing: glove.6B.300d.txt        OK
No errors detected in compressed data of glove.6B.zip.

ダウンロードした glove.6B.zip には 50次元、100次元、200次元、300次元の事前学習済み単語ベクトルが含まれています。今回は 50 次元の事前学習済み単語ベクトルを使います。

$ unzip glove.6B.zip glove.6B.50d.txt
Archive:  glove.6B.zip
  inflating: glove.6B.50d.txt

GCS から BigQuery へのデータのインポートは、次の形式をサポートしています。

  • CSV
  • JSON (改行区切り)
  • Avro
  • Parquet
  • ORC
  • Datastore エクスポート
  • Firestore エクスポート

展開した glove.6B.50d.txt はスペース区切りのファイルのため、CSV に変換します。また、正常に BigQuery にインポートするための便宜上、一部の文字を削除します。

$ cat glove.6B.50d.txt | tr -d "[,'\"]" | sed -e "s/ /,/g" > glove.6B.50d.csv

gsutil mb コマンドで GCS にバケットを作成します。

$ GCS_BUCKET_NAME="techblog-bq-ml-glove"
$ gsutil version
gsutil version: 4.48

$ gsutil mb gs://$GCS_BUCKET_NAME/
Creating gs://techblog-bq-ml-glove/...

$ gsutil ls
gs://techblog-bq-ml-glove/

gsutil cp コマンドで、glove.6B.50d.csv を作成したバケットにアップロードします。

$ gsutil -m cp glove.6B.50d.csv  gs://$GCS_BUCKET_NAME/

データセットとテーブルの作成

データを BigQuery に読み込む前に、データセットとテーブルを作成する必要があります。

  • データセット: テーブルとビューへのアクセスを整理して制御するために使用される最上位のコンテナ。テーブルやビューはデータセットに属する。
  • テーブル: 個々のレコードは行の形式にまとめられ、各レコードは列 (フィールド) で構成される。すべてのテーブルは、列名、データ型、その他の情報を記述するスキーマによって定義される。

bq コマンドラインツールを使用し BigQuery にデータセットとテーブルを作成します。

$ bq version
This is BigQuery CLI 2.0.54

bq mk コマンドで dataset フラグを指定し、データセットを作成します。データセット名は techblog_glove としています。

$ BQ_DATASET_NAME="techblog_glove"
$ bq mk \
     --dataset \
     --default_table_expiration 36000 \
     --description "pre-trained word vectors of GLoVe." \
     $BQ_DATASET_NAME

次に、bq mk コマンドで table フラグを指定し、テーブルを作成します。 テーブル名は wiki_gigaword_6B_400K_50d としています。
テーブルのスキーマは作成時に JSON 形式またはインラインで指定することができますが、今回は BigQuery へのデータ読み込み時にスキーマを自動検出する機能を使用します。

$ BQ_TABLE_NAME="wiki_gigaword_6B_400K_50d"
$ bq mk \
    --table \
    --expiration 36000 \
    --description "Wikipedia 2014 + Gigaword (6B tokens, 400K vocab, 50d)" \
    --label organization:development \
    $BQ_DATASET_NAME.$BQ_TABLE_NAME

データの読み込み

次に、bq load コマンドを用いて GCS 上にアップロードした CSV データを BigQuery のテーブルに読み込みます。autodetect フラグを指定し、スキーマの自動検出を有効化します。

$ bq load \
    --source_format=CSV \
    --autodetect \
    $BQ_DATASET_NAME.$BQ_TABLE_NAME \
    gs://$GCS_BUCKET_NAME/glove.6B.50d.csv

bq show コマンドを用いて、テーブルのスキーマを確認します。 string_field_0 列が単語名、double_field_1 列から double_field_50 列が単語ベクトルです。

$ bq show \
    $BQ_DATASET_NAME.$BQ_TABLE_NAME
Table datascience-bigquery-exp:techblog_glove.wiki_gigaword_6B_400K_50d
   Last modified             Schema             Total Rows   Total Bytes     Expiration      Time Partitioning   Clustered Fields            Labels
 ----------------- --------------------------- ------------ ------------- ----------------- ------------------- ------------------ --------------------------
  03 Mar 20:25:22   |- string_field_0: string   400008       163751385     04 Mar 05:14:38                                          organization:development
                    |- double_field_1: float
                    |- double_field_2: float
                    |- double_field_3: float
                    ...
                    |- double_field_48: float
                    |- double_field_49: float
                    |- double_field_50: float

モデルの作成

今回は、クラスタリング手法のひとつである k-means を用いて 50 次元の単語ベクトルをクラスタリングしてみます。

モデルの作成は、CREATE MODEL ステートメントを使用します。モデルは BigQuery データセット内に作られます。
OPTIONS にモデルのオプションを指定します。アルゴリズムを指定する model_type は必須です。今回は kmeans を指定します。
k-means のクラスタ数を指定する num_clusters は、広範な分野の知識を持つ Web 百科事典である Wikipedia を含むコーパスから単語ベクトルを学習していることを踏まえると、大きな値 (e.g. 1000) を試したいところですが、num_clusters の値範囲は 2 から 100 のため、今回は 100 を指定しました。
また、k-means の距離はユークリッド距離 (デフォルト値)、クラスタの初期化方法は kmeans++ を指定、早期終了と特徴の標準化を有効化します。
string_field_0 列は単語名のため、EXCEPT で使用する特徴から除いています。

CREATE OR REPLACE MODEL
  techblog_glove.wiki_gigaword_6B_400K_50d_clusters OPTIONS (model_type='kmeans',
    num_clusters=100,
    kmeans_init_method='kmeans++',
    early_stop=TRUE,
    standardize_features=TRUE) AS
SELECT
  * EXCEPT(string_field_0)
FROM
  `techblog_glove.wiki_gigaword_6B_400K_50d`

bq query コマンドでモデルを作成する SQL クエリを実行します。ジョブの完了には数分かかります。

$ bq query \
   --use_legacy_sql=false \
'CREATE OR REPLACE MODEL
  techblog_glove.wiki_gigaword_6B_400K_50d_clusters OPTIONS (model_type="kmeans",
    num_clusters=100,
    kmeans_init_method="kmeans++",
    early_stop=TRUE,
    standardize_features=TRUE) AS
SELECT
  * EXCEPT(string_field_0)
FROM
  `techblog_glove.wiki_gigaword_6B_400K_50d`'

モデルの作成中に特定の前処理を行いたい場合は、今回は使用していませんが TRANSFORM 句を使用することができます。TRANSFORM を使用すると予測と評価でも自動的に指定した前処理が適用されます。

ML.EVALUATE 関数を使用して、訓練時のモデルの評価指標を取得できます。指標はアルゴリズムによって異なり、k-means の場合は Davies-Bouldin インデックスと平均二乗距離の値が得られます。

  $ bq query \
  >  --use_legacy_sql=false \
  > 'SELECT
  >   *
  > FROM
  >   ML.EVALUATE(MODEL `techblog_glove.wiki_gigaword_6B_400K_50d_clusters`)'
  Waiting on bqjob_r6c495073648bda2_00000170b00a885c_1 ... (0s) Current status: DONE
  +----------------------+-----------------------+
  | davies_bouldin_index | mean_squared_distance |
  +----------------------+-----------------------+
  |    3.663048881391826 |     36.99838360729689 |
  +----------------------+-----------------------+

クラスタリング結果の確認

ML.PREDICT 関数を使用してクラスタリング結果をテーブルに格納します。

$ BQ_RESULT_TABLE_NAME="wiki_gigaword_6B_400K_50d_clusters_result"
$ bq query \
   --destination_table $BQ_DATASET_NAME.$BQ_RESULT_TABLE_NAME \
   --use_legacy_sql=false \
'SELECT
  *
FROM
  ML.PREDICT(MODEL `techblog_glove.wiki_gigaword_6B_400K_50d_clusters`,
    (
    SELECT
      *
    FROM
      `techblog_glove.wiki_gigaword_6B_400K_50d`))'

ML.PREDICT 関数の出力の列はアルゴリズムによって異なり、k-means の場合、centroid_id と nearest_centroids_distance の2つの列となります。nearest_centroids_distance には、最も近い k 個のクラスタまでの距離が含まれます。ここで k は num_clusters または 5 の小さい方の値です。

最後に、米国IT企業がどのクラスタに属するかを調べてみます。
クラスタリング結果のテーブルに対する SELECT 文の WHERE 句に米国IT企業の社名を指定し SQL クエリを実行します。

  $ bq query \
  >   --use_legacy_sql=false \
  > 'SELECT
  >   CENTROID_ID,
  >   string_field_0
  > FROM
  >   `techblog_glove.wiki_gigaword_6B_400K_50d_clusters_result`
  > WHERE
  >   string_field_0 IN ("google",
  >     "apple",
  >     "facebook",
  >     "microsoft",
  >     "amazon")'
  Waiting on bqjob_r48dad22efccea83_00000170a0b94e7e_1 ... (0s) Current status: DONE
  +-------------+----------------+
  | CENTROID_ID | string_field_0 |
  +-------------+----------------+
  |          26 | amazon         |
  |          30 | apple          |
  |          30 | google         |
  |          30 | facebook       |
  |          30 | microsoft      |
  +-------------+----------------+

今回のモデルでは、米国IT企業の5社中4社が同一のクラスタに割り当てられていることが確認できます。
k-means はセントロイドの初期値が異なると、異なるクラスタを形成する場合が多くあります。BigQuery ML の k-means では、kmeans_init_method を custom とし、kmeans_init_col にセントロイドの初期値とする行を TRUE とした列を指定することで再現性を確保する方法が用意されています。

おわりに

この記事では、Google のクラウド DWH である BigQuery 上で、機械学習モデルを作成・実行する BigQuery ML の機能を確認しました。例として、BigQuery ML の k-means を用いて GloVe の事前学習済み単語ベクトルをクラスタリングしてみました。BigQuery ML を用いることで、スケーラビリティを備えた BigQuery 上で標準 SQL を使い機械学習モデルを迅速に作成・実行することができます。

参考文献

[1] bq コマンドライン ツール リファレンス

t2sy

2016年11月、データサイエンティストとして中途入社。時系列分析や異常検知、情報推薦に特に興味があります。クロスバイク、映画鑑賞、猫が好き。

Recommends

こちらもおすすめ

Special Topics

注目記事はこちら