Azure Machine Learning で AutoML 使ってモデル構築→デプロイまでのハンズオン

# はじめに
[Azure Advent Calendar 2020](https://qiita.com/advent-calendar/2020/azure) の12日目のエントリーです！

[Azure Learn](https://docs.microsoft.com/ja-jp/learn/) を掘っていたら、手っ取り早く Azure Machine Learning と AutoML を試せる題材があったのでハンズオン形式でまとめました。

## ターゲット
- 非エンジニア・データ分析初心者
    - 機械学習難しそうだけどちょっと気になる
    - Azure ほとんど触ったことない
- データサイエンティスト
    - Azure Machine Learning の AutoML 機能が気になってる
    - どんな感じで構築したモデルのメトリクスを見れるのか知りたい

## スコープ

- Azure Machine Learning ワークスペース構築
- Notebook と Computing 設定
- 実データを用いて AutoML で回帰モデル構築
- モデルのデプロイ
- デプロイしたモデルのテスト

## 用意するもの
- Azure Machine Learning を利用可能な Azure Subscription

## 背景
データ分析プロジェクトはとにかくトライアンドエラーで進めていくことが多いと思います。ステップを列記するとこんな感じでしょうか？

- 課題定義
    - 実ビジネスで解消したい課題の整理
    - データ分析でのゴール設定
    - 仮説立案
- データ分析
    - データ収集・前処理
    - データ探索
    - アルゴリズム選定
    - モデル構築
    - 検証・チューニング
- 本番運用
    - モデル再評価スキームの策定
    - 機械学習パイプライン設計・構築
    - CI/CDパイプライン設計・構築

それぞれ円滑に進めるためには高い専門性が必要ですが、ハードルを下げるサービスが日進月歩で拡充しています。データサイエンティストの職人技だった一部領域を自動化できるようになったり、分析基盤が MLOpsといわれる領域もカバーするようになったり、などなど。

Azure でこの辺りの中核になっているサービスが、Azure Machine Learning であり、その機能である AutoML というわけです。






## Azure Machine Learning とは
- 機械学習モデルのトレーニング、デプロイ、自動化、管理、追跡までできるデータ分析環境
- 便利機能いろいろ
    - AutoML 機能でモデル作成 (= 本稿のスコープ)
    - VS Code 拡張機能使ってリソース管理/実装
    - Azure Machine Learning Designer でドラッグアンドドロップで ML パイプラインをデプロイ
    - コンソールから豊富に選べる Jupyter Notebook サンプル
- 様々な計算リソースを数分で利用、スケールアウトも簡単
- 各種オープンソースツールと連携
    - Scikit-learn, TensorFlow, PyTorch, Ray RLlib, Kubeflow, mlflow などなど
- 各種 Azure サービスとも連携
    - Azure Machine Learning Pipeline や Azure DevOps でモデルの改良や運用管理
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/92567457-3e3b-06e1-f43c-351637a90105.png)


## AutoML とは
- 前処理済みのデータから最適なMLモデルをいい感じに作成
- アルゴリズムの選定とハイパーパラメーターチューニングを自動化
- 一行もコード書かずにモデルを作成
    - とはいえパラメーターに関するちょっとした知識は必要
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/d5db8aba-e201-df57-25e3-cb7ee53dede3.png)


# Azure Machine Learning 初期設定
まずは AutoML 実行の手前、Dataset の作成までの手順です。

## ワークスペース作成
検索窓から Machine Learning を検索し、クリック。
![2020-11-30_16h24_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/90e54815-db5d-d566-5992-6f7bf3cff35e.png)

新規ワークスペースの追加をクリック
![2020-11-30_16h26_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/72304fd3-1de0-6873-2c0e-483f3f932ba4.png)

検証用なので新しくリソースグループを作ります。ワークスペース名を任意で設定したら、確認及び作成に進み、検証に成功しましたと出たら作成をクリックします
![2020-11-30_16h43_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/64d76dcf-065f-cbbc-1ac0-7bdfcb2eaa5d.png)

こちらの画面に移った後、ちょっと待ちます。デプロイ完了と出たらOKです
![2020-11-24_23h40_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/5340f654-ca70-5c24-1d0b-a921ce694c8a.png)


## Notebook とコンピューティングの作成
こちらがホーム画面。とりあえず Notebook を立ち上げてみます。
![2020-11-24_23h46_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/f1ecee03-c549-b254-44bb-7ca670a2d537.png)

名称とファイルタイプを選びます。
Notebook だけでなく普通のテキストファイルも書き込めるようです。混成チームでタスク共有したりするときはなんだかんだでマークダウン書くだけでも障壁になることもあるので便利そう。
![2020-11-24_23h47_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/21eb598a-b87f-44db-d39f-c0bedf1abcc9.png)

サンプルノートブックにはそのまま使えそうなものがたくさん。別タブで開かれるので気軽に情報を参照できます。
![2020-11-25_00h22_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/c22309f9-afb8-9a79-db48-859ceb3961ce.png)

コンピューティングリソースがないと出ます。つくりましょう。
![2020-11-24_23h48_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/9eef69e1-d202-833e-1fba-06d3de05072c.png)

VM の選択画面に映ります。検証用なのでコストのカラムをクリックして価格順にソート、一番安価なインスタンスを選択します。(一時間当たり 0.1 USD ってめちゃくちゃ安い...)
![2020-11-24_23h50_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/5083f1dc-153b-9cbd-9856-b30bbc921b91.png)

VS code 使用時には SSH アクセスを有効化する必要あり。今回は VNET インジェクションも不要なのでこの内容で作成をクリック
![2020-11-24_23h54_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/23505940-bbce-bff4-b744-87ef536b8a45.png)

しばらく待つとコンピューティング左側のアイコンが緑色になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/598c085b-505a-8a23-c9ae-f63fecf9f961.png)

もう普通に Notebook として使える感じです。言語は R も選べるみたいですね
![2020-11-25_00h15_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/02a2a09d-b1ec-8485-8be4-3e065e066b01.png)

備考：コンピューティングが停止している場合は以下からアクセスできます
![2020-11-25_11h04_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/3ca6631f-2a9c-f4ac-2ce6-a0d58368f186.png)



## データセットの作成
今回利用するのは気象情報が付与されたレンタルサイクルログデータのCSV。

- ワシントン D.C 周辺の自転車レンタルログ
- 気象データもマージ済みのCSV
- 自転車のレンタル数が予測対象です。
- https://aka.ms/bike-rentals
![2020-11-30_17h13_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/b8307440-6355-d719-6317-2b2bbc43a9f4.png)

整理されたデータです

| カラム名 | 概要 |
|---|---|
| day<br>| 月間通算日番号 |
| mnth<br>| 月番号 |
| year<br>| 年。2011 or 2012 |
| season<br>| 季節番号。春夏秋冬で4つ |
| holiday<br>| 祝日 or not |
| weekday<br>| 曜日。 0-6 の整数値|
| workingday<br>| 就業日 or not | 
| weathersit<br>| 天気。晴、曇り、雨で3つ |
| temp<br>| 気温指標 #1 (正規化済み) |
| atemp<br>| 気温指標 #2 (正規化済み) |
| hum<br>| 湿度指標 (正規化済み) |
| windspeed<br>| 風速指標 (正規化済み) | 
| rentals<br>| 自転車のレンタル数 (目的変数)|

データベース → 登録されたデータセット → データセットの作成 → Web ファイルと進みます。
![2020-11-25_10h45_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/733a6131-581e-0937-be0d-e99ea8724c9e.png)

以下パラメータを入力、次へをクリック。データの欄に以下 URL を貼り付け 
https://aka.ms/bike-rentals
![2020-11-25_10h47_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/cd9997ab-86b3-4c48-ef57-ddf5770027a9.png)

以下のように記載、次へをクリック。
![2020-11-25_10h50_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/fd795452-b7b8-baca-91c1-16ef6b0df0cf.png)

スキーマは自動類推されるみたいですね。one-hot encoding するカラムの指定とかも不要。右にスクロールするとプレビューを見られます。データセットに含む必要のないカラムがあれば除外し、次へをクリック、そのまま進めばデータセットの作成完了です。
![2020-11-25_10h52_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/e4e30491-07e7-2f58-7cfc-720b7ede8a14.png)




# AutoML
登録したデータセットを用いて AutoML でモデルを作成していきます。

## モデル作成手順
自動 ML → 新しい自動 ML の実行へと進む
![2020-11-25_10h58_23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/c7f2a043-2cf4-13f2-1e47-68da92e669e7.png)

先ほど作成したデータセットを選択、次へをクリック
![2020-11-25_11h01_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/7cc4aac7-4e7b-2dc2-d611-5591f473eff0.png)

実験名称と予測したい列を入力、新しいコンピューティングをクリック
![2020-11-25_11h19_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/678d8f48-7ac0-e633-5ad4-0d464bdd8088.png)

トレーニング用のクラスタの設定画面。最小ノード数を 0 にしておくとトレーニングジョブが実行中でない場合に課金が発生しないようになります。小さいデータセットなので最大ノード数は 1 に設定。完了したら次へをクリック
![2020-11-25_11h15_22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/bb1eb6a5-643b-d307-ab55-489e89718daf.png)

追加の構成設定を表示するをクリックし、評価指標や検証方法などを指定、保存。

![2020-11-25_12h39_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/6b4347a2-776c-3681-f7a5-cbb916a97fba.png)

画面遷移後、終了をクリックすると実験がスタートします
![2020-11-25_12h44_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/c36520c7-0bc2-47d3-0fba-4bc092fadcaa.png)


## 備考：回帰モデルの設定項目概要
設定項目の超概要です。さしあたり自分が利用するデータについて妥当性が判断できるくらいのざっくりとして知識があればいいんじゃないかなと思います (思いたい)

- プライマリメトリック
    - 自動的にモデルを作る際に指標とするパラメータのこと 
        - 予測と値の差がどのくらい離れているかの指標
        - 今回は正規化された平均平方二乗誤差 (nRMSE)を選択
    - AutoML の回帰で利用できるメトリック
        - R2 スコア
            - 一番直感的に理解しやすい指標。モデルの絶対評価指標としてよく使われる
            - 0 から 1 の値をとる
            - 自然に起こる確率をもとにしたモデルでは 0
            - 100% 予測と値が一致してれば 1
        - 正規化された平均絶対誤差 (nMAE)
            - 予測と値の絶対値の平均を正規化したもの
            - 回帰におけるもっとも基本的な指標
            - データに対するロバスト性が高い
            - 変化に対してモデルの挙動を安定させたいときに
        - 正規化された平均平方二乗誤差 (nRMSE)
            - 誤差の事情を平均して平方根をとったものを正規化したもの
            - 統計の世界ではこれが一般的らしい
            - 大きく予測値から外れた値に対して強い罰則
        - スピアマン相関
            - データに対して順位しか付けられないときに
    - 最適なモデルの説明
        - 各特徴量の影響度をみたいのでオンに

- ブロックされたアルゴリズム
    - 大規模データセット扱う場合、モデリングに時間がかかる処理を避けたい場合に
    - 出来るだけ多くのアルゴリズムで試したいので今回は特に設定しない
- 終了条件
    - トレーニングジョブ時間
        - トレーニング完了までに許容する時間の上限値
    - メトリック スコア閾値
        - 指定したプライマリメトリックに対して設定する閾値
        - この数値を上回ったら実験を自動終了してくれる
- 検証
    - K分割交差検証
        - サンプルデータを K 個に等分、そのうち一つをテストデータ、その他でトレーニング
        - テスト/トレーニングのペアを順繰りにセット、実行してモデルを検証
        - 全部のデータが1回テストされるので公平
        - データセットが意図的にパーティション分割されてる場合は注意が必要
    - モンテカルロ交差検証
        - データの一部をランダムに引っ張ってきてトレーニングセットに、残りをテストに
        - これを複数回やってモデルを検証
        - より偏ったモデルになる可能性も
    - トレーニングと検証の分割
        - 手動でデータ分割の割合をしてい
    - 自動
        - よしなにやってくれる



## 実行結果比較
以下は実験の一覧。各実験で生成された最適なモデルを比較できます。列の編集やグラフの追加でダッシュボードのカスタマイズできます。
![2020-11-25_14h31_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/55d0b667-6b8f-2089-5b2d-72795c50aa1a.png)

設定パラメータでの結果を比較するため、以下3パターンでテストしてみました。すべて実験名 mslearn-bike-rental で実行しています。

|  実行番号  |  コンピューティング  |  最大ノード数  |  ジョブ時間  | スコア閾値 | 結果-実行時間 | 結果-最適モデル | 結果-最適モデルのスコア値 |
| ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- |
|  #1  |  STANDARD_D1_V2  | 1 | 0.25 (15分) | 0.08 | 12分51秒 | MaxAbsScaler, LightGBM | 0.07989 |
|  #7  |  <font color="Red"> Standard_DS3_v2  | <font color="Red">10 | 0.25 (15分) | 0.08 | <font color="Red">7分2秒 | MaxAbsScaler, LightGBM | 0.07989 |
|  #27  |  Standard_DS3_v2  | 10 | 0.25 (15分) | <font color="Red">0.07 | 16分35秒 | <font color="Red">VotingEnsemble | <font color="Red">0.07623 |

 #1 は考えうる最小構成のコンピューティングで実行、#2 はデフォルト設定のコンピューティングリソースで実行した場合。15分のうちクラスタの立ち上げに数分かかることを考慮すると内部処理は相当早くなっているみたいです。

 #3 は #2 と同じコンピューティングでより精度目標を高く設定した場合。最適モデルが VotingEnsenmle になってるので、複数の学習済みモデルを作って多数決で推論結果を求めたモデルが最適のモデルでしたよ、ということになります。

### 実験 - 詳細
実験名をクリックするとこの画面に遷移します
![2020-12-02_23h15_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/e817e47d-fe8b-e3e3-80de-a72deb98f9e0.png)

### 実験 - データガードレール
どんな条件でクロスバリデーションや欠損値保管が実行されたかを確認できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/50b732f2-2e70-01a0-5342-c99fb34dd7c4.png)

### 実験 - モデル
テストされたすべてのモデルの比較ができます。それぞれのモデルのダウンロードも可能
![2020-12-02_22h54_09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/6347999a-7e7b-7efe-e738-e63aed6e17e2.png)

### モデル - 概要
特定のアルゴリズムをクリックするとこちらの画面に遷移します。各種メトリクスの確認できます。
![2020-12-02_22h59_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/0ff70058-b820-5977-d084-a65b9bb6a2b7.png)

### モデル - 特徴量重要度
- workingday
    - 重要度高いのはイメージ通り
    - レンタルバイクの用途は設置場所によって様々なはず。もっと背景を知りたいところ。
- temp や hum などの天候を表す指標はレンタル日から前の日との差分を指標としても面白いかも
- Year は説明変数とするのは不自然? 
- weathersit や season は取り扱いが難しそう…


![2020-12-02_23h02_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/de6c5be4-f7ba-dd6c-b30a-8db485ac4382.png)

### モデル - 残差ヒストグラム
- メトリックタブで確認できます
- 予測と値の誤差のばらつきですね
- 理想的にはゼロ付近で正規分布
- そうでない場合は何らかの規則性や分散の偏りをチェックして手立てを考える
- bin 数は変えられないみたいです (2020年12月)

![2020-11-25_14h45_46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/14b6237b-457d-9758-e99d-dc64b5db00cd.png)


### モデル - Predicted vs True
- これもメトリックタブ
- 上のグラフのX軸は実際の値、Y軸は予測値
- 理想的には緑の点線 (実際の値 = 予測値)
- 下は実際の値のヒストグラム
- これも bin 変えられない？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/9f7bc435-c545-5c95-199a-ae3c7bebdc8d.png)






# モデルのデプロイ
前項で作成した Voting Ensemble のモデルをデプロイします。
実験 → 実験名 → 実行ID → アルゴリズム名と進みます
![2020-11-25_15h41_27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/a8ead79e-0a61-b344-44e8-93fecd6fbd6d.png)

デプロイをクリック。運用ステージでは AKS でのデプロイが良いそうですが、テスト環境ということで ACI サービスをコンピューティングリソースに指定、認証を有効にし、デプロイをクリック
![2020-11-25_15h47_50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/123de9b7-50a3-e2c0-062d-68665bbac325.png)

エンドポイント → 使用と進み、REST エンドポイントと主キーを控えておきます。(デプロイ成功の表示からエンドポイントURLが表示されるまでタイムラグがあるので注意)
![2020-11-25_15h54_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/33e4961d-1c72-c1b4-bd98-1358335e1818.png)



# サービスのテスト
作成しておいた Notebook を開き、
![2020-11-25_16h25_58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/f723c45a-6ac9-d9db-384f-42ad715f46ab.png)

Notebook の1セル目に以下スクリプトを入力、セルが選択されている状態で ```shift + enter``` を押すとスクリプトを実行できます。

```python
endpoint = '{生成したREST エンドポイント名に置き換え}'
key = '{生成した主キーに置き換え}'

import json
import requests

# テスト用の5日間分の気象予報データ設定
x = [[1,1,2022,1,0,6,0,2,0.344167,0.363625,0.805833,0.160446],
    [2,1,2022,1,0,0,0,2,0.363478,0.353739,0.696087,0.248539],
    [3,1,2022,1,0,1,1,1,0.196364,0.189405,0.437273,0.248309],
    [4,1,2022,1,0,2,1,1,0.2,0.212122,0.590435,0.160296],
    [5,1,2022,1,0,3,1,1,0.226957,0.22927,0.436957,0.1869]]

# データフレームを JSON に変換
input_json = json.dumps({"data": x})

# リクエストの書式と認証方式を指定
headers = {"Content-Type":"application/json",
        "Authorization":"Bearer " + key}

# リクエスト送信
response = requests.post(endpoint, input_json, headers=headers)

# エンドポイントからレスポンスあったら結果を表示
if response.status_code == 200:
    y = json.loads(response.json())
    print("Predictions:")
    for i in range(len(x)):
        print (" Day: {}. Predicted rentals: {}".format(i+1, max(0, round(y["result"][i]))))
else:
    print(response)

```

以下の返り値を得られます。問題なくデプロイされているようですね。
> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/363781/1ceddee9-8339-3146-7434-90b65a11a84a.png)




# さいごに
デプロイまでコード一行も書かずに進められるのは驚きです。

1時間もあれば一通り試せるので是非。このままだと課金が発生してしまうので、エンドポイントの削除とNotebookに紐づくインスタンスの停止は忘れずに。

# 参照
[Azure Machine Learning のドキュメント](https://docs.microsoft.com/ja-jp/azure/machine-learning/)
[Azure Learn 記事：Azure Machine Learning で自動機械学習を使用する](https://docs.microsoft.com/ja-jp/learn/modules/use-automated-machine-learning/)
[利用データ：Capital Bikeshare](https://www.capitalbikeshare.com/system-data)
[Understanding Cross Validation](https://towardsdatascience.com/understanding-cross-validation-419dbd47e9bd)