【機械学習】MLflowのQuickstartやってみた(3)


前回の記事

前回の記事の続きです。

環境

  • macOS High Sierra
  • pyenv 1.2.1
  • anaconda3-5.0.1
  • Python 3.6.8
  • MLflow==0.8.2

モデルを保存する

MLflowでは通常通りに機械学習のモデルファイルをそのままArtifactsに保存することが出来ます。
それに加えてMLmodelと呼ばれる形式でモデルを保存することが可能です。

MLmodelとは

いくつかのプラットフォームのモデルを統一的に扱えるようにしたもの。
ここを見た感じでは、azulemlh2okeraspytorchsagemakersklearntensorflowなどなど(知らないものもいくつか。。。)で保存したモデルについては統一的な扱いが出来そうな気がします。
前回の記事で使用したサンプルプロジェクトでは以下のようなコードでsklearnのモデルを保存していました。

mlflow-example/train.py
mlflow.sklearn.log_model(lr, "model")

保存される内容としては以下の3ファイルのようです。

MLmodel

統一的な呼び出しを実現するための管理ファイルのようです。
ライブラリのバージョンやどのプラットフォームで保存したモデルかなどが記載されています。

MLmodel
artifact_path: model
flavors:
  python_function:
    data: model.pkl
    env: conda.yaml
    loader_module: mlflow.sklearn
    python_version: 3.6.8
  sklearn:
    pickled_model: model.pkl
    serialization_format: cloudpickle
    sklearn_version: 0.20.2
run_id: 156398506a8d4a94a27a7bbef191ac39
utc_time_created: '2019-03-02 07:07:43.692121'

conda.yaml

実行に必要な環境が記載されています。
MLprojectの時と同じように、この呼出しの時にも新しい仮想環境が作成されそうです。

conda.yaml
channels:
- defaults
dependencies:
- python=3.6.8
- scikit-learn=0.20.2
- cloudpickle==0.8.0
name: mlflow-env

model.pkl

モデル本体です。

保存したモデルを推論に使う

ではQuickstartに沿ってやってみます。

以下を実行するとsklearnのロジスティック回帰を使用したモデルが保存されます。

$ python sklearn_logistic_regression/train.py

sklearn_logistic_regression/train.pyこのリポジトリexampleディレクトリにあります)

出力はこんな感じ

~/.local/lib/python3.6/site-packages/sklearn/linear_model/logistic.py:433: FutureWarning: Default solver will be changed to 'lbfgs' in 0.22. Specify a solver to silence this warning.
  FutureWarning)
Score: 0.6666666666666666
Model saved in run edf260a6f5ec4813b7ae38a7cef4e19a

ちゃんと実行されてモデルも保存されているようです。
このモデルを使用するには、上記でedf260a6f5ec4813b7ae38a7cef4e19aとなっている<RUN_ID>を使って以下のようにします。

$ mlflow pyfunc serve -r <RUN_ID> -m model
# $ mlflow pyfunc serve -r edf260a6f5ec4813b7ae38a7cef4e19a -m model

mlflow uiなどを起動している場合はデフォルトで5000番ポートを使用しています。mlflow pyfunc serveもデフォルトで5000番ポートを使用するので同時に起動させると以下のようなエラーが発生します。

Traceback (most recent call last):
#  ︙
#(長いので中略)
#  ︙
5.0.1/envs/ml/lib/python3.6/socketserver.py", line 470, in server_bind
    self.socket.bind(self.server_address)
OSError: [Errno 48] Address already in use

このような場合は以下のように使用されていないポート番号(<PORT>)を指定するようにコマンドの変更が必要です。

$ mlflow pyfunc serve --port <PORT> -r <RUN_ID> -m model
# $ mlflow pyfunc serve --port 1234 -r edf260a6f5ec4813b7ae38a7cef4e19a -m model

これで以下のように仮想環境を作成後に推論サーバーが起動します。

2019/03/03 16:17:36 INFO mlflow.projects: === Creating conda environment mlflow-f49dbaff98259ddf94bbf2f77d8d5d47b925c1b3 ===
Using Anaconda API: https://api.anaconda.org
Solving environment: done


==> WARNING: A newer version of conda exists. <==
  current version: 4.4.7
  latest version: 4.6.7

Please update conda by running

    $ conda update -n base conda



Downloading and Extracting Packages
mkl 2019.1: ############################################################################################################################################################## | 100% 
mkl_fft 1.0.10: ########################################################################################################################################################## | 100% 
scikit-learn 0.20.2: ##################################################################################################################################################### | 100% 
mkl_random 1.0.2: ######################################################################################################################################################## | 100% 
scipy 1.2.1: ############################################################################################################################################################# | 100% 
numpy 1.16.2: ############################################################################################################################################################ | 100% 
numpy-base 1.16.2: ####################################################################################################################################################### | 100% 
cloudpickle 0.8.0: ####################################################################################################################################################### | 100% 
Preparing transaction: done
Verifying transaction: done
Executing transaction: done
#
# To activate this environment, use
#
#     $ conda activate mlflow-f49dbaff98259ddf94bbf2f77d8d5d47b925c1b3
#
# To deactivate an active environment, use
#
#     $ conda deactivate

2019/03/03 16:19:45 INFO mlflow.pyfunc.cli: === Running command 'source activate mlflow-f49dbaff98259ddf94bbf2f77d8d5d47b925c1b3 && ~/.local/bin/mlflow pyfunc serve -r edf260a6f5ec4813b7ae38a7cef4e19a -m model --no-conda'
 * Serving Flask app "mlflow.pyfunc.scoring_server" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: off
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)

あとはお好きなようにアクセスしてあげると結果を返してくれるようです。
以下はcurlを使う例です。

# ポート番号をデフォルトから変更している場合は`localhost:5000/invocations`の`5000`を設定した値に変更すること
$ curl -d '{"columns":["x"], "data":[[1], [-1]]}' -H 'Content-Type: application/json; format=pandas-split' -X POST localhost:5000/invocations
127.0.0.1 - - [03/Mar/2019 16:35:54] "POST /invocations HTTP/1.1" 200 -
[1, 0]

おー何やら結果が返ってきました。
機械学習API完成です。

また、注意点として、モデルを保存するコマンド(今回の例では $ python sklearn_logistic_regression/train.py)と使用するコマンド(今回の例では mlflow pyfunc serve -r <RUN_ID> -m model)を実行するPythonのバージョンが同じでない場合は以下のようなエラーが発生するようです。

File "/usr/local/lib/python3.6/site-packages/mlflow/sklearn.py", line 54, in _load_model_from_local_file
return pickle.load(f)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc6 in position 0: ordinal not in range(128)

まとめ

MLmodelを使って、機械学習モデルの保存と推論サーバーの使用方法について実施してみました。

まだQuickstartをやってみただけですが、これくらいの知識でも最低限実用に足りる知識は得たような感覚があります。(もちろん詳細はもっといろいろあるはずですが)

また、MLflowは現時点ではβ番ということなので、本記事の内容は正式リリースの時点では変更になっている可能性がありますので予めご了承ください。