はじめに
今回はMLflowを使って機械学習の実験管理について少し試したのでまとめてみました。
本当はこのエントリが新年一発目になる予定だったのですが、諸事情あって別のエントリになりました
環境の用意
MLflow
MLflowは機械学習で必要なプロセスのうち、「実験の管理」「プロジェクト管理」「モデルのサービング」を行うことができるソフトウェアです。
mlflow.org
私自身mlflowは全く触ったことがなかったものの、いくつかのブログを読んでいて興味を持ったので試してみようと思います。
今回は「実験の管理」について書こうと思います。
作る環境
今回は検証なのでMacbook上で実施します。
リモートサーバとして使うところまで試したかったので、以下のような構成にします。
MLflowを動かすだけならMLflowとテスト用のjupyter*1があればよいのですが、model repositoryを使うためにはbackend-storeにローカルストレージ以外を使う必要がある*2ため、簡単のためsqlite3としています。
また、artifactの格納先はクラウドのオブジェクトストレージ等必要ですが、コストの兼ね合いもあるのでS3互換オブジェクトストレージである、minio*3を利用します。
ネットワークの作成 (2019/1/7追記: これ以下のコマンドもこれに伴い少し変えています)
dockerコンテナ間の通信は昔はlinkオプションなどありましたが、現在では非推奨となっているようです。
当初めんどくさかったのでこの記事ではコンテナのIPを調べて接続していましたが、よりスマートにコンテナ名で名前解決する方法をQiitaの記事*4にて発見しましたので、その方法にアップデートします。
dockerのコンテナ間を名前解決させるにはブリッジネットワークを作り接続すればOKとのことなので、まずはネットワークを作成しておきます。
ここではmlflow_networkという名前にします。
docker network create mlflow_network
minioコンテナの作成
公式のDockerイメージ*5があるので、これを利用します。
docker container run -d --name minio -p 9000:9000 --net mlflow_network minio/minio server /data
http://localhost:9000/にアクセスすると、WebUIが見られます。
今回はデフォルトのままセットアップを行ったので、ID: minioadmin, pass: minioadminです。
ちゃんと使うときは注意してください。
こんな感じです。
右下の「+」ボタンからバケットが作れるので、一つ作っておきます。
今回は「mlflow」としました。
MLflowコンテナの作成
まずMLflowを立ち上げます。
先に断っておきますが、今回は可搬性のあるコンテナを作ることを目標としていなくて、まずはMLfowの動作テストのみなので、コンテナに入ってセットアップする方向にします。
docker run -itd --name mflow -p 5000:5000 --env MLFLOW_S3_ENDPOINT_URL=http://minio:9000 --env AWS_ACCESS_KEY_ID=minioadmin --env AWS_SECRET_ACCESS_KEY=minioadmin --net mlflow_network continuumio/anaconda3 docker exec -it mflow /bin/bash
ここで重要なのは以下3つの環境変数をセットアップすることです。
環境変数名 | 設定する値 |
MLFLOW_S3_ENDPOINT_URL | minioのエンドポイントURLを指定します。今回の場合http://minio:9000です。 |
AWS_ACCESS_KEY_ID | minioのユーザIDを指定します。今回の場合はminioadminです。 |
AWS_SECRET_ACCESS_KEY | minioのパスワードを指定します。今回の場合はminioadminです。 |
ここから先はコンテナ内で作業します。
# コンテナ最新化 apt -y update && apt -y upgrade # mflow, botoのインストール pip install mlflow boto3
では、MLflowを起動します。注意点として、オプションなしで起動すると127.0.0.1でlistenしてしまうので、0.0.0.0でlistenするようにオプションを渡します。
mlflow server --host 0.0.0.0 --backend-store-uri sqlite:///mlflow.db --default-artifact-root s3://mlflow
...前略... [2020-01-03 03:23:46 +0000] [94] [INFO] Starting gunicorn 20.0.4 [2020-01-03 03:23:46 +0000] [94] [INFO] Listening at: http://0.0.0.0:5000 (94) [2020-01-03 03:23:46 +0000] [94] [INFO] Using worker: sync [2020-01-03 03:23:46 +0000] [97] [INFO] Booting worker with pid: 97 [2020-01-03 03:23:46 +0000] [98] [INFO] Booting worker with pid: 98 [2020-01-03 03:23:46 +0000] [99] [INFO] Booting worker with pid: 99 [2020-01-03 03:23:46 +0000] [100] [INFO] Booting worker with pid: 100
こうなれば起動してます。Macbook上のブラウザからhttp://127.0.0.1:5000/へアクセスしてみましょう。
このような画面が出てくればOKです。少し読み込みに時間がかかるかもしれません。
jupyter-notebookコンテナの作成
これは公式のコンテナイメージ*6を使います。
こちらもMLflowのときと同様にminio関連の情報を環境変数として渡す必要があります。
docker run -p 8900:8888 --name jupyter --env MLFLOW_S3_ENDPOINT_URL=http://minio:9000 --env AWS_ACCESS_KEY_ID=minioadmin --env AWS_SECRET_ACCESS_KEY=minioadmin --net mlflow_network jupyter/scipy-notebook
立ち上がると以下のような画面になります。tokenをコピーしておいてください。
ちなみに、本来token情報は公開したらダメですが、今回は実験用ですぐ壊すことと、外部からアクセスする方法がないので載せてしまっています。
十分取り扱いには気をつけてください。
Macbook上のブラウザからhttp://127.0.0.1:8900/へアクセスしましょう。以下のような画面が表示されますので、先程入力したtokenを貼り付けてください。
以下のような画面に遷移すれば成功です!
では続いて、こっちのコンテナにもmlflowをセットアップしていきます。
まずは以下のようにしてターミナルを立ち上げます。
立ち上がったターミナルで以下のように入力し、MLflow(とboto)をインストールします。
pip install mlflow boto3
これで準備は完了です。試していきましょう。
実験
実際にコードを用意する
JupyterNotebook上でコードを書いていきます。
まずはインポートや初期設定です。
import mlflow mlflow.set_tracking_uri("http://mlflow:5000")
172.17.0.4:5000というのは、前の方で調べたMLflowのIPアドレスです。個人ごとの環境によって書き換えてください。
また、もし同一サーバで動かすような場合はmlflow.set_tracking_uriは不要です。
次に実際の学習コードを用意します。
今回は公式にある以下コードを少し改変して使います。
mlflow.org
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 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 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 = 0.5 l1_ratio = 0.5 with mlflow.start_run(): 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) mlflow.sklearn.log_model(lr, "model")
このコードはwine qualityデータセットをElasticNetを利用して回帰モデルをフィッティングするコードです。
下の方にあるalpha, l1_ratioがパラメータとなっていて、ここをいじる感じになります。
まずはそれぞれ0.5のままで、実行してみましょう。
以下のような出力が得られます。
Elasticnet model (alpha=0.500000, l1_ratio=0.500000): RMSE: 0.7931640229276851 MAE: 0.6271946374319586 R2: 0.10862644997792614
今回の学習ではRMSE: 0.79, MAE: 0.63, R2係数: 0.11程度です。
これがパラメータ等込ですでにmlflowのほうに記録されています。
再度MLflowを開いて、画面を更新しましょう。すると、以下のようになっているはずです。
学習時のパラメータや結果が保存されていることがわかります。
Dateのところをクリックすると、より詳細な情報を見ることができます。
先程のコードで指定したパラメータやモデルそのものが保存されているのがわかります。
わかりやすくていいですね。
何度かパラメータを変えて実験してみる
適当にパラメータを変えて何度かやってみます。
そうすると、このような感じでどんどん記録されていきます。
複数を選択して比較することも可能です。例えばこんな感じ。
下の方にあるように、グラフで表示することできるのでわかりやすくていいですね。
改めてコードを見る
ではどのように記録しているのか、実際にコードを見ていきます。
このあたりですね。
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) mlflow.sklearn.log_model(lr, "model")
こういうコードを元のコードに挟んでいくことで、パラメータを記録していけるようですね。
なんのロギングができるかは、以下を見るのがいいと思います。
今回使った、パラメータ、メトリックはわかりやすいですが、他にもtagをつけたり、ローカルにあるファイル(アーティファクト)も保管できるようです。
あとモデルの保存はそれぞれのフレームワークごとに用意されているようです。
例えば以下はsklearnのマニュアルです。
他にもtensorflowやpytorch含め、メジャーどころは網羅されているように見えます。
まとめ
今回は環境構築から、まずは実験の管理という観点でMLflowを使ってみました。
個人レベルだとパラメータの管理ができるだけでとても便利なのですが、他にもプロジェクト管理やモデルのサービング機能があります。
せっかくなので、正月休み中に一通り触ってまとめておきたいと思います。