Azure ML & MLflowで可視化: Azure ML StudioとMLflow Trackingによるデータ収集・可視化の紹介

# はじめに

Azure OpenAI Serivceを活用したサービスの検討・検証を行っていますが、回答精度の向上には何が起きているかを確認・追跡する必要があり、これには効率よくプロンプトやパラメータ等の蓄積・可視化できるかが重要になってきます。  
そこで生成AIに関するログの収集・可視化サービスとしてAzure ML Studioを活用できないかを調査したので、その内容について以下の3部構成で記載いたします。  
まず本記事ではMLflowの概要と、そのMLflowを利用した際のAzure ML Studio上での表示について紹介させて頂きます。

(1) [Azure ML & MLflowで可視化: Azure ML StudioとMLflow Trackingによるデータ収集・可視化の紹介](https://qiita.com/y-na-biz/private/2355f416d0f216263dcc) (本記事)
(2) [PromptFlowログの可視化：Azure AI/ML Studio & MLflowと連携し収集・可視化する](https://qiita.com/y-na-biz/private/0da02dfd01417deed46b)
(3) RAG実施時の検索エンジン(Azure AI Search)精度指標の検討 (※記載中)

## MLflowとは

MLflowは、機械学習(ML)のライフサイクルを管理・効率化するためのツールやプロセスを提案するオープンソースのMLOpsプラットフォームで以下の[コアコンポーネント](https://mlflow.org/docs/latest/introduction/index.html#core-components-of-mlflow)から構成されています。こちらに記載した通り最近LLM(大規模言語モデル)関連のコンポーネントが追加されるなどLLMOpsへの対応も積極的に進められております。

| コンポーネント名                                         | 概要                                                                                                                 |
| -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| [Tracking](https://mlflow.org/docs/latest/tracking.html)  | MLプロセス中のパラメータ、コードバージョン、メトリクス、成果物について記録し、時間経過に伴う変化を可視化・確認する。 |
| [Model Registry](https://mlflow.org/docs/latest/model-registry.html)  | モデルのバージョン管理やタグ付け等の機能を提供し、ライフサイクル全体を一元管理する。                                 |
| [MLflow Deployments for LLMs](https://mlflow.org/docs/latest/llms/deployments/index.html)<br/>(旧称:MLflow AI Gateway) | LLMモデルの様々なLLMモデル/サービスへのアクセスについて共通的なAPIを提供する。                                       |
| [Evaluate](https://mlflow.org/docs/latest/models.html#model-evaluation) | LLMも含めて詳細なモデル分析を行うためのツールで、客観的なモデル比較を容易にする。                                    |
| [Prompt Engineering UI](https://mlflow.org/docs/latest/llms/prompt-engineering/index.html)   | プロンプトの実験、改良、評価、テスト、デプロイのための専用のUIを提供する。                                           |
| [Recipes](https://mlflow.org/docs/latest/recipes.html)<br/>(旧称MLflow Pipelines) | 高品質なモデルを迅速に開発し、本番環境にデプロイすることを可能にするフレームワークを提供する。                       |
| [Projects](https://mlflow.org/docs/latest/projects.html) | MLコードや成果物等を再利用可能かつ再現可能な形でパッケージ化し、他ユーザへの共有やワークフロー構築を可能にする。     |

この中でAzure ML ワークスペースは以下のMicrosoftのページに記載されている通り**MLflowと互換性**があり、MLflow Tracking Serverとして利用できるようになっています。またAzure ML ワークスペースのWeb UIである「Azure ML Studio」についても同様にMLflowのデータの可視化に対応しています。  

- [MLflow と Azure Machine Learning - Azure Machine Learning](https://learn.microsoft.com/ja-jp/azure/machine-learning/concept-mlflow?view=azureml-api-2)

しかしながら、Microsoftのドキュメントには[モデル管理](https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-manage-models-mlflow?view=azureml-api-2#support-matrix-for-managing-models-with-mlflow)と[検索クエリ](https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-track-experiments-mlflow?view=azureml-api-2#support-matrix-for-querying-runs-and-experiments)のサポートマトリクスは記載されているものの、肝心のMLflowのロギング関係の対応表やどういった形で可視化されるのか等のページが見つけることが出来ませんでした。
そこで今回、MLflowのロギングAPIをメインにAzure ML Studio上での対応とどういった可視化が可能なのかを確認・整理しました。

なお本記事ではMLflowの詳細については述べませんので、実験管理の例やシナリオ等については以下のc60evaporator(Kenta Nakamura)さん記載の記事等をご参照頂ければ幸いです。

- [【MLOps】「いつか使いたい！」と思っていた人のためのMLflowまとめ](https://qiita.com/c60evaporator/items/e0eb1a0c521d1310d95d)

## MLflowのAzure MLワークスペースへの接続設定

ここではAzure ML上で実行されるVM等ではなく、ローカルやリモート環境からMLflowを用いてAzure MLワークスペースに接続する方法を記載いたします。  
最新の内容については[こちらのMicrosoftドキュメント](https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-use-mlflow-configure-tracking?view=azureml-api-2)を参照ください。

### 1. MLflowとMLflow用のAzure MLプラグインのインストール

以下の通りPythonパッケージ`mlflow-skinny`とそのMLflow用のAzure MLプラグイン`azureml-mlflow`をインストールします。  
`mlflow-skinny`は、`mlflow ui`等を持たない軽量のMLflowパッケージで、主に追跡とログ機能を利用する場合にインストールします。

``` bash
pip install mlflow-skinny azureml-mlflow
```

### 2. Azure MLワークスペースへの権限割り当て

Azure MLワークスペースにアクセスしたいユーザ、またはマネージドIDに必要なアクセス許可が割り当てられているかを確認します。  
24年4月時点での組み込みロールとしては以下の2つがあります。後者は主にAzure AI Studioを利用する際のものになります。  
ただしこの2つはAzure ML上のほぼ全ての権限が付与されてしまうので、より厳密に権限を設定する場合は[こちらのページ](https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-assign-roles?view=azureml-api-2&tabs=team-lead#mlflow-operations)を参考にしながらカスタムロールを作成し割り当ててください。

- AzureML データ科学者
- Azure AI Developer

### 3. MLflow Tracking URIの取得

MLflow Tracking URIを以下のいずれかの方法で取得します。  

- Azure Portalを利用する方法：  
  - Azure Portalを開きAzure Machine Learning ワークスペースを開きます。
  - 「概要」の「要点」欄中に「MLflow 追跡 URI」がありますので、こちらをコピーします。
  　
- 手動で構成する方法：
  - Tracking URIは以下のフォーマットの通りとなっています。各環境に応じて`region`等の値を設定してください。
    > azureml://{region}.api.azureml.ms/mlflow/v1.0/subscriptions/{subscription_id}/resourceGroups/{resource_group}/providers/Microsoft.MachineLearningServices/workspaces/{workspace_name}

    なおPrivate Linkが有効になっている場合のワークスペースは、こちらとは異なるURIになるので、次項のAzure ML SDKを利用して取得してください。  
    　
- Azure ML SDK (Python) を利用する方法：
  - Azure ML SDK (`azure-ai-ml`ライブラリ)をインストールします。
  - 以下のコードによりTracking URIを取得します。

    ``` python
    from azure.ai.ml import MLClient
    from azure.identity import DefaultAzureCredential
    
    # Enter details of your AzureML workspace
    subscription_id = '<SUBSCRIPTION_ID>'
    resource_group = '<RESOURCE_GROUP>'
    workspace_name = '<WORKSPACE_NAME>'
    
    ml_client = MLClient(credential=DefaultAzureCredential(),
                         subscription_id=subscription_id, 
                         resource_group_name=resource_group,
                         workspace_name=workspace_name)
    
    mlflow_tracking_uri = ml_client.workspaces.get(ml_client.workspace_name).mlflow_tracking_uri
    ```
　

### 4. Tracking URIの設定

取得したTracking URIを以下のどちらかの方法を用いてMLflowに設定します。

- 環境変数(`MLFLOW_TRACKING_UR`)に設定する
- MLflowのAPI(`mlflow.set_tracking_uri()`)により設定する


以上がMLflowをAzure MLワークスペースに接続する方法となります。  
なおAzureサービスの認証には内部でPythonライブラリの`azure-identity`が利用されており、[以下の認証が順番に試行](https://learn.microsoft.com/ja-jp/azure/machine-learning/how-to-use-mlflow-configure-tracking?view=azureml-api-2#configure-authentication)されるので、いずれかの認証方法を設定してください。

> 1. 環境変数: 環境変数で指定されたアカウント情報を読み取り、それを使用して認証を行います。
> 2. マネージド ID: マネージド ID が有効な Azure ホストにアプリケーションがデプロイされている場合、それを使用して認証されます。
> 3. Azure CLI: ユーザーが Azure CLI az login コマンドを使用してサインインする場合、そのユーザーとして認証されます。
> 4. Azure PowerShell: ユーザーが Azure PowerShell の Connect-AzAccount コマンドを使用してサインインする場合、そのユーザーとして認証されます。
> 5. 対話型ブラウザー: 既定のブラウザーを使用してユーザーを対話形式で認証します。


## MLflowロギング用メソッドとAzure ML Studio上での表示

次にMLflowロギング用メソッドとAzure ML Studioの何処に表示されるかを以下の表にまとめました。なおMLflowの実行(run)はAzure ML Studio上では「ジョブ」として表示されます。  

<table>
<thead><tr>
  <th>分類</th>
  <th>メソッド</th>
  <th>概要</th>
  <th>ジョブ詳細画面</th>
  <th>ダッシュボードビュー</th>
</tr></thead>
<tbody>
<tr>
  <td>パラメータ</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_param"> 
      MLflow.log_param()
    </a><br>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_params"> 
      MLflow.log_params()
    </a>
  </td>
  <td>モデル等への入力パラメータの記録</td>
  <td>・「概要」タブ→パラメータ－</td>
  <td>・比較テーブル→パラメータ－</td>
</tr>
<tr>
  <td>メトリクス</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_metric"> 
      MLflow.log_metric()
    </a><br>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_metrics"> 
      MLflow.log_metrics()
    </a>
  </td>
  <td>
    実行結果の評価指標等の記録<br>
    ※数値のみ
  </td>
  <td>
    ・「概要」タブ→メトリック<br>
    ・「メトリック」タブ
  </td>
  <td>
    ・比較テーブル→メトリック<br>
    ・グラフ：折れ線、横棒、散布図 (X軸、Y軸)
  </td>
</tr>
<tr>
  <td>タグ</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.set_tag"> 
      MLflow.set_tag()
    </a><br>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.set_tags"> 
     MLflow.set_tags()
    </a>
  </td>
  <td>タグ情報の記録</td>
  <td>・「概要」タブ→タグ</td>
  <td>
    ・比較テーブル→プロパティ→複数のタグ<br>
    ・グラフ：各グラフの凡例
  </td>
</tr>
<tr>
  <td>アーティファクト</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_artifact"> 
      MLflow.log_artifact()
    </a><br>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_artifacts"> 
      MLflow.log_artifacts()
    </a>
  </td>
  <td>ファイルやディレクトリのアップロード</td>
  <td>・「出力とログ」タブ</td>
  <td>(なし)</td>
</tr>
<tr>
  <td>アーティファクト</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_text"> 
      MLflow.log_text()
    </a>
  </td>
  <td>文字列データをテキストファイルとして保存</td>
  <td>・「出力とログ」タブ</td>
  <td>(なし)</td>
</tr>
<tr>
  <td>アーティファクト</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_image"> 
      MLflow.log_image()
    </a>
  </td>
  <td>numpy.ndarray等を画像化して保存</td>
  <td>
    ・「画像」タブ<br>
    ・「出力とログ」タブ
  </td>
  <td>・イメージ</td>
</tr>
<tr>
  <td>アーティファクト</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_figure"> 
      MLflow.log_figure()
    </a>
  </td>
  <td>matplotlibやplotlyの図を画像化して保存</td>
  <td>
    ・「画像」タブ<br>
    ・「出力とログ」タブ
  </td>
  <td>・イメージ</td>
</tr>
<tr>
  <td>アーティファクト</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_dict"> 
      MLflow.log_dict()
    </a>
  </td>
  <td>PythonのDict形式のデータをjson、yaml形式で保存</td>
  <td>・「出力とログ」タブ</td>
  <td>(なし)</td>
</tr>
<tr>
  <td>アーティファクト</td>
  <td>
    <a href="https://mlflow.org/docs/latest/python_api/mlflow.html#mlflow.log_table"> 
      MLflow.log_table()
    </a>
  </td>
  <td>PythonのDict形式やDataFrame形式のデータをjsonで保存</td>
  <td>・「出力とログ」タブ</td>
  <td>
    ・比較テーブル→プロパティ→複数のタグ→MLflow.loggedArtifacts<br>
    ※(ファイル名、タイプのみ出力)
  </td>
</tr>
</tbody>
<tfoot>
    <tr><th colspan=5>$\hspace{88em}$</th></tr>
</tfoot>
</table>

こちらのサンプルコードとそのスクリーンショットは以下の通りです。  
ちなみに`mlflow.start_run()`呼び出し時に`nested=True`を設定するかと実行(`run`)間に親子関係を設定できます。

### サンプルコード

``` python
import os
import mlflow
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

os.environ['MLFLOW_TRACKING_URI'] = "<Tracking URI>"

experiment = mlflow.set_experiment("sample experiment")
with mlflow.start_run(run_name="sample run", experiment_id=experiment.experiment_id) as run:
    mlflow.log_param("param1", "param-value1")
    mlflow.log_params({"param2":9000})
    mlflow.log_metric("metric1", 0.3)
    mlflow.log_metrics({"metric2":1000})
    mlflow.set_tag("tag1", "tag-value1")
    mlflow.set_tags({"tag2":"tag-value2"})
    mlflow.log_text("# header", "artifact1.md")
    mlflow.log_text("<h1>header<h1>", "artifact2.html")

    with mlflow.start_run(run_name="child run1", nested=True) as child_run:
        mlflow.log_param("param1", "param-value1")
        mlflow.log_params({"param2":1000})
        mlflow.log_metric("metric1", 0.1)
        mlflow.log_metrics({"metric2":2000})

    with mlflow.start_run(run_name="child run2", nested=True) as child_run:
        array = np.asarray([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
        dataset = mlflow.data.from_numpy(array, source="data.csv")

        mlflow.log_artifact("./artifact1.txt")
        mlflow.log_artifacts("./artifact")
        mlflow.log_text("log_text", "artifact3.txt")

    with mlflow.start_run(run_name="child run3", nested=True) as child_run:
        fig, ax = plt.subplots()
        ax.plot([0, 1], [2, 3])
        mlflow.log_figure(fig, "figure.png")
        data = fig.canvas.tostring_rgb()
        w, h = fig.canvas.get_width_height()
        c = len(data) // (w * h)
        img = np.frombuffer(data, dtype=np.uint8).reshape(h, w, c)
        mlflow.log_image(img, "image.png")

    with mlflow.start_run(run_name="child run4", nested=True) as child_run:
        table_dict = {
            "inputs": ["What is MLflow?", "What is Databricks?"],
            "outputs": ["MLflow is ...", "Databricks is ..."],
            "toxicity": [0.0, 0.0],
        }
        mlflow.log_dict(table_dict, "log_dict.json")
        mlflow.log_dict(table_dict, "log_dict.yml")
        mlflow.log_dict(table_dict, "log_dict.txt")
        mlflow.log_table(table_dict, "log_table1.json")
        mlflow.log_table(pd.DataFrame.from_dict(table_dict),  "log_table2.json")
```

### スクリーンショット

#### 実験(Experiments)一覧

実験一覧画面では`mlflow.set_experiment()`で指定した名称にて表示されます。  
実験名は一意で、存在しない場合は作成、する場合は既存のものが取得される仕様となっています。

![Experiments](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/be554044-35e2-710b-bf85-bbc3f63fc32b.png)

#### 実行(Run)一覧

実行一覧では`mlflow.start_run()`の内容が表示されます。  
こちらは複数回実行した場合、実行が同じ名称で追加される仕様となっている点に注意してください。
また`nested=True`とした実行は階層表示され、複数階層(入れ子)にも対応しています。

![Jobs](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/b04f0243-a0c7-e145-5320-77cb2e09da70.png)

#### ダッシュボードビュー

実行一覧上部に表示されている「ダッシュボードビュー」を押下した場合の表示で、複数の実行結果をグラフやテーブルにて比較することが出来ます。
グラフは表中に記載した通り「折れ線」「横棒」「散布図」に対応しています。

![Dashboard View](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/7ed597c0-ad47-6839-49ce-5a41ab7e4b9c.png)

グラフ等の画像データが登録されていた場合、同じファイル名でグルーピングされて一覧表示されます。  

![画像表示](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/81edd407-0e6a-0410-3aaa-154705bfd0b9.png)


数値等のデータについては「比較テーブル」にて比較することが出来、ベースライン(初めに選択した実行)からの変更度合いを確認することが出来ます。  
![比較テーブル](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/b8f3676d-a502-e35e-b602-baf1ddfee69b.png)

#### ジョブ詳細 - 概要

ジョブ詳細の概要タブでは、設定したタグやパラメーター、メトリック等が一覧で確認できます。  

![Job details - Summary](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/13d00494-2bf5-0b1b-f178-e4a27fb922a8.png)

#### ジョブ詳細 - メトリック

メトリックタブでは、各メトリックを数値、あるいはグラフで確認することが出来ます。  
※子ジョブの情報も表示されますがカスタムグラフの追加は不可だったので、比較する場合は「[ダッシュボードビュー](#ダッシュボードビュー)」を利用する必要があります。

![Job details - Metrics](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/27bd0bdc-0713-89d4-9495-efa7334577d0.png)

#### ジョブ詳細 - 画像 

画像タブでは保存したグラフやイメージを確認することが出来ます。

![Job details -Images](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/4c2550d3-0f5a-8d08-570f-15e471a396bc.png)

#### ジョブ詳細 - 出力とログ

出力とログでは、上記の画像も含めた保存したファイルを確認することが出来ます。  
またMarkdownやHTMLファイルの表示にも対応しています。

![Job details - Outputs and Logs](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/decd8ed8-639c-78c0-2cde-3ed95ef842df.png)

#### ジョブ詳細 - 子ジョブ 

子ジョブでは`nested=True`として設定したジョブの一覧が表示されます。  

![Job details - Child jobs](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695104/4d9cfe17-5c1b-7d23-0723-9864a279d5c1.png)


## まとめ

以上がMLflowとAzure ML Studioの調査結果となります。Azure ML StudioはAzure上で提供されているPaaSでサーバ等を構築することなく簡単にMLflowのTracking Serverを準備することが出来ます。  
ただ残念ながらMLflowとAzure ML ワークスペースとの関係が難しく、どう使い始めていいか分からない方が多いと思います。
LLMでも最近マルチモーダル化の進展が激しく、従来のテキストベースのログでは対応しきれないことが想定されます。今回の調査でダッシュボードビューにて画像の一覧表示が可能なことを確認できたので、例えば入力で利用した画像や生成した画像等を一括して確認する、ということが比較的簡単に出来ることが分かりました。
またジョブについても複数の階層を取ることが出来るため、提供するサービスやシステムに応じて実験やジョブの構成等を設計・検討することで効率よく精度の確認や検証を進めることが出来るかと思うので、ぜひご利用についてご検討ください。

もし宜しければ是非以下の関連記事も参照ください。

## 関連記事

(1) [Azure ML & MLflowで可視化: Azure ML StudioとMLflow Trackingによるデータ収集・可視化の紹介](https://qiita.com/y-na-biz/private/2355f416d0f216263dcc) (本記事)
(2) [PromptFlowログの可視化：Azure AI/ML Studio & MLflowと連携し収集・可視化する](https://qiita.com/y-na-biz/private/0da02dfd01417deed46b)
(3) RAG実施時の検索エンジン(Azure AI Search)精度指標の検討 (※記載中)

## 参考記事

[【MLOps】「いつか使いたい！」と思っていた人のためのMLflowまとめ](https://qiita.com/c60evaporator/items/e0eb1a0c521d1310d95d)