【AzureML】自動車価格の予測チュートリアルをやってみた

MLStudioが2024年に廃止するようです。
https://docs.microsoft.com/en-us/azure/machine-learning/migrate-overview

> Support for Machine Learning Studio (classic) will end on 31 August 2024. We recommend you transition to Azure Machine Learning by that date.

これを機にほかのサービスへの移行を考えていたのですが、AzureMLにデザイナーという機能があり、MLStudioのようにブロックで操作できるようでした。
サンプルがいくつかあるので、いくつか試していこうと思います。
今回は[自動車価格の予測チュートリアル](https://docs.microsoft.com/ja-jp/azure/machine-learning/tutorial-designer-automobile-price-train-score)をやっていきたいと思います。

# ワークスペース作成
まず初めにAzureMLワークスペースを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/5bdcd8ec-628d-86ee-5e0f-abbbad466391.png)
リソースの作成からAzureMachineLeaningを検索します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/49dd171d-6715-abff-9dc7-0c2c98fc2418.png)
出てきたページの作成を押し、必要情報を入力して作成しましょう。
詳しい内容は[こちら](https://docs.microsoft.com/ja-jp/azure/machine-learning/how-to-manage-workspace?tabs=azure-portal)にあります。

ワークスペースが作成されると、このように表示されてると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/ed702bad-4f63-c80f-553d-8743cee599cb.png)
作成したワークスペースを開いてみるとスタジオの起動と真ん中に出てくるのでこちらをクリックして起動しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/7ad7340b-9250-15e2-de10-562b76c63952.png)

スタジオを起動するとこのようなページに移動すると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/943cde2b-4c0c-158d-92ab-dfb26c9df9c0.png)


# デザイナーでモデルを作る
今回はブロックでモデルを作成できるデザイナーという機能を利用していきたいと思います。
左のタブからデザイナーの項目をクリックして開いてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/b32423d8-98b3-71ff-5f5c-1b4d1150f69f.png)
このような画面になると思います。
新しいパイプラインの項目にはいくつかサンプルが並んでいます。
これらはすでにサンプルのブロックが組みあがっているものになります。
今回利用するAutomobile Price Predictionについてもすでに出来上がったものが提供されています。
今回は一通りの流れを確認したいので一から作っていきたいと思います。
空のパイプラインを作りたいので左にある+ブロック(使いやすい事前構築済み...)をクリックします。
開くとこのような画面になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/455b15cd-4c40-82c7-d1b7-674be3be2984.png)

MLStudioで見慣れた画面で安心しますね…
それではブロックを投入しながらモデルを構築していきます。

## サンプルデータの取得
まずはサンプルデータを持ってきます。
左の検索窓に「Automobile」と入れて検索します。
この時「データ」と「コンポーネント」タブがありますが、データのほうを選択しておきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/cbb76a51-ed75-ffe0-bede-b8cbcf5c6214.png)

このようにブロックが出てきます。これを右にドラッグ＆ドロップして配置します。
これでデータの準備ができました。データの内容を確認してみたいので配置したブロックを右クリックして「データを表示する」を押下して表示してみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/2138f73a-1d32-4812-5e99-eb3a7c0e0d6c.png)
このようにデータの内容が可視化されていますね。

続いてデータの前処理をしていきましょう。
今回は列の削除と欠損の補完を行います。

## 不要データの削除
まずは列の削除をしていきましょう。先ほど「データ」を選択したタブを「コンポーネント」にして、「Select Columns in Dataset」を検索します。
出てきたブロックをドラッグドロップして配置しましょう。
配置できたらAutomobileブロックの下にある○とSelectColumnsの上にある○をドラッグしてつなげます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/119116ed-bf65-47f3-bc2e-f7ded128e5ce.png)

SelectColumnsのブロックをだプルクリックすると右に項目が表示されます。
項目内にある列の編集をクリックし、ポップアップを表示します。
含めるではすべての列を選択します。
右の＋ボタンをクリックしてルールを追加します。
左の項目を含まないにして、次に列の名前を選択します。
すると列の名前を入力する項目が表示されます。今回は「normalized-losses」という項目を除去したいのでこちらを入力します。
その後保存をクリックしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/38898eba-2fea-eee7-5fae-186cb4675b9f.png)

## 欠損データの処理
続いて欠損データの処理を行います。
検索から「Clean Missing Data」を検索し、ブロックを配置しましょう。
配置したらSelectColumnsとCleanMissingDataを繋げます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/013bf1e1-9180-abdc-dedd-648eaa8158d2.png)

また、項目内のCleaningModeを「Remove entire row」にしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/bf8ba8bf-a308-8031-2597-2f3b06e14c85.png)

先ほど同様ブロックをダブルクリックして右の項目から列の編集を選び、ポップアップの含むの項目をすべての列にしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/7d61ad95-4e94-2870-308f-aff125f60a0c.png)

## データの分割
前処理できたので、こんどはデータの分割をします。
片方を学習に、もう片方をテストに使います。
検索から「Split Data」を調べ、ブロックを配置します。
CleanMissingDataブロックの左のCleaned datasと書いてある○からSplitDataに線を繋げます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/0d90a841-d7c8-9221-ed87-1e4d4510fe0a.png)

SplitDataをダブルクリックして項目を表示したら「Fraction of rows in the output dataset ...」の項目を0.7にしましょう。
これは分割する際のデータの割合で、初めに出力するデータを全体の70%にすることを指定しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/6d00c16e-5039-e2ee-ba62-ecae319e7a34.png)

## 学習アルゴリズムの追加
続いて学習するためのアルゴリズムを配置します。今回は「LinerRegression」を利用したいと思います。
検索して出てきたブロックを配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/d8614745-f92f-bd0b-a0fc-22123816bb89.png)

学習させるためにはTrainModelブロックが必要です。こちらも検索して配置しましょう。
配置したら、左上の○とLinerREgressionを、右上の○はSplitDataの左下の○とを繋げましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/2a716a43-2a0f-797e-167c-bcf52a1be4cd.png)

このままだと予測する値が決まっていないので、TrainModelをダブルクリックして項目を開き、LabelColumn横にある列の編集から指定します。
今回は「price」を予測したいのでこちらを選択します。その後保存します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/9dd3bac6-fa0b-2324-1dc0-18561a2ac8f2.png)

## モデルのテストと評価
作成したモデルのテストを行うために「ScoreModel」ブロックを追加します。
左上の○とTrainModelの○を、右上の○とSplitDataの右下の○を繋げます。
また、テスト結果をもちいてモデルの評価を行いたいので「EvaluteModel」ブロックを追加します。
左上の○とScoreModelの○を繋げましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/632cac2e-c0f3-0516-db8d-73828529bebe.png)


## ジョブの実行
ジョブの実行をするため、クラスターの作成をしましょう。
画面右上にある設定を開き、「AzureMLコンピューティングクラスター...」から作成を行います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/44db76b8-6003-5c42-8796-d4ef8692657d.png)

クラスタの種類によってコストが変わってくるので、データ量などを確認しながら選ぶといいかなと思います。
今回は一番上のものを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/c5985d7a-7c0e-505d-c986-29a75f8d31f5.png)

選択後コンピューティング名を設定し、作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/e4101da5-4444-e64e-0f39-4f574153814c.png)

先ほどの設定画面に戻り、「AzureMLコンピューティングクラスタを選択する」から作成したクラスターを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/015fa9aa-3c97-f137-6ae2-cc906da123eb.png)


クラスタが設定できたら上部にある「送信」をクリックしてジョブを開始させます。
パイプラインの設定で新規作成から実験名を記入し送信します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/78d1d9f3-cbfa-e965-6c6c-720548c86f95.png)

左にサービスからの応答を待機してますという項目が出ます。
少し経つと表示が変わり、このようになるのでジョブの詳細をクリックしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/e5db03d1-53a8-8305-e749-3b680f6f5888.png)

実行内容が確認できますね。
実行中のブロックは左に縞模様ができているみたいです。
この画面で実行の確認や結果の確認をすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/f9790cc3-3923-3932-60d3-f02ba02be38a.png)

実行が完了したら結果を見てみたいと思います。
まずはテストの結果を見ましょう。ScoreModelを右クリックして「データを表示する」を押下し、ScoredDatasetをクリックしましょう。
すると右に項目が表示されます。
項目内のScoredLabelsの項目が予測結果になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/31dcac55-6fa9-3d3c-e98f-15f90f07638c.png)

モデルの評価についても同様に確認してみましょう。
EvaluteModelをダブルクリックして項目を表示します。項目上部のタブでメトリックを選択しましょう。
すると評価指標を確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/7d0cc07c-8eee-44a8-d094-a2952ef3a9ae.png)


## モデルの運用
実際にこのモデルを運用する際にはAPIを通して実行したり、バッチ処理を行うなどが考えられます。
今回はAPIを利用するリアルタイム推論パイプラインを利用したいと思います。
実行画面右上にある推論パイプラインをクリックして、「リアルタイム推論パイプライン」を選択しましょう。
推論パイプラインの項目がない場合は左にある「最新の情報に更新」をクリックすると表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/e382016c-8f66-9764-329c-2ffbe0daca04.png)

こちらをクリックすると自動でパイプラインの形が変化します。
変化後、送信をクリックして再度パイプラインを実行します。この時既存の実験を選択することで、後で確認する際に実験ごとに確認できわかりやすくなると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/c9169fe4-3567-3f45-bf39-829609fcc7ee.png)

先ほど同様少し時間がたつと左側のサービスの応答を待っていますという項目が変わるので、ジョブの詳細をクリックして開きましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/0f3f258f-be8f-82a1-b952-0f0a4f8560b7.png)
先ほど同様に実行の状況が確認できます。
実行が終了したら最新の情報に更新してみましょう。右上にデプロイの表示が出ると思います。
こちらの設定には推論用のコンピューティングを作成する必要があります

左のタブからコンピューティングを選択し、推論クラスターの項目を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/86e15e96-0a1e-cb85-75fb-151168bfa3d9.png)

新規をクリックして作成を始めます。
場所は利用地点に近いものを選ぶといいかなと思います。
その後VMのサイズを選択します。一番上のものを選ぶとエラーが出てしまったので、2つ目のものを選ぶことにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/19c43218-a890-31e3-7d93-994ff47f57d0.png)

設定後は作成をクリックしましょう。
作成には少し時間がかかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/751aedde-fc78-a9ae-7af5-49685a8a9773.png)


作成が終了したら、元の画面に戻りましょう。
jobから先ほど設定した実験をクリックして開きます。実行したパイプラインが表示されてると思います。後ろにRealtime...とくっついているものを選びましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/995809fd-e47a-c123-f2d6-70b1b8989d2f.png)

戻ったら右上にあるデプロイをクリックしてセットアップを行いましょう。
名前を設定し、コンピューティング名の箇所に先ほど作成したクラスターを選択します。
その後デプロイしましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/e7c64a4f-e942-661d-2944-a5fbfa9f4461.png)

作成されたエンドポイントを確認してみましょう。
左の項目からエンドポイントを選択すると確認ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/4f630dff-0a9a-9adc-7a42-cb0c7b00ef9d.png)

作成したエンドポイントを開いてみると、Restエンドポイントが表示されてると思います。
こちらに対してPOSTすることで推論結果を取得することができます。

## エンドポイントのテスト
参考にしているチュートリアルではweb上でテストができるようなのですが、自分の環境だとできなかったため、pythonから実行してエンドポイントを確認してみたいと思います。
実行するにはデプロイ状態がhealthyである必要があるため、準備が整うまで待機しましょう。

準備ができたら詳細、デプロイログのタブに「テスト」と「使用」のタブが追加されます。
使用では利用する際に用いるエンドポイントとキー、コードが載っています。
今回はこのコードのpythonを用いたいと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/395106/8e1f0da9-e97f-6b2f-8b90-c6ef555caf12.png)

ではpythonコードをコピーしてコードの修正をしていきます。
修正箇所はAPIに投げるデータの部分です。投げるデータの概要は

```json
{
  "Inputs": {
    "WebServiceInput0": ["ここにデータ"],
  },
  "GlobalParameters": {}
}
```
このような形式である必要があります。
また、入れ込むデータの形は、

```json
{
  "symboling": -2,
  "normalized-losses": 0.0,
  "make": "volvo",
  ...
},
```
のように`{"カラム名": データ}`である必要があります。
また、実験時に用いたデータの型、カラムをすべてそろえてあげる必要があるので注意しましょう。(この辺も前処理でどうにかできるかもしれませんね)

では実際のコードです。
```python
import urllib.request
import json
import os
import ssl

def allowSelfSignedHttps(allowed):
    if allowed and not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None):
        ssl._create_default_https_context = ssl._create_unverified_context

allowSelfSignedHttps(True)
data = {
  "Inputs": {
    "WebServiceInput0": [{
            "symboling": -2,
            "normalized-losses": 0.0,
            "make": "volvo",
            "fuel-type": "gas",
            "aspiration": "std",
            "num-of-doors": "four",
            "body-style": "sedan",
            "drive-wheels": "rwd",
            "engine-location": "front",
            "wheel-base": 104.3,
            "length": 188.8,
            "width": 67.2,
            "height": 56.2,
            "curb-weight": 2912,
            "engine-type": "ohc",
            "num-of-cylinders": "four",
            "engine-size": 141,
            "fuel-system": "mpfi",
            "bore": 3.78,
            "stroke": 3.15,
            "compression-ratio": 9.5,
            "horsepower": 114,
            "peak-rpm": 5400,
            "city-mpg": 23,
            "highway-mpg": 28,
            "price": 12940
            },
        ],
  },
  "GlobalParameters": {}
}

body = str.encode(json.dumps(data))

url = 'YourEndpoint'
api_key = 'YOurAPIKey'
headers = {'Content-Type':'application/json', 'Authorization':('Bearer '+ api_key)}

req = urllib.request.Request(url, body, headers)

try:
    response = urllib.request.urlopen(req)

    result = response.read()
    print(result)
except urllib.error.HTTPError as error:
    print("The request failed with status code: " + str(error.code))
    print(error.info())
    print(error.read().decode("utf8", 'ignore'))
```

こちらを実行すると下記のような結果が返ってきました。
「Scored Labels」が追加されていますね。
```json
{
    "Results": {
        "WebServiceOutput0": [
            {
                "symboling": -2,
                "make": "volvo",
                "fuel-type": "gas",
                "aspiration": "std",
                "num-of-doors": "four",
                "body-style": "sedan",
                "drive-wheels": "rwd",
                "engine-location": "front",
                "wheel-base": 104.3,
                "length": 188.8,
                "width": 67.2,
                "height": 56.2,
                "curb-weight": 2912,
                "engine-type": "ohc",
                "num-of-cylinders": "four",
                "engine-size": 141,
                "fuel-system": "mpfi",
                "bore": 3.78,
                "stroke": 3.15,
                "compression-ratio": 9.5,
                "horsepower": 114.0,
                "peak-rpm": 5400.0,
                "city-mpg": 23,
                "highway-mpg": 28,
                "price": 12940.0,
                "Scored Labels": 16672.492419106384
            }
        ]
    }
}
```

これで作成したエンドポイントの確認ができました！

# まとめ
今回はAzureMLのデザイナー機能を使ったモデルの作成からエンドポイントの実行までやってみました。
最後の確認ではコードを触ったものの、モデルの実装からエンドポイントの作成まではノーコードでできたのではないでしょうか。
今回はすべて手作業で作成してみましたが、次回以降はすでにあるサンプルを動かしつつ、AzureMLでできることを探っていけるといいかなと思います。