kakakakakku blog

Weekly Tech Blog: Keep on Learning!

実験管理を便利に行う MLflow Tracking に入門した

実験管理やモデルレジストリなど,機械学習ライフサイクルをうまく管理するプラットフォームとして有名な「MLflow」に入門する.GitHub リポジトリの Star は 12000 もあってスゴイ!MLflow は MLOps の文脈でもよく聞くので,1度試しておこうと思った.

現在,MLflow には大きく「4種類」のコンポーネントがある.

  • MLflow Tracking : 実験管理 / ハイパーパラメータや評価メトリクスなどを記録したり比較できたりする
  • MLflow Projects : パッケージング / 機械学習を実行するための設定情報などを再現可能にする
  • MLflow Models : モデル化 / さまざまなデプロイツールをサポートする汎用的なモデル形式を提供する
  • MLflow Model Registry : モデルレジストリ / レジストリとしてモデルを管理する

github.com

MLflow Tutorial

今回は MLflow のドキュメントに載っている「Tutorial」「Quickstart」を参考にしながら,MLflow の中で基本となるコンポーネント「MLflow Tracking」を試す.ドキュメントを読みながら進めて,特にハマるところもなく試せた.

mlflow.org

mlflow.org

準備

準備として MLflow と scikit-learn をインストールする.さらに MLflow の GitHub リポジトリにある examples を使うため git clone もしておく.今回は macOS を使って環境を構築する.それぞれのバージョンは以下の通り.

  • MLflow : 1.26.1
  • scikit-learn : 1.1.1
$ pip install mlflow
$ pip install scikit-learn
$ git clone https://github.com/mlflow/mlflow

次に mlflow ui コマンドを実行して,実験結果を表示する画面を起動する.デフォルトでは http://127.0.0.1:5000/ で表示できる.準備 OK!

$ mlflow/examples
$ mlflow ui

MLflow 用語

今から出てくる MLflow 用語の関係性を以下にまとめておく.Experiment(実験)に複数の Run(実行)が紐付く.

  • Experiment(実験)
  • Run(実行)

MLflow Tracking サンプルコード

MLflow Tracking のイメージを掴むために,まず「Quickstart」に載っている quickstart/mlflow_tracking.py を実行する.コードのポイントは大きく3点ある.

  • log_param() 関数を使って「パラメータ : param1を登録する
  • log_metric() 関数を使って「メトリクス : fooを登録する
  • log_artifacts() 関数を使って「アーティファクト : test.txtを登録する
import os
from random import random, randint

from mlflow import log_metric, log_param, log_artifacts

if __name__ == "__main__":
    print("Running mlflow_tracking.py")

    log_param("param1", randint(0, 100))

    log_metric("foo", random())
    log_metric("foo", random() + 1)
    log_metric("foo", random() + 2)

    if not os.path.exists("outputs"):
        os.makedirs("outputs")
    with open("outputs/test.txt", "w") as f:
        f.write("hello world!")

    log_artifacts("outputs")

MLflow の関数は以下のドキュメントに詳細に載っている.

今回は「計3回」実行する.

$ python quickstart/mlflow_tracking.py
$ python quickstart/mlflow_tracking.py
$ python quickstart/mlflow_tracking.py

実行後に画面を更新すると実行結果を確認できる.また param1foo など登録したパラメータとメトリクスも一覧できる.

個別に実行を選択するとアーティファクトも確認できる.便利!

MLflow Tracking と scikit-learn を組み合わせる

次は「Tutorial」に載っている sklearn_elasticnet_wine/train.py を使って実践的なトレーニングを実行する.今回のお題として,ワインの品質を予測する.アルゴリズムとしては scikit-learn の ElasticNet を使って回帰分析をする.ハイパーパラメータとしては alpha(L1, L2 正規化項の合計)と l1_ratio(L1 正則化項の割合)を変えながらパフォーマンスを確認する.モデルの評価メトリクスとしては RMSE(二乗平均平方根誤差) を使う.

そして,今回は「Tutorial」の手順を一部変更する.そのまま実行すると Default Experiments に紐付くため,新しく sklearn-elasticnet-wine Experiments を作って紐付ける.今回は id=1 の Experiments になった.

import mlflow

experiment_id = mlflow.create_experiment('sklearn-elasticnet-wine')
print(experiment_id)

コードのポイントは大きく4点ある.その他のコードはデータセットを分割したり,scikit-learn を使ってトレーニングをしたり,一般的な機械学習コードそのものなので,紹介は割愛する.

  • 実行を sklearn-elasticnet-wine Experiments に紐付けるために mlflow.start_run()mlflow.start_run(experiment_id=1) に変更する
  • log_param() 関数を使って「ハイパーパラメータ : alphal1_ratioを登録する
  • log_metric() 関数を使って「メトリクス : rmser2maeを登録する
  • log_model() 関数を使って「モデル」を登録する
# The data set used in this example is from http://archive.ics.uci.edu/ml/datasets/Wine+Quality
# P. Cortez, A. Cerdeira, F. Almeida, T. Matos and J. Reis.
# Modeling wine preferences by data mining from physicochemical properties. In Decision Support Systems, Elsevier, 47(4):547-553, 2009.

import os
import warnings
import sys

import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
from sklearn.model_selection import train_test_split
from sklearn.linear_model import ElasticNet
from urllib.parse import urlparse
import mlflow
import mlflow.sklearn

import logging

logging.basicConfig(level=logging.WARN)
logger = logging.getLogger(__name__)


def eval_metrics(actual, pred):
    rmse = np.sqrt(mean_squared_error(actual, pred))
    mae = mean_absolute_error(actual, pred)
    r2 = r2_score(actual, pred)
    return rmse, mae, r2


if __name__ == "__main__":
    warnings.filterwarnings("ignore")
    np.random.seed(40)

    # Read the wine-quality csv file from the URL
    csv_url = (
        "http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"
    )
    try:
        data = pd.read_csv(csv_url, sep=";")
    except Exception as e:
        logger.exception(
            "Unable to download training & test CSV, check your internet connection. Error: %s", e
        )

    # Split the data into training and test sets. (0.75, 0.25) split.
    train, test = train_test_split(data)

    # The predicted column is "quality" which is a scalar from [3, 9]
    train_x = train.drop(["quality"], axis=1)
    test_x = test.drop(["quality"], axis=1)
    train_y = train[["quality"]]
    test_y = test[["quality"]]

    alpha = float(sys.argv[1]) if len(sys.argv) > 1 else 0.5
    l1_ratio = float(sys.argv[2]) if len(sys.argv) > 2 else 0.5

    with mlflow.start_run(experiment_id=1):
        lr = ElasticNet(alpha=alpha, l1_ratio=l1_ratio, random_state=42)
        lr.fit(train_x, train_y)

        predicted_qualities = lr.predict(test_x)

        (rmse, mae, r2) = eval_metrics(test_y, predicted_qualities)

        print("Elasticnet model (alpha=%f, l1_ratio=%f):" % (alpha, l1_ratio))
        print("  RMSE: %s" % rmse)
        print("  MAE: %s" % mae)
        print("  R2: %s" % r2)

        mlflow.log_param("alpha", alpha)
        mlflow.log_param("l1_ratio", l1_ratio)
        mlflow.log_metric("rmse", rmse)
        mlflow.log_metric("r2", r2)
        mlflow.log_metric("mae", mae)

        tracking_url_type_store = urlparse(mlflow.get_tracking_uri()).scheme

        # Model registry does not work with file store
        if tracking_url_type_store != "file":

            # Register the model
            # There are other ways to use the Model Registry, which depends on the use case,
            # please refer to the doc for more information:
            # https://mlflow.org/docs/latest/model-registry.html#api-workflow
            mlflow.sklearn.log_model(lr, "model", registered_model_name="ElasticnetWineModel")
        else:
            mlflow.sklearn.log_model(lr, "model")

今回はハイパーパラメータを変更しながら「計5回」実行する.

  • 実行 1. alpha=0.5 / l1_ratio=0.5
  • 実行 2. alpha=0.4 / l1_ratio=0.4
  • 実行 3. alpha=0.3 / l1_ratio=0.3
  • 実行 4. alpha=0.2 / l1_ratio=0.2
  • 実行 5. alpha=0.1 / l1_ratio=0.1
$ python sklearn_elasticnet_wine/train.py
Elasticnet model (alpha=0.500000, l1_ratio=0.500000):
  RMSE: 0.7931640229276851
  MAE: 0.6271946374319586
  R2: 0.10862644997792614

$ python sklearn_elasticnet_wine/train.py 0.4 0.4
Elasticnet model (alpha=0.400000, l1_ratio=0.400000):
  RMSE: 0.7644619587468349
  MAE: 0.5966303605775048
  R2: 0.17197111491474282

$ python sklearn_elasticnet_wine/train.py 0.3 0.3
Elasticnet model (alpha=0.300000, l1_ratio=0.300000):
  RMSE: 0.7443224557281489
  MAE: 0.5754825491733004
  R2: 0.2150247343683439

$ python sklearn_elasticnet_wine/train.py 0.2 0.2
Elasticnet model (alpha=0.200000, l1_ratio=0.200000):
  RMSE: 0.7336400911821402
  MAE: 0.5643841279275428
  R2: 0.23739466063584158

$ python sklearn_elasticnet_wine/train.py 0.1 0.1
Elasticnet model (alpha=0.100000, l1_ratio=0.100000):
  RMSE: 0.7128829045893679
  MAE: 0.5462202174984664
  R2: 0.2799376066653344

実行後に画面を更新すると実行結果を確認できる.実行結果は sklearn-elasticnet-wine Experiments に紐付いている.

MLflow では metrics.rmse < 0.74 のようなクエリを書くことで実行結果をフィルタできる.

クエリ構文は以下のドキュメントに詳細に載っている.

www.mlflow.org

さらに実行結果(今回だと5件)を選択して評価メトリクス RMSE を比較できる.

さらにハイパーパラメータと評価メトリクスの関係性も比較できる.以下の例では,ハイパーパラメータ alpha と評価メトリクス RMSE の関係性を比較している.

そして,アーティファクトを確認するとモデル model.pkl も含まれている.

モデルをデプロイして推論する

MLflow CLI で mlflow models serve コマンドを実行すると,指定したモデルをデプロイした推論エンドポイントを起動できる.

www.mlflow.org

今回は最も RMSE の値が低くパフォーマンスが高かった実行 e8901371a1494c34bd0a26e8180c5441 のモデルをデプロイする.アーティファクトからパスを取得して,以下のように実行する.

$ mlflow models serve -m file:///Users/kakakakakku/mlflow/examples/mlruns/1/e8901371a1494c34bd0a26e8180c5441/artifacts/model -p 1234

推論エンドポイント http://127.0.0.1:1234 に以下のように curl コマンドを実行すると推論結果を取得できる.mlflow models serve コマンドのログを眺めていたら HTTP Server は Gunicorn を使っていた.

$ curl -X POST -H 'Content-Type:application/json; format=pandas-split' http://127.0.0.1:1234/invocations\
  --data '{"columns":["alcohol", "chlorides", "citric acid", "density", "fixed acidity", "free sulfur dioxide", "pH", "residual sugar", "sulphates", "total sulfur dioxide", "volatile acidity"],"data":[[12.8, 0.029, 0.48, 0.98, 6.2, 29, 3.33, 1.2, 0.39, 75, 0.66]]}' 
[10.652032783691832]

$ curl -X POST -H 'Content-Type:application/json; format=pandas-split' http://127.0.0.1:1234/invocations\
  --data '{"columns":["alcohol", "chlorides", "citric acid", "density", "fixed acidity", "free sulfur dioxide", "pH", "residual sugar", "sulphates", "total sulfur dioxide", "volatile acidity"],"data":[[15.8, 0.029, 0.48, 0.98, 6.2, 29, 3.33, 1.2, 0.39, 75, 0.66]]}' 
[10.83593352318585]

まとめ

今回は MLflow のドキュメントに載っている「Tutorial」「Quickstart」を参考にしながら,MLflow の中で基本となるコンポーネント「MLflow Tracking」を試した.実験管理がしやすく便利だった.他にもグラフ画像などを登録する log_figure() 関数や簡単にパラメータとメトリクスを登録する Automatic Logging など,まだまだ気になる機能がある.引き続き試していくぞ!

www.mlflow.org