DataSpider Servista で機械学習プロジェクトを作成 with GCP

# はじめに
[Goole Cloud Next'22](https://cloud.withgoogle.com/next) でアナウンスのあった BigQuery で[非構造データ](https://cloud.google.com/blog/products/data-analytics/how-to-manage-and-process-unstructured-data-in-bigquery?hl=en)をサポートすることに触発され、DataSpider Servista からBiqQuery ML (以下 BQML と表記する)を操作してみました。本記事では DataSpider Servista と BigQuery を活用して、機械学習プロジェクトを作成した内容をまとめています。本記事を実行するためには主に以下のプラットフォーム、サービスを利用します。

* [DataSpider Servista](https://www.hulft.com/software/dataspider): データ連携ツール
  * Google BigQuery アダプタ(以下 BigQuery アダプタと表記する): BigQuery を操作するためのアダプタ
  * Google Cloud Storage アダプタ(以下 GCS アダプタと表記する): Google Cloud Storage を操作するためのアダプタ
* [Google Colaboratory](https://colab.research.google.com): ブラウザから Python を実行できるサービス
* [Google Cloud Platform](https://cloud.google.com): Google が提供するクラウドコンピューティングサービス

なお、本記事では DataSpider Servista のプロジェクトの新規作成、スクリプトの新規作成などの基礎的な内容は説明しません。

# DataSpider Servista
DataSpider Servista（以下 DSS と表記する） はノンプログラミング・ノーコードでシステム間のデータやアプリケーションを連携する EAI ツールです。 DSS にはデータベース、アプリケーション、また主要クラウドサービスなどに対応した50種類以上のアダプタが存在し、機械学習に関連したアダプタは以下のとおりです。

* DataRobot アダプタ
* Azure Machine Learning アダプタ

今回は BigQuery アダプタのオペレーション`検索系SQL実行`より BQML を動かすことで、機械学習プロジェクトの作成を試みました。プロジェクト完成までの流れは以下のとおりです。

* グローバルリソースの作成
* モデルの学習
* モデルのインポート
* データの前処理
* 前処理後のデータ書き込み
* 予測

# グローバルリソースの作成
まず初めに `BigQuery アダプタ`および`GCS アダプタ`で使用するグローバルリソースを作成します。そのためには GCP で[サービスアカウントの作成](https://console.cloud.google.com/iam-admin/serviceaccounts)、また対象アカウントの認証キーをJSON形式で作成する必要があります。認証キーを作成したら、対象のキーをグローバルリソース`Google BigQuery接続設定`および`Google Cloud Storage接続設定`に設定し、接続テストを実行して、接続に成功することを確認します。
![グローバルリソースの作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/a9212623-6a92-32a3-8803-25c9055e6722.png)

# モデルの学習
本項目には[検索系SQL実行オペレーションでモデル学習](https://qiita.com/kosuke21/private/67f6570ac3ce0c8c9936#%E6%A4%9C%E7%B4%A2%E7%B3%BBsql%E5%AE%9F%E8%A1%8C%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A7%E3%83%A2%E3%83%87%E3%83%AB%E5%AD%A6%E7%BF%92)と [Google Colaboratory でモデル学習](https://qiita.com/kosuke21/private/67f6570ac3ce0c8c9936#google-colaboratory-%E3%81%A7%E3%83%A2%E3%83%87%E3%83%AB%E5%AD%A6%E7%BF%92)がありますが、`検索系SQL実行オペレーションでモデル学習`ではモデルの学習はできないため、`検索系SQL実行オペレーションでモデル学習`の内容を実施する必要はありません。

## 検索系SQL実行オペレーションでモデル学習
1. [グローバルリソースの作成](https://qiita.com/kosuke21/private/67f6570ac3ce0c8c9936#%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90)が完了したら、続いて DSS で新規プロジェクトおよびスクリプトを作成し、 BigQuery アダプタの`検索系SQL実行`オペレーションを用いてモデルの作成を試みます。検索系SQL実行オペレーションのプロパティ`接続先`に対象のグローバルリソースを設定します。

1. プロパティアクション`プロジェクトID一覧の更新`を押下し、プロジェクトIDを更新します。このとき以下のエラーが発生する場合は [Cloud Resource Manager API](https://console.cloud.google.com/apis/library/cloudresourcemanager.googleapis.com) を有効にします。

    ```
    com.appresso.ds.dp.modules.adapter.gcp.bigquery.service.ResourceManagerServiceException: Google Resource Manager対する操作が失敗しました。HTTPステータスコード : 403, エラーコード : accessNotConfigured, メッセージ : Cloud Resource Manager API has not been used in project <Your Project Number> before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/cloudresourcemanager.googleapis.com/overview?project=<Your Project Number> then retry. (以下省略)
    ```

1. BQML でモデルを作成するために、プロパティ`SQL文`にSQLを記述します。今回は BQML のチュートリアル[分類モデルの作成](https://cloud.google.com/bigquery-ml/docs/linear-regression-tutorial)を引用することとし、以下のSQLを設定します。 (チュートリアルの[ステップ3](https://cloud.google.com/bigquery-ml/docs/logistic-regression-prediction#step_four_create_a_logistic_regression_model)までを完了していることを前提)
    ```
    CREATE OR REPLACE MODEL
      `census.census_model`
    OPTIONS
      ( model_type='LOGISTIC_REG',
        auto_class_weights=TRUE,
        input_label_cols=['income_bracket']
      ) AS
    SELECT
      * EXCEPT(dataframe)
    FROM
      `census.input_view`
    WHERE
      dataframe = 'training'
    ```
1. プロパティ`保存先データセット名`及び`保存先テーブル名`については以下の画像のとおり存在しない値を設定します。
    ![検索系SQL実行_学習.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/a4b59b98-7256-891c-fff4-5046aa3734ed.png)

1. `検索系SQL実行`オペレーションの設定が完了したら、スクリプトキャンバスで`Start`、`検索系SQL実行`、`End`をプロセスフローで繋ぎ、スクリプトを実行します。スクリプトを実行すると以下のエラーが発生します。
    ![スクリプト実行エラー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/10c4cdd2-22e9-d1a8-8fbb-e34f4db7deae.png)


`検索系SQL実行`オペレーションではDDLを実行することはできないため、[Google Colaboratory でモデル学習](https://qiita.com/drafts/67f6570ac3ce0c8c9936#google-colaboratory-でモデル学習)でモデルを学習します。

## Google Colaboratory でモデル学習 
ここでは Google Colaboratory でモデルを学習し、本モデルを GCS へ保存します。そのため、事前に [GCS](https://console.cloud.google.com/storage/browser) にモデル保存用のバケットを作成する必要があります。[DecisionForests.ipynb](https://colab.research.google.com/drive/1sGeq11nD0826mC7Z6KePpPDGsRzue-uc?usp=sharing) にモデル作成およびモデルを GCS へ保存するためのサンプルコードを配置しました。本ノートブックを使用する場合は学習したモデルを保存するための GCP の Project ID と GCS のフォルダパスをノートブックに設定する必要があります。対象の値を設定後、ノートブックを上から実行すると対象のモデルを GCS に保存できます。

# BigQuery でモデルのインポート
BigQuery では TensorFlow で作成したモデルをインポートすることができます。[Google Colaboratory でモデル学習](https://qiita.com/kosuke21/private/67f6570ac3ce0c8c9936#google-colaboratory-%E3%81%A7%E3%83%A2%E3%83%87%E3%83%AB%E5%AD%A6%E7%BF%92)で作成した TensorFlow の決定木モデルを BigQuery にインポートするために以下の SQL を BigQuery で実行します。クエリが成功すると GCS に保存した決定木モデルが BigQuery のデータセットの中に保存されます。以下の SQL を実行するためには事前に`<YOUR_BQ_DATASET_NAME>`に該当するデータセットを BigQuery に作成する必要があリます。
```
CREATE OR REPLACE MODEL
  `<YOUR_BQ_DATASET_NAME>.<MODEL_NAME>`
OPTIONS
  ( MODEL_TYPE='TENSORFLOW',
    MODEL_PATH='<GCS_DIR_PATH>'
  )
```

# データの前処理
ここではモデル学習時に使用したデータを流用して、データの前処理を DSS で行います。[DecisionForests.ipynb](https://colab.research.google.com/drive/1sGeq11nD0826mC7Z6KePpPDGsRzue-uc?usp=sharing) で作成したモデルのカテゴリ変数についてはラベルエンコーディング (※モデルの精度は気にせず、扱う変数を減らすためにラベルエンコーディングを使用)を施しているため、DSS でも同様に入力するカテゴリ変数をラベルエンコーディングします。また、今回学習で使用した UC Irvine Machine Learning Repository の [Bank Marketing](https://archive-beta.ics.uci.edu/dataset/222/bank+marketing) には欠損値がないため、データの Null チェック、また欠損値が存在した場合の処置は考慮していません。[DecisionForests.ipynb](https://colab.research.google.com/drive/1sGeq11nD0826mC7Z6KePpPDGsRzue-uc?usp=sharing) を実行すると Google Corabolatory インスタンスに`/content/<yyyyMMdd>.csv`が保存されます。本ファイルを DataSpider Server に保存し、データの前処理を行います。DSS でラベルエンコーディングするために、コンポーネント`ドキュメントMapper`のロジック`スイッチ判定による出力の切り替え`を使用し、入力されるデータの文字列に応じてラベルエンコーディングするように設定しました。ドキュメントMapper 内のロジックは以下の画像のとおりです。

![ラベルエンコーディング.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/bc6bfe63-ab96-bbae-6592-3551a9102741.png)

データの前処理に関わる大まな流れは以下のとおりです。

1. オペレーション`CSVファイル読み取り`でDataSpider Serverから入力データ`<yyyyMMdd>.csv`を読み込みます。
1. コンポーネント`ドキュメントMapper`で入力データのラベルエンコーディングを行います。
1. オペレーション`CSVファイル書き込み`でDataSpider Serverに前処理後の入力データを書き込みます。

# 前処理後のデータ書き込み （GCS）
BQML を実行するためには入力データを BigQuery から操作できる場所に保存する必要があります。今回は BigQuery に入力データ用テーブル (データセット) を作成し、そこにデータを保存します。そのため、まずは前処理したデータを GCS へ保存します。DSS の GCS アダプタ、オペレーション`ファイル/フォルダ読み取り`を使用することで GCS にファイルを保存することができます。`ファイル/フォルダ読み取り`オペレーションのプロパティ `接続先`には[グローバルリソースの作成](https://qiita.com/kosuke21/private/67f6570ac3ce0c8c9936#%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90)で作成した`Google Cloud Storage接続設定`を設定します。また GCS に前処理されたデータを保存するためのバケット (必要があればフォルダも) を作成する必要があリましす。プロパティの設定は以下の画像のとおりです。
![GCSファイルフォルダ書込.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/fec83a3e-f90f-587b-bb7f-04143a96de7f.png)

# 前処理後のデータ書き込み (BigQuery)
予測する前の最後の手順として、GCS へ保存したデータを BigQuery のテーブルに書き込みます。 BigQuery アダプタのオペレーション`テーブルデータ入力`を使用することで、GCS に保存してるデータを BigQuery のテーブルに書き込めます。本操作を行うためには事前に書き込み先テーブルを作成 (定義) する必要があります。プロパティの設定は以下の画像のとおりです。
![BQテーブルデータ入力.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/088846b9-f557-5557-f3e6-132f1ab852b0.png)

# 予測
ここでは[前処理後のデータ書き込み (BigQuery)](https://qiita.com/kosuke21/private/67f6570ac3ce0c8c9936#%E5%89%8D%E5%87%A6%E7%90%86%E5%BE%8C%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF-bigquery)で保存したデータを決定木モデルに与えて予測を行います。予測の実行には BigQuery アダプタの検索系SQL実行オペレーションを使用します。検索系SQL実行のプロパティ`接続先`および`プロジェクトID`については[検索系SQL実行オペレーションでモデル学習](https://qiita.com/kosuke21/private/67f6570ac3ce0c8c9936#%E6%A4%9C%E7%B4%A2%E7%B3%BBsql%E5%AE%9F%E8%A1%8C%E3%82%AA%E3%83%9A%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A7%E3%83%A2%E3%83%87%E3%83%AB%E5%AD%A6%E7%BF%92)で設定する値と同じものを設定します。また、事前に予測結果を保存するための`保存先データセット`および`保存先テーブル`を BigQuery に作成する必要があリます。

本オペレーションでは以下のSQL文を実行します。
```
SELECT
  *
FROM
  ML.PREDICT (MODEL `<YOUR_BQ_DATASET_NAME>.<MODEL_NAME>`,
    (
    SELECT
      *
    FROM
      `<INPUT_DATASET>.<INPUT_TABLE>`
    )
  )
```

検索系SQL実行オペレーションの設定値は以下のとおりです。プロパティ設定後にスクリプトを実行すると、予測結果が BigQuery のテーブルに保存されます。
![検索系SQL実行_予測.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/d61f0725-8399-dde9-2974-453139a4eff5.png)

# スクリプト全体像
今回作成したプロジェクトの全体像は以下のとおりです。
* 機械学習 (プロジェクト)
  * 予測実行 (スクリプト)
    * コンポーネント`スクリプト呼び出し処理`: `前処理からBQ入力データ書込`を呼び出します。
    * コンポーネント`スクリプト呼び出し処理`: `予測`を呼び出します。

`予測実行`スクリプトの内容は以下の画像のとおりです。
![予測実行.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/dd43d841-7312-a825-31c1-abedce580556.png)

また`予測実行`のスクリプト呼び出し処理で実行される`前処理からBQ入力データ書込`および`予測`の内容は以下の画像のとおりです。 

__前処理からBQ入力データ書込__
![予測タスク.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/f0fdc880-b94c-a36a-2690-d1b01a9608ce.png)


__予測__
![予測タスク.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2998227/2bd78ef2-3759-ce6f-3818-f32818654352.png)

# 感想
1. DSS は異なるタイプのアプリケーション・サービスを連携することに特化したツールであると再認識しました。今回、主に GCP 関連アダプタを使用してプロジェクトを作成したが、単に機械学習のみを回す場合は GCP だけでやりたいことを実現できます。但し、社内システムやその他サービス間でのデータ連携を実現する場合は GCP のみで解決することは難しく、GCP で実現できたとしても GCP の知識を持っていること、またコーディングできることが前提になるように思えます。一方で、DSS は基本的にノーコードでデータおよびアプリケーションを連携できるため、システム間の連携を実現したい方にとっては導入しやすいツールになっています。
1. EAI ツールである DSS で強引に機械学習システムの運用を試みたが、`データの前処理`、`学習`、`予測`などの機械学習の各タスクをシームレスに行えないことが非常に辛いポイントでした。今回機械学習で使用した学習データ量は小さいものであるため、モデルの学習時間は非常に短いものでした。しかし、実際の機械学習ではより大きなデータセットを使用することになり、モデルの学習には数時間〜数日掛かるため、学習を実行するまでの作業をスムーズに行いたいものです。
1. 機械学習向けのデータの前処理は専用ツールで行なったほうがいい。今回コードを書かずにできるだけ DSS で機械学習プロジェクトを完結させることを念頭に作業を行いました。そのため、データの前処理のためにドキュメントMapper で多数のロジックを配置することになり、画面内がロジックとマッピングリンクで覆い尽くされ、スキーマとロジック間の関係性が理解しにくいものとなリました。BQML を動かすためにデータの前処理を行うのであれば、 [TRANSFORM 句](https://cloud.google.com/bigquery-ml/docs/bigqueryml-transform?hl=ja)を使用した方がより簡易に前処理を実施できます。TRANSFORM 句はモデルにデータを入力した際にデータの前処理を行うための定義となり、この定義はモデルの評価と予測時にも反映されます。