MLflow 项目

MLflow 项目是一种用于以可重用和可再现的方式打包数据科学代码的格式,主要基于约定。此外,项目组件包括一个 API 和命令行工具,用于运行项目,使得将项目串联成工作流成为可能。

概述

在核心部分,MLflow 项目只是组织和描述代码的一种约定,以便其他数据科学家(或自动化工具)可以运行它。每个项目只是一个包含代码的文件目录或 Git 仓库。MLflow 可以根据在此目录中放置文件的约定运行某些项目(例如,conda.yaml 文件被视为 Conda 环境),但您可以通过添加一个 MLproject 文件来更详细地描述您的项目,这是一个 YAML 格式的文本文件。每个项目可以指定几个属性:

名称

项目的可读名称。

入口点

可以在项目中运行的命令及其参数信息。大多数项目至少包含一个您希望其他用户调用的入口点。一些项目也可以包含多个入口点:例如,您可能有一个包含多个特征化算法的单一Git仓库。您也可以将项目中的任何 .py.sh 文件作为入口点调用。如果您在 MLproject 文件中列出您的入口点,您还可以为它们指定 参数 ,包括数据类型和默认值。

环境

用于执行项目入口点的软件环境。这包括项目代码所需的所有库依赖项。有关MLflow项目支持的软件环境的更多信息,请参阅 项目环境 ,包括 Conda环境Virtualenv环境Docker容器

你可以使用 mlflow run 命令行工具或 mlflow.projects.run() Python API 从 Git URI 或本地目录运行任何项目。这些 API 还允许将项目提交到 DatabricksKubernetes 进行远程执行。

重要

默认情况下,MLflow 为 Git 项目使用一个新的临时工作目录。这意味着您通常应该使用绝对路径而不是相对路径将任何文件参数传递给 MLflow 项目。如果您的项目声明了其参数,MLflow 会自动为类型为 path 的参数将路径设为绝对路径。

指定项目

默认情况下,任何 Git 仓库或本地目录都可以被视为 MLflow 项目;你可以将目录中包含的任何 bash 或 Python 脚本作为项目入口点调用。项目目录 部分描述了 MLflow 如何将目录解释为项目。

为了对项目的属性提供额外的控制,您还可以在项目的仓库或目录中包含一个 MLproject 文件

最后,MLflow 项目允许您指定用于执行项目入口点的软件 环境

项目环境

MLflow 目前支持以下项目环境:Virtualenv 环境、conda 环境、Docker 容器环境和系统环境。

虚拟环境 (首选)

Virtualenv 环境支持 PyPI 上可用的 Python 包。当一个 MLflow 项目指定了一个 Virtualenv 环境时,MLflow 将使用 pyenv 下载指定版本的 Python,并使用 virtualenv 创建一个包含项目依赖的隔离环境,在运行项目代码之前将其激活为执行环境。

你可以在你的 MLflow 项目中通过在你的 MLproject 文件中包含一个 python_env 条目来指定一个 Virtualenv 环境。详情请参阅 项目目录指定环境 部分。

Docker 容器环境

Docker 容器 允许你捕获非 Python 依赖项,如 Java 库。

当你运行一个指定了 Docker 镜像的 MLflow 项目时,MLflow 会按照 MLproject 文件中指定的参数运行你的镜像。在这种情况下,你需要预先构建包含环境和代码的镜像以运行它。要使用基于你的镜像并包含项目内容的新镜像在 /mlflow/projects/code 目录中运行项目,请在运行 mlflow run 时使用 --build-image 标志。

环境变量,例如 MLFLOW_TRACKING_URI,在项目执行期间会在Docker容器内传递。此外,项目创建的 运行和实验 会保存到由您的 跟踪URI 指定的跟踪服务器中。当针对本地跟踪URI运行时,MLflow会将主机系统的跟踪目录(例如,本地 mlruns 目录)挂载到容器内,以便在项目执行期间记录的指标、参数和工件在之后可以访问。

请参阅 使用 MLflow 进行 Docker 化的模型训练 ,这是一个带有 Docker 环境的 MLflow 项目示例。

要指定一个 Docker 容器环境,您 必须 在项目中添加一个 MLproject 文件。有关在 MLproject 文件中指定 Docker 容器环境的信息,请参阅 指定环境

Conda 环境

Conda 环境支持 Python 包和本地库(例如,CuDNN 或 Intel MKL)。当一个 MLflow 项目指定了一个 Conda 环境时,它会在项目代码运行之前被激活。

警告

通过使用 conda,您有责任遵守 Anaconda 的服务条款

默认情况下,MLflow 使用系统路径来查找并运行 conda 二进制文件。你可以通过设置 MLFLOW_CONDA_HOME 环境变量来使用不同的 Conda 安装;在这种情况下,MLflow 尝试运行位于 $MLFLOW_CONDA_HOME/bin/conda 的二进制文件。

你可以通过在项目目录的根目录中包含一个 conda.yaml 文件,或者在你的 MLproject 文件中包含一个 conda_env 条目,来为你的 MLflow 项目指定一个 Conda 环境。详情请参见 项目目录指定环境 部分。

mlflow run 命令支持将 conda 环境项目作为 virtualenv 环境项目运行。为此,请使用 --env-manager virtualenv 运行 mlflow run

mlflow run /path/to/conda/project --env-manager virtualenv

警告

当一个 conda 环境项目作为 virtualenv 环境项目执行时,conda 依赖将被忽略,只有 pip 依赖将被安装。

系统环境

您也可以直接在当前系统环境中运行 MLflow 项目。在项目执行之前,必须先在系统上安装所有项目的依赖项。系统环境是在运行时提供的。它不是 MLflow 项目目录内容或 MLproject 文件的一部分。有关在运行项目时使用系统环境的信息,请参阅 运行项目 部分中的 Environment 参数描述。

项目目录

当运行一个不包含 MLproject 文件的 MLflow 项目目录或仓库时,MLflow 使用以下约定来确定项目的属性:

  • 项目的名称是目录的名称。

  • Conda 环境conda.yaml 中指定,如果存在的话。如果没有 conda.yaml 文件,MLflow 在运行项目时会使用仅包含 Python(特别是 Conda 可用的最新 Python)的 Conda 环境。

  • 项目中的任何 .py.sh 文件都可以作为入口点。MLflow 使用 Python 执行带有 .py 扩展名的入口点,并使用 bash 执行带有 .sh 扩展名的入口点。有关在运行时指定项目入口点的更多信息,请参阅 运行项目

  • 默认情况下,当没有包含 MLproject 文件时,入口点没有任何参数。参数可以通过 mlflow run CLI 或在 mlflow.projects.run() Python API 中在运行时提供。运行时参数使用 --key value 语法在命令行上传递给入口点。有关运行项目和使用运行时参数的更多信息,请参阅 运行项目

MLproject 文件

你可以通过在项目的根目录添加一个 MLproject 文件来获得对 MLflow 项目的更多控制,该文件是一个使用 YAML 语法的文本文件。以下是一个 MLproject 文件的示例:

name: My Project

python_env: python_env.yaml
# or
# conda_env: my_env.yaml
# or
# docker_env:
#    image:  mlflow-docker-example

entry_points:
  main:
    parameters:
      data_file: path
      regularization: {type: float, default: 0.1}
    command: "python train.py -r {regularization} {data_file}"
  validate:
    parameters:
      data_file: path
    command: "python validate.py {data_file}"

该文件可以指定一个名称和 Conda 或 Docker 环境,以及每个入口点的更详细信息。具体来说,每个入口点定义了一个 要运行的命令 和 :ref:`传递给命令的参数 <project_parameters>`(包括数据类型)。

指定环境

本节描述如何在 MLproject 文件中指定 Conda 和 Docker 容器环境。MLproject 文件不能同时指定 Conda 环境和 Docker 环境。

虚拟环境

MLproject 文件中包含一个顶级的 python_env 条目。该条目的值必须是一个指向 MLflow 项目目录内 python_env YAML 文件的 相对 路径。以下是一个包含 python_env 定义的 MLProject 文件示例:

python_env: files/config/python_env.yaml

python_env 指的是位于 <MLFLOW_PROJECT_DIRECTORY>/files/config/python_env.yaml 的环境文件,其中 <MLFLOW_PROJECT_DIRECTORY> 是 MLflow 项目根目录的路径。

以下是一个 python_env.yaml 文件的示例:

# Python version required to run the project.
python: "3.8.15"
# Dependencies required to build packages. This field is optional.
build_dependencies:
  - pip
  - setuptools
  - wheel==0.37.1
# Dependencies required to run the project.
dependencies:
  - mlflow==2.3
  - scikit-learn==1.0.2
Conda 环境

MLproject 文件中包含一个顶级的 conda_env 条目。此条目的值必须是一个指向 MLflow 项目目录内的 Conda 环境 YAML 文件相对 路径。在以下示例中:

conda_env: files/config/conda_environment.yaml

conda_env 指的是位于 <MLFLOW_PROJECT_DIRECTORY>/files/config/conda_environment.yaml 的环境文件,其中 <MLFLOW_PROJECT_DIRECTORY> 是 MLflow 项目根目录的路径。

Docker 容器环境

MLproject 文件中包含一个顶级的 docker_env 条目。此条目的值必须是系统执行项目时可访问的 Docker 镜像名称;此镜像名称可以包括注册路径和标签。以下是几个示例。

示例 1:没有注册路径的图像

docker_env:
  image: mlflow-docker-example-environment

在这个例子中,docker_env 指的是名为 mlflow-docker-example-environment 且默认标签为 latest 的 Docker 镜像。因为没有指定注册路径,Docker 会在运行 MLflow 项目的系统上搜索这个镜像。如果找不到镜像,Docker 会尝试从 DockerHub 拉取它。

示例 2:挂载卷并指定环境变量

您还可以指定要在docker镜像中挂载的本地卷(就像通常使用Docker的`-v`选项一样),以及额外的环境变量(根据Docker的`-e`选项)。环境变量可以从主机系统环境变量中复制,或者为Docker环境指定新的变量。`environment`字段应该是一个列表。此列表中的元素可以是两个字符串的列表(用于定义新变量)或单个字符串(用于从主机系统复制变量)。例如:

docker_env:
  image: mlflow-docker-example-environment
  volumes: ["/local/path:/container/mount/path"]
  environment: [["NEW_ENV_VAR", "new_var_value"], "VAR_TO_COPY_FROM_HOST_ENVIRONMENT"]

在这个例子中,我们的Docker容器将挂载一个额外的本地卷,并设置两个额外的环境变量:一个是新定义的,另一个是从主机系统复制的。

示例 3:远程注册表中的图像

docker_env:
  image: 012345678910.dkr.ecr.us-west-2.amazonaws.com/mlflow-docker-example-environment:7.0

在这个例子中,docker_env 指的是 Docker 镜像,其名称为 mlflow-docker-example-environment,标签为 7.0,位于路径为 012345678910.dkr.ecr.us-west-2.amazonaws.com 的 Docker 注册表中,这对应于一个 Amazon ECR 注册表。当运行 MLflow 项目时,Docker 会尝试从指定的注册表中拉取镜像。执行 MLflow 项目的系统必须具有从指定注册表拉取此镜像的凭证。

示例 4:构建新镜像

docker_env:
  image: python:3.8
mlflow run ... --build-image

要构建一个基于指定镜像和项目目录中文件的新镜像,请使用 --build-image 参数。在上面的示例中,如果本地不存在镜像 python:3.8 ,则从 Docker Hub 拉取该镜像,并基于它构建一个新镜像。项目在从此镜像创建的容器中执行。

命令语法

MLproject 文件中指定入口点时,命令可以是 Python 格式字符串语法 中的任何字符串。入口点的 parameters 字段中声明的所有参数都将传递到此字符串中进行替换。如果您使用未列在 parameters 字段中的其他参数调用项目,MLflow 将使用 --key value 语法传递它们,因此您可以使用 MLproject 文件仅为参数的子集声明类型和默认值。

在命令中替换参数之前,MLflow 使用 Python shlex.quote 函数对其进行转义,因此您无需担心在命令字段中添加引号。

指定参数

MLflow 允许为每个参数指定数据类型和默认值。你可以通过以下方式仅指定数据类型:

parameter_name: data_type

在你的 YAML 文件中,或者使用以下任一语法添加一个默认值(在 YAML 中它们是等效的):

parameter_name: {type: data_type, default: value}  # Short syntax

parameter_name:     # Long syntax
  type: data_type
  default: value

MLflow 支持四种参数类型,其中一些类型它有特殊处理(例如,将数据下载到本地文件)。任何未声明的参数都被视为 string。参数类型包括:

字符串

一个文本字符串。

浮动

一个实数。MLflow 验证该参数是一个数字。

路径

本地文件系统上的路径。MLflow 将任何相对的 path 参数转换为绝对路径。MLflow 还会将作为分布式存储 URI(s3://dbfs://gs:// 等)传递的任何路径下载到本地文件。对于只能读取本地文件的程序,请使用此类型。

uri

数据在本地或分布式存储系统中的URI。MLflow 将相对路径转换为绝对路径,如 path 类型。对于知道如何从分布式存储读取的程序(例如,使用 Spark 的程序),请使用此类型。

运行项目

MLflow 提供了两种运行项目的方式:mlflow run 命令行工具,或 mlflow.projects.run() Python API。这两种工具都接受以下参数:

项目URI

本地文件系统上的目录或 Git 仓库路径,指定为 https://<repo> 形式的 URI(使用 HTTPS)或 ``user@host:path``(使用 Git over SSH)。要针对位于项目子目录中的 MLproject 文件运行,请在 URI 参数的末尾添加一个 ‘#’,后跟从项目根目录到包含所需项目的子目录的相对路径。

项目版本

对于基于 Git 的项目,Git 仓库中的提交哈希或分支名称。

入口点

入口点的名称,默认为 main 。你可以使用 MLproject 文件中命名的任何入口点,或者项目中任何 .py.sh 文件,给定为从项目根目录开始的路径(例如,src/test.py)。

参数

键值参数。任何带有 声明类型 的参数都会被验证并在需要时进行转换。

部署模式
  • 命令行和API都允许你在 Databricks 环境中 远程启动项目 。这包括设置集群参数,如虚拟机类型。当然,你也可以使用 mlflow run 命令的本地版本来在任何其他你选择的计算基础设施上运行项目(例如,提交一个执行 mlflow run 的脚本到一个标准的作业排队系统)。

  • 你也可以使用 mlflow run CLI 在 Kubernetes 集群上远程启动项目(参见 在 Kubernetes 上运行 MLflow 项目)。

环境

默认情况下,MLflow 项目在项目目录或 MLproject 文件指定的环境中运行(参见 指定项目环境)。你可以通过提供 --env-manager=local 标志来忽略项目指定的环境,并在当前系统环境中运行项目,但如果项目环境和当前系统环境之间存在依赖性不匹配,这可能会导致意外结果。

例如,该教程创建并发布了一个训练线性模型的 MLflow 项目。该项目也在 GitHub 上发布,地址为 https://github.com/mlflow/mlflow-example。要运行此项目:

mlflow run git@github.com:mlflow/mlflow-example.git -P alpha=0.5

还有一些额外的选项可以禁用创建 Conda 环境,如果你希望在现有的 shell 环境中快速测试一个项目,这会很有用。

在 Databricks 上运行 MLflow 项目

你可以在 Databricks 上远程运行 MLflow 项目。要使用此功能,你必须拥有一个企业版 Databricks 账户(不支持社区版),并且你必须已经设置好 Databricks CLI。在 Databricks 文档中找到详细说明(Azure DatabricksDatabricks on AWS)。

在 Kubernetes 上运行 MLflow 项目

你可以在 Kubernetes 上使用 Docker 环境 运行 MLflow 项目。以下部分提供了该功能的概述,包括一个带有示例的简单项目执行指南。

要查看此功能的实际操作,您还可以参考 Docker 示例,其中包含所需的 Kubernetes 后端配置 (kubernetes_backend.json) 和 Kubernetes Job Spec (kubernetes_job_template.yaml) 文件。

工作原理

当您在 Kubernetes 上运行 MLflow 项目时,MLflow 会构建一个包含项目内容的新 Docker 镜像;此镜像继承自项目的 Docker 环境。然后,MLflow 将新的项目镜像推送到您指定的 Docker 注册表,并在您指定的 Kubernetes 集群上启动一个 Kubernetes Job。此 Kubernetes Job 下载项目镜像并启动相应的 Docker 容器。最后,容器调用您项目的 入口点,将参数、标签、指标和工件记录到您的 MLflow 跟踪服务器

执行指南

你可以通过以下步骤在Kubernetes上运行你的MLflow项目:

  1. 如果尚不存在,请为您的 MLflow 项目添加一个 Docker 环境。参考 指定环境

  2. 创建一个包含以下条目的后端配置 JSON 文件:

    • kube-context Kubernetes 上下文 ,MLflow 将在此上下文中运行作业。如果未提供,MLflow 将使用当前上下文。如果没有可用的上下文,MLflow 将假定它正在 Kubernetes 集群中运行,并将使用当前 pod 运行的 Kubernetes 服务账户(’集群内’ 配置)。

    • repository-uri 是用于上传(推送)项目执行Docker镜像的Docker仓库的URI。您的Kubernetes集群必须能够访问此仓库,以便运行您的MLflow项目。

    • kube-job-template-path 指向 Kubernetes Job 的 YAML 配置文件路径 - 一个 Kubernetes Job 规范。MLflow 读取 Job 规范并替换某些字段以促进作业执行和监控;MLflow 不会修改原始模板文件。有关为 MLflow 编写 Kubernetes Job 规范模板的更多信息,请参阅 作业模板 部分。

Kubernetes 后端配置示例

{
  "kube-context": "docker-for-desktop",
  "repository-uri": "username/mlflow-kubernetes-example",
  "kube-job-template-path": "/Users/username/path/to/kubernetes_job_template.yaml"
}
  1. 如有必要,获取访问您的项目Docker和Kubernetes资源的凭证,包括:

    • 在 MLproject 文件中指定的 Docker 环境镜像

    • 在您的后端配置文件中,由 repository-uri 引用的 Docker 仓库。

    • 您的后端配置文件中引用的 Kubernetes 上下文 kube-context

    MLflow 期望在运行项目之前,这些资源可以通过 dockerkubectl 命令行界面访问。

  2. 使用 MLflow Projects CLI 或 Python API 运行项目,指定项目 URI 和后端配置文件的路径。例如:

    mlflow run <project_uri> --backend kubernetes --backend-config examples/docker/kubernetes_config.json
    

    其中 <project_uri> 是一个 Git 仓库 URI 或一个文件夹。

作业模板

MLflow 通过创建 Kubernetes Job 资源 在 Kubernetes 上执行项目。MLflow 通过读取用户指定的 Job Spec 为 MLflow 项目创建一个 Kubernetes Job。当 MLflow 读取 Job Spec 时,它会格式化以下字段:

  • metadata.name 被替换为一个包含 MLflow 项目名称和项目执行时间的字符串

  • spec.template.spec.container[0].name 替换为 MLflow 项目的名称

  • spec.template.spec.container[0].image 被替换为项目执行期间创建的Docker镜像的URI。此URI包含Docker镜像的摘要哈希。

  • spec.template.spec.container[0].command 被替换为执行 MLflow 项目时指定的项目入口点命令。

以下示例展示了一个与 MLflow 项目执行兼容的简单 Kubernetes Job 规范。替换的字段使用方括号文本表示。

示例 Kubernetes Job 规范

apiVersion: batch/v1
kind: Job
metadata:
  name: "{replaced with MLflow Project name}"
  namespace: mlflow
spec:
  ttlSecondsAfterFinished: 100
  backoffLimit: 0
  template:
    spec:
      containers:
      - name: "{replaced with MLflow Project name}"
        image: "{replaced with URI of Docker image created during Project execution}"
        command: ["{replaced with MLflow Project entry point command}"]
        env: ["{appended with MLFLOW_TRACKING_URI, MLFLOW_RUN_ID and MLFLOW_EXPERIMENT_ID}"]
        resources:
          limits:
            memory: 512Mi
          requests:
            memory: 256Mi
      restartPolicy: Never

container.namecontainer.imagecontainer.command 字段仅对 Job Spec 中定义的 第一个 容器进行替换。此外,MLFLOW_TRACKING_URIMLFLOW_RUN_IDMLFLOW_EXPERIMENT_ID 会被附加到 container.env 中。使用 KUBE_MLFLOW_TRACKING_URI 将不同的跟踪 URI 传递给作业容器,而不是标准的 MLFLOW_TRACKING_URI。所有后续的容器定义将不作修改地应用。

快速迭代

如果你想快速开发一个项目,我们建议创建一个 MLproject 文件,并将你的主程序指定为 main 入口点,然后使用 mlflow run . 运行它。为了避免重复编写参数,你可以在 MLproject 文件中添加默认参数。

构建多步骤工作流程

使用 mlflow.projects.run() API 结合 mlflow.client,可以构建多步骤工作流,其中每个步骤可以是独立的项目(或同一项目中的入口点)。每次调用 mlflow.projects.run() 都会返回一个运行对象,您可以使用 mlflow.client 来确定运行何时结束并获取其输出工件。这些工件随后可以传递到另一个接受 pathuri 参数的步骤中。您可以在一个Python程序中协调整个工作流,该程序查看每个步骤的结果,并使用自定义代码决定下一步提交什么。多步骤工作流的一些示例用例包括:

模块化你的数据科学代码

不同的用户可以发布可重用的步骤用于数据特征化、训练、验证等,其他用户或团队可以在他们的工作流中运行这些步骤。因为 MLflow 支持 Git 版本控制,另一个团队可以将他们的工作流锁定到项目的特定版本,或者按照自己的时间表升级到新版本。

超参数调优

使用 mlflow.projects.run() 你可以在本地机器或像 Databricks 这样的云平台上并行启动多个运行。你的驱动程序可以实时检查每个运行的指标,以取消运行、启动新的运行,或在目标指标上选择表现最好的运行。

交叉验证

有时你想在训练和验证数据的不同随机分割上运行相同的训练代码。使用 MLflow 项目,你可以以一种允许这种方式打包项目,例如,通过将训练/验证分割的随机种子作为参数,或者首先调用另一个可以分割输入数据的项目。

有关如何构建此类多步骤工作流的示例,请参阅 MLflow 的 多步骤工作流示例项目