概要
MLFlowの機能をざっと試す第二弾。前回はtrackingを扱ったので今回はprojects。
projectsはdockerやcondaでプロジェクトの管理ができる。本稿ではdockerは扱わずcondaを利用する。
バージョン情報
- mlflow==1.0.0
experimentの生成
今回の例で利用するexperimentを用意しておく。
MNISTの手書き数字分類を行うので、experiment名をmnist_digitとしてCLIから生成してみる。
$ mlflow experiments create --experiment-name mnist_digit
tensorflowのプロジェクトを作る
tensorflow(CPU版)のプロジェクトをmlflowで作ってみる。
まずは必要な依存関係をyamlで記述する。
# conda.yaml name: tf_example channels: - defaults - anaconda dependencies: - python==3.7 - numpy==1.16.1 - pandas - tensorflow==1.13.1 - keras - pip: - mlflow==1.0.0 - click
続いてmlflowのプロジェクトファイルを生成する。こちらもyaml。ファイル名はMLprojectとすること。
下記の記述ではプロジェクト名、先ほど書いたcondaの設定のパス、それから実行するコマンドとパラメータを記述している。
# MLproject name: tensorflow_mnist_digit # 先ほど書いたconda.yamlのパスを指定 conda_env: conda.yaml entry_points: main: parameters: # コマンドラインに渡す引数 epochs: {type: int, default: 20} batch_size: {type: int, default: 32} command: # 実行コマンド "python train.py --epochs {epochs} --batch-size {batch_size}"
最後にtensorflowで何かしらtrainするコードを用意する。
kerasでMNISTの数字認識を学習させて、引数やaccuracyの情報をmlflowに記録している。
mlflow側でstart_runしていないけどそのあたりはmlflow runをすればよしなにやってくれる。
import click import mlflow import mlflow.keras import tensorflow as tf @click.command() @click.option('--epochs', type=int) @click.option('--batch-size', type=int) def main(epochs, batch_size): # 何かしらtensorflowで学習 mnist = tf.keras.datasets.mnist (x_train, y_train),(x_test, y_test) = mnist.load_data() x_train, x_test = x_train / 255.0, x_test / 255.0 model = tf.keras.models.Sequential([ tf.keras.layers.Flatten(input_shape=(28, 28)), tf.keras.layers.Dense(512, activation=tf.nn.relu), tf.keras.layers.Dropout(0.2), tf.keras.layers.Dense(10, activation=tf.nn.softmax) ]) model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy']) model.fit(x_train, y_train, epochs=epochs, batch_size=batch_size) accuracy = model.evaluate(x_test, y_test) # paramとmetricsの保存 mlflow.log_metrics({'accuracy': accuracy[1]}) # modelの保存 mlflow.keras.log_model(model, 'mnist_digit') if __name__ == "__main__": main()
コマンドライン引数としてepochsとbatch-sizeの2つを取るようにしている。
階層としてはこんな風にファイルが格納されているものとする。
tf_example/ ├── MLproject ├── conda.yaml └── train.py
作成したプロジェクトを実行するコマンド。
$ mlflow run --experiment-name mnist_digit -P batch-size=64 tf_example
引数の意味は --experiment-nameでexperimentを指定。実行コマンドに渡す引数は -P param-name=value のような形で指定できる。最後のtf_exampleは作成したプロジェクトが入っているディレクトリのパス。
mlflow uiを起動してtrackingされた結果を表示。
train.py側でrun_id, experiment_id等を取得
MLprojectをrunした場合、 mlflow.start_run をしなくても勝手に引数で設定したexperiment_idやtracking_uriが設定されて処理が実行される。
これらの値は環境変数に設定されているのでスクリプトからも参照可能。
env name | env nameが入ってる定数 |
---|---|
MLFLOW_RUN_ID | mlflow.tracking._RUN_ID_ENV_VAR |
MLFLOW_EXPERIMENT_ID | mlflow.tracking._EXPERIMENT_ID_ENV_VAR |
MLFLOW_TRACKING_URI | mlflow.tracking._TRACKING_URI_ENV_VAR |
MLprojectから呼ばれるコマンドに下記のようなprint文を追加。
print( 'run_id=', os.getenv(mlflow.tracking._RUN_ID_ENV_VAR) ) print( 'experiment_id=', os.getenv(mlflow.tracking._EXPERIMENT_ID_ENV_VAR) ) print( 'tracking_url=', os.getenv(mlflow.tracking._TRACKING_URI_ENV_VAR) )
実行結果
run_id= 7ce8d27eb53b4acc9978e035dc4edbf4 experiment_id= 1 tracking_url= /home/user/work/tf_example/mlruns
entry_pointの指定
上で利用したサンプルでは、entry_pointの名前がmainになっている。
entry_points: main: # ←ココ command: "python train.py
entry_point のデフォルトはmain。それ以外のentry_pointを記述して、実行時にどれを呼び出すか引数 --entry-point で指定することもできる。
下記は前処理と学習処理を分けた例。前処理は1度だけ実行し、その後の学習処理は引数を変えながら回すような場合はentry-pointを分けておくと楽そう。
name: tensorflow_mnist_digit conda_env: conda.yaml entry_points: pre-proc: #前処理 parameters: path: {type: string, default: None} command: "python pre_proc.py" main: #メインの学習処理 parameters: epochs: {type: int, default: 20} command: "python train.py --epochs {epochs}"
前処理を実行後、mainの処理を引数を変えながら実行する例。
$ mlflow run --entry-point pre-proc -P path=foo tf_example $ mlflow run -P epochs=30 tf_example $ mlflow run -P epochs=40 tf_example $ mlflow run -P epochs=50 tf_example
tracking_uriを指定してmlflow run
上述のコマンドではローカルファイルにtracking関連のデータが生成される。mlflow serverでサーバを立ち上げてそれに対してtrackingしてみる。
適当な場所でmlflow serverを立ち上げる。
$ mlflow server \ --backend-store-uri sqlite:///mlruns.sqlite3 \ --default-artifact-root artifact
tracking_uriを指定してexperimentを生成。
$ MLFLOW_TRACKING_URI=http://localhost:5000/ \ mlflow experiments create --experiment-name mnist_digit
mlflow runを実行。
$ MLFLOW_TRACKING_URI=http://localhost:5000/ \ MLFLOW_EXPERIMENT_NAME=mnist_digit \ mlflow run -P epochs=30 tf_example
gitにあるコードに対して実行する
gitにプロジェクトを置いておいて直接実行することもできる。複数のブランチを作ってどのブランチが勝つかといった比較もできる。
例として下記のgithubプロジェクトにdev1とdev2、2つのブランチが切ってありそれぞれ手書き文字認識のコードを実行していみる。
repository | https://github.com/imind-inc/mlflow_example |
branch | dev1, dev2 |
path | mnist_digit/ |
dev1/dev2、それぞれを実行してみる。下記のようにコマンドを記述する。
$ mlflow run {githubのレポジトリ}#{MLprojectのあるディレクトリのパス} --version {ブランチ名}
実行例
# dev1の実行 $ mlflow run https://github.com/imind-inc/mlflow_example.git#mnist_digit \ --version dev1 # dev2の実行 $ mlflow run https://github.com/imind-inc/mlflow_example.git#mnist_digit \ --version dev2 # dev1(epochs=50)の実行 $ mlflow run https://github.com/imind-inc/mlflow_example.git#mnist_digit \ --version dev1 -P epochs=50 # dev2(epochs=50)の実行 $ mlflow run https://github.com/imind-inc/mlflow_example.git#mnist_digit \ --version dev2 -P epochs=50
実行後にuiを見ると4つのrunが登録されている。
versionはブランチ名ではなくコミットIDで表示される。
347ca5はCNNで実装した方でepochs=50を指定した場合がaccuracy=0.994ともっとも良い結果になっている。
versionのリンクからgithubの当該バージョンに飛べる。
githubのprivateレポジトリの利用
githubのprivateレポジトリを利用している場合、毎回ユーザー名とパスワードの入力が求められる。
下記は mlflow_example_private というprivateレポジトリを利用した場合。ユーザー名とパスワードの入力が要求されている。
$ mlflow run https://github.com/imind-inc/mlflow_example_private.git#mnist_digit Username for 'https://github.com': mwsoft Password for 'https://mwsoft@github.com':
.netrc等を使ってパスワードの省略をしていればこの要求は発生しない。
pythonから実行
CLIでmlflow runする以外にもPython上からも叩ける。
mlflow.projects.run( uri='https://github.com/imind-inc/mlflow_example.git#mnist_digit', entry_point='main', version='dev1', parameters={'epochs': 10}, experiment_name='mnist_digit', use_conda=True )
引数の意味
引数名 | 意味 |
---|---|
uri | localやgitのuri |
entry_point | |
version | gitのcommit idとかブランチ名とか |
parameters | -P で渡していた引数 |
experiment_name | |
experiment_id | experimentはnameかidのいずれかを指定可能 |
user_conda | condaで仮想環境を作って実行するか(default=True) |
storage_dir | 一時ファイル等を保存するディレクトリ |
--no-condaオプション
mlflow runはデフォルトでは個別にcondaのenvを生成するので気がつくとmlflow-{uuid}なenvが溜まっていく。
$ conda env list | cut -f1 -d" " base mlflow-23c8fb8fdd7a6f021919868f8cc531b8f3a01939 mlflow-351a306d17b5ac3470475a6e0d1fd1ac75d0eb9c mlflow-6b07d473e5f9dd40eb717254ee64b1dfdbec4a76
必要なライブラリが既に用意されている環境であれば、 --no-conda を指定して新規にenvを作らないように指定できる。
$ mlflow run https://github.com/imind-inc/mlflow_example.git#mnist_digit \ --version dev1 \ --no-conda
メモ。mlflowが作ったenvを滅ぼすコマンド(非推奨)。
for e in `conda env list | grep mlflow- | cut -f1 -d' '` do echo remove $e conda env remove -n $e done
改定履歴
Author: Masato Watanabe, Date: 2019-06-14, 記事投稿