MLflowのログデータ移行方法　～学習済みモデルを共有して使いまわす～

# サマリー

機械学習の結果を保存する際にMLflowを利用している。
特にPyTorchのモデルを保存して使いまわすとき、`run_id`と呼ばれるハッシュ値を指定すれば何も考えずに学習済みモデルをロードできるので非常に便利だ。

この記事では、サーバAで学習したモデルをサーバBに丸ごとお引越しする方法についての備忘録である。

## 前提 ～MLflowの保存形式～

保存用サーバを立てない場合、MLflowのログデータはすべて作業用ディレクトリ直下にある`mlruns`ディレクトリの中に保存される。
たとえば、いまサーバAの作業用ディレクトリが`/home/serverA/tutorial`だとすると、学習ジョブ単位で`/home/serverA/tutorial/mlruns/<experiment_id>/<run_id>`ディレクトリに保存されることになる。

### experiment_id
ここで、`<experiment_id>`は0から始まる整数である。
その実験がどういうものかは`/home/serverA/tutorial/mlruns/<experiment_id>/meta.yaml`に記述されている。

### run_id
次に、`<run_id>`は各ジョブごとに割り当てられたハッシュ値になっている。
フォルダ名そのものがハッシュ値になっているので(読みにくいが)分かりやすい。

ドキュメント（[MLflow Document | mlflow.pyfunc.load_model()](https://www.mlflow.org/docs/latest/python_api/mlflow.pyfunc.html#mlflow.pyfunc.load_model)）を読むとわかるように、
`model = mlflow.pyfunc.load_model("runs:/<run_id>/model")`などとすることで学習済みモデルを読み込める。
いちいちモデルをインスタンス化してからパラメータをロード、みたいなことをしなくていいので極めて便利だ。


## つまり、ここでやりたいこととは

`/home/serverA/tutorial/mlruns/3/HASH_001`に保存されているサーバAで学習させたモデルを、
`/home/serverB/tutorial/mlruns/7/HASH_001`に移行することでサーバBでも使いまわしたい、
ということになる。

手順のサマリは以下の通り：

1. 移行先ディレクトリをサーバBにつくる
1. サーバAからサーバBに`<run_id>`ディレクトリ丸ごとをコピー(お引越し)
1. コピーしたデータのメタファイルを修正



# (1) 移行先ディレクトリの用意（experimentフォルダの作成）

たとえば、移行元のサーバAの`./mlruns/<experiment_id>/meta.yaml`の中身はこんな感じになっている。

```yaml:/home/serverA/tutorial/mlruns/3/meta.yaml
artifact_location: file:///home/serverA/tutorial/mlruns/3
experiment_id: '3'
lifecycle_stage: active
name: ServerA-TrainedModels
```

この`./mlruns/<experiment_id>`ディレクトリとその中に入っている`./mlruns/<experiment_id>/meta.yaml`は、通常はMLflowが自動で作成してくれるものだが、手動で用意しても読み込んでくれる。

したがって、移行先のサーバBに、新しく`<experiment_id>`ディレクトリをつくり、それに合わせて`./mlruns/<experiment_id>/meta.yaml`の中身を書き換えればよい。

ここでの例に従えば、サーバBにて、`/home/serverB/tutorial/mlruns/7`ディレクトリを新規作成し、その中に`/home/serverB/tutorial/mlruns/7/meta.yaml`を用意し、次のように書き換える。

```yaml:/home/serverB/tutorial/mlruns/7/meta.yaml
artifact_location: file:///home/serverB/tutorial/mlruns/7
experiment_id: '7'
lifecycle_stage: active
name: TrainedModels-fromServerA
```

**※新しい`./mlruns/<experiment_id>`を作らず、すでに他の実行結果が入っているディレクトリを流用しても構わない。
その場合、`./mlruns/<experiment_id>/meta.yaml`はもう作成されているものでよいので、(1)の作業は飛ばしていきなり(2)に進めばよい。**

# (2) データの移動

サーバAの`<run_id>`ディレクトリを丸ごとコピーして移行させる。
方法は適当にWinSCPとか、scpコマンドとか好きな方法で。。。

本例では、サーバBの`/home/serverB/tutorial/mlruns/7`直下に、`HASH_001`ディレクトリコピーしてきて配置するものとして話を続ける。

# (3) `run_id`のメタファイルを編集

実は、`<run_id>`直下にも`meta.yaml`というファイルがある。
ここに書かれているメタ情報をMLflowが読み込んで認識するようになるので、移行先のサーバBに置かれているこのメタファイルを編集し、サーバB環境に合わせた内容に書き換えればよい。

サーバAから移行した直後は、おそらくこんな感じになっているはず。

```yaml:/home/serverB/tutorial/mlruns/7/HASH_001/meta.yaml
artifact_uri: file:///home/serverA/tutorial/mlruns/3/HASH_001/artifacts
end_time: 1643299186272
entry_point_name: ''
experiment_id: '3'
lifecycle_stage: active
name: ''
run_id: HASH_001
run_uuid: HASH_001
source_name: ''
source_type: 4
source_version: ''
start_time: 1643296614316
status: 3
tags: []
user_id: <user-id>
```

見れば明らかなように、`artifact_uri`と`experiment_id`がサーバAのときのままになっているので、これをサーバBに合わせて次のように修正すればよい。

```yaml:/home/serverB/tutorial/mlruns/7/HASH_001/meta.yaml
artifact_uri: file:///home/serverA/tutorial/mlruns/7/HASH_001/artifacts
end_time: 1643299186272
entry_point_name: ''
experiment_id: '7'
lifecycle_stage: active
name: ''
run_id: HASH_001
run_uuid: HASH_001
source_name: ''
source_type: 4
source_version: ''
start_time: 1643296614316
status: 3
tags: []
user_id: <user-id>
```

この状態で`$ mlflow ui`をしてGUIサーバをサーバBで立ち上げれば、問題なく`HASH_001`のデータが移行されて見えるようになる。

無事にお引越し完了！！