当前位置: 首页 > 工具软件 > MLflow > 使用案例 >

MLOps极致细节:6. MLFlow docker案例,Windows平台运行(附代码)

公瑞
2023-12-01

MLOps极致细节:6. MLFlow docker案例,Windows平台运行(附代码)

在这个博客中,我们将基于mlflow的官方案例来解读如何使用docker环境运行mlflow代码。我们使用的平台是Win10,但在运行过程中,我们发现在Win10平台上操作会出现bug,由于目前手头上正巧没有Linux主机,所以只能继续使用Win10平台,并在本文中详细解释bug的原因(docker: Error response from daemon: invalid mode)以及如何更正。

我们先将代码clone下来:git clone https://gitee.com/yichaoyyds/mlflow-ex-docker-basic.git。然年后进入文件夹mlflow-ex-docker-basic

  • 平台:Win10
  • IDE:Visual Studio Code
  • 需要预装:Anaconda3


1 Yaml文件

根本上,MLFlow Projects的作用就是让其他数据科学家更方便地运行MLFlow项目。在原本的MLFlow项目的基础上,我们需要增加一个MLProject的yaml文件,在项目的根目录下。

一个MLProject的例子如下:

name: docker-example
docker_env:
  image:  mlflow-docker-example
entry_points:
  main:
    parameters:
      alpha: float
      l1_ratio: {type: float, default: 0.1}
    command: "python train.py --alpha {alpha} --l1-ratio {l1_ratio}"

需要注意的是,这里我们选择基于docker来搭环境,所以yaml文件里需要写docker_env: ...,而如果我们选择基于conda来大环境,那么yaml文件里需要写conda_env: conda.yaml。关于conda的案例参见之前的博客。另外,需要注意,image的命名,在之后docker build的时候需要一致。

2 Dockerfile文件

这个大家就很熟悉了,这里我们基于miniforge3,然后安装了一些依赖,由于在大陆,直接运行pip可能会超时,所以我就加了镜像源。

FROM condaforge/miniforge3
RUN pip install mlflow>=1.0 -i https://pypi.tuna.tsinghua.edu.cn/simple\
    && pip install azure-storage-blob==12.3.0 -i https://pypi.tuna.tsinghua.edu.cn/simple\
    && pip install numpy==1.21.2 -i https://pypi.tuna.tsinghua.edu.cn/simple\
    && pip install scipy -i https://pypi.tuna.tsinghua.edu.cn/simple\
    && pip install pandas==1.3.3 -i https://pypi.tuna.tsinghua.edu.cn/simple\
    && pip install scikit-learn==0.24.2 -i https://pypi.tuna.tsinghua.edu.cn/simple\
    && pip install cloudpickle -i https://pypi.tuna.tsinghua.edu.cn/simple

接下来,我们创建镜像:

docker build -t mlflow-docker-example -f Dockerfile .

最后那个.不要漏了,指的是当前路径下的Dockerfile文件。成功后,我们在terminal中输入docker image list,应该就能看到新建的镜像了。需要注意镜像和容器的区别,当前,我们只是建了一个镜像,并没有“实例化”成容器,所以如果我们在terminal中输入docker container ls,我们不会看到有相关容器正在运行。

如果你对docker的操作还不是很熟悉,那么可以参考我的docker每日一阅系列博客

3 主文件

train.py文件是我们需要运行的主文件。在这个文件中,我们通过sklearn来训练一个模型,去预测Wine Quality。当然,这只是一个非常简单的例子,模型本身并没有太多可借鉴性,但我们可以从这个例子中学习如何将这个代码放到docker container上去运行,然后在本地查看结果。

代码的逻辑很简单,包括读取数据集并拆分训练和测试集:

wine_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "wine-quality.csv")
data = pd.read_csv(wine_path)
train, test = train_test_split(data)
train_x = train.drop(["quality"], axis=1)
test_x = test.drop(["quality"], axis=1)
train_y = train[["quality"]]
test_y = test[["quality"]]

然后新建一个mlflow run: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)

最后将这些结果的数据保存到mlflow的日志中:

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)

4 MLFlow Project Run

下一步就是需要在容器中运行train.py代码,正常的操作是,只需要在terminal中输入:

mlflow run . -P alpha=0.5

注意我们当前位置(如果我们ls一下,就能看到train.py以及MLproject),所以上面这条指令中,.指的是当前路径下,系统会自己去找MLproject这个文件。

在我们运行后(Win10平台下),我们“惊讶地”发现,运行不成功,terminal中的日志如下:

2022/03/09 10:34:54 INFO mlflow.projects.docker: === Building docker image docker-example:40a87f9 ===
2022/03/09 10:34:55 INFO mlflow.projects.docker: Temporary docker context file C:\XXX\Local\Temp\tmp3s4p1222 was not deleted.
2022/03/09 10:34:55 INFO mlflow.projects.utils: === Created directory C:\XXX\Local\Temp\tmpgk0laptb for downloading remote URIs passed to arguments of type 'path' ===
2022/03/09 10:34:55 INFO mlflow.projects.backend.local: === Running command 'docker run --rm -v D:\XXX\mlruns:/mlflow/tmp/mlruns -v D:\XXX\mlruns\0\5cd6ea07fe594a7995dc11cc6c79568e\artifacts:D:\XXX\mlruns\0\5cd6ea07fe594a7995dc11cc6c79568e\artifacts -e MLFLOW_RUN_ID=5cd6ea07fe594a7995dc11cc6c79568e -e MLFLOW_TRACKING_URI=file:///mlflow/tmp/mlruns -e MLFLOW_EXPERIMENT_ID=0 docker-example:40a87f9 python train.py --alpha 0.5 
--l1-ratio 0.1' in run with ID '5cd6ea07fe594a7995dc11cc6c79568e' ===
docker: Error response from daemon: invalid mode: \XXX\mlruns\0\5cd6ea07fe594a7995dc11cc6c79568e\artifacts.
See 'docker run --help'.
2022/03/09 10:34:56 ERROR mlflow.cli: === Run (ID '5cd6ea07fe594a7995dc11cc6c79568e') failed ===

关于Docker Container的使用描述,mlflow的官网是这么写的:

When you run an MLflow project that specifies a Docker image, MLflow adds a new Docker layer that copies the project’s contents into the /mlflow/projects/code directory. This step produces a new image. MLflow then runs the new image and invokes the project entrypoint in the resulting container.

Environment variables, such as MLFLOW_TRACKING_URI, are propagated inside the Docker container during project execution. Additionally, runs and experiments created by the project are saved to the tracking server specified by your tracking URI. When running against a local tracking URI, MLflow mounts the host system’s tracking directory (e.g., a local mlruns directory) inside the container so that metrics, parameters, and artifacts logged during project execution are accessible afterwards.

我们综合上两端文字和日志,来分析一下其中的逻辑。

首先,我们基于dockerfile创建一个image(镜像):docker build -t mlflow-docker-example -f Dockerfile .。这个没问题。

然后,当我们运行mlflow run的时候,mlflow会基于这个镜像的基础上再加一层,build出一个新的镜像,目的是在这个新的镜像内,把项目的这些代码放到/mlflow/projects/code这个路径下。我们看到,在terminal的返回日志中是这么记录的:2022/03/09 10:34:54 INFO mlflow.projects.docker: === Building docker image docker-example:40a87f9 ===。虽然没有全部运行成功,但这个新的镜像是建好了,如果我们在terminal中输入docker image list,会看到:

REPOSITORY              TAG       IMAGE ID       CREATED          SIZE
docker-example          40a87f9   be0b149cae42   10 minutes ago   924MB
mlflow-docker-example   latest    f521cdac5b17   41 hours ago     924MB

而且,这个镜像因为后面执行了docker run的操作也实例化了一个让我们进入这个容器。我们输入docker container ls -a会看到:

CONTAINER ID   IMAGE                    COMMAND               CREATED          STATUS
        PORTS     NAMES
2b721f33cdf1   docker-example:40a87f9   "tini -- /bin/bash"   38 seconds ago   Up 37 seconds      
                  suspicious_nash

我们进入这个容器:

docker run -it docker-example:40a87f9

lspwd的结果如下:

(base) root@2b721f33cdf1:/mlflow/projects/code# ls                                                a87f9
Dockerfile  Dockerfile.mlflow-autogenerated  MLproject  README.md  README.rst  mlruns  train.py  wine-quality.csv                                                                                   ine-quality.csv
(base) root@2b721f33cdf1:/mlflow/projects/code# pwd
/mlflow/projects/code

所以,确实如官方文档中所说,项目的代码都被放到/mlflow/projects/code这个路径下,这个是mlflow自动执行的,在我们的Dockerfile中,并没有使用COPY以及WORKDIR的命令。

然后就是出错的一步了,在terminal中:

2022/03/09 10:34:55 INFO mlflow.projects.backend.local: === Running command 'docker run --rm -v D:\XXX\mlruns:/mlflow/tmp/mlruns -v D:\XXX\mlruns\0\5cd6ea07fe594a7995dc11cc6c79568e\artifacts:D:\XXX\mlruns\0\5cd6ea07fe594a7995dc11cc6c79568e\artifacts -e MLFLOW_RUN_ID=5cd6ea07fe594a7995dc11cc6c79568e -e MLFLOW_TRACKING_URI=file:///mlflow/tmp/mlruns -e MLFLOW_EXPERIMENT_ID=0 docker-example:40a87f9 python train.py --alpha 0.5 
--l1-ratio 0.1' in run with ID '5cd6ea07fe594a7995dc11cc6c79568e' ===
docker: Error response from daemon: invalid mode: \XXX\mlruns\0\5cd6ea07fe594a7995dc11cc6c79568e\artifacts.
See 'docker run --help'.
2022/03/09 10:34:56 ERROR mlflow.cli: === Run (ID '5cd6ea07fe594a7995dc11cc6c79568e') failed ===

问题是处在路径的书写上,比如:D:\XXX\mlruns\,这个路径的正确写法应该是/d/XXX/mlruns/。我们重新写一下docker run指令:

docker run --rm -v /d/XXX/mlruns:/mlflow/tmp/mlruns -v /d/XXX/mlruns/0/5cd6ea07fe594a7995dc11cc6c79568e/artifacts:/d/XXX/mlruns/0/5cd6ea07fe594a7995dc11cc6c79568e/artifacts -e MLFLOW_RUN_ID=5cd6ea07fe594a7995dc11cc6c79568e -e MLFLOW_TRACKING_URI=file:///mlflow/tmp/mlruns -e MLFLOW_EXPERIMENT_ID=0 docker-example:40a87f9 python train.py --alpha 0.5 --l1-ratio 0.1

就可以正常运行了。terminal中返回

Elasticnet model (alpha=0.500000, l1_ratio=0.100000):
  RMSE: 0.7947931019036528
  MAE: 0.6189130834228137
  R2: 0.1841166871822183

这个问题应该在linux环境下不会出现,因为Windows环境下的路径书写规则与Linux不大一样而导致error。

 类似资料: