実験管理やモデルレジストリなど,機械学習ライフサイクルをうまく管理するプラットフォームとして有名な「MLflow」に入門する.GitHub リポジトリの Star は 12000 もあってスゴイ!MLflow は MLOps の文脈でもよく聞くので,1度試しておこうと思った.
現在,MLflow には大きく「4種類」のコンポーネントがある.
- MLflow Tracking : 実験管理 / ハイパーパラメータや評価メトリクスなどを記録したり比較できたりする
- MLflow Projects : パッケージング / 機械学習を実行するための設定情報などを再現可能にする
- MLflow Models : モデル化 / さまざまなデプロイツールをサポートする汎用的なモデル形式を提供する
- MLflow Model Registry : モデルレジストリ / レジストリとしてモデルを管理する
MLflow Tutorial
今回は MLflow のドキュメントに載っている「Tutorial」と「Quickstart」を参考にしながら,MLflow の中で基本となるコンポーネント「MLflow Tracking」を試す.ドキュメントを読みながら進めて,特にハマるところもなく試せた.
準備
準備として 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
実行後に画面を更新すると実行結果を確認できる.また param1
や foo
など登録したパラメータとメトリクスも一覧できる.
個別に実行を選択するとアーティファクトも確認できる.便利!
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()
関数を使って「ハイパーパラメータ :alpha
とl1_ratio
」を登録するlog_metric()
関数を使って「メトリクス :rmse
とr2
とmae
」を登録する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
のようなクエリを書くことで実行結果をフィルタできる.
クエリ構文は以下のドキュメントに詳細に載っている.
さらに実行結果(今回だと5件)を選択して評価メトリクス RMSE を比較できる.
さらにハイパーパラメータと評価メトリクスの関係性も比較できる.以下の例では,ハイパーパラメータ alpha
と評価メトリクス RMSE の関係性を比較している.
そして,アーティファクトを確認するとモデル model.pkl
も含まれている.
モデルをデプロイして推論する
MLflow CLI で mlflow models serve
コマンドを実行すると,指定したモデルをデプロイした推論エンドポイントを起動できる.
今回は最も 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 など,まだまだ気になる機能がある.引き続き試していくぞ!