跳到主要内容

MLflow 跟踪 API

MLflow 跟踪 提供 Python, R, Java 或 REST API 来记录您的实验数据和模型。

自动日志记录

自动日志记录 是一个强大的功能,它允许您记录指标、参数和模型,而无需显式的日志记录语句,只需在您的机器学习代码顶部调用一次 mlflow.autolog()

import mlflow

mlflow.autolog()

# Your training code...

有关支持的库以及如何使用它们的自动日志 API,请参阅 使用 MLflow Tracking 进行自动日志记录 文档。

手动日志记录

另外,您也可以通过在您的机器学习代码中添加 log 方法来记录 MLflow 指标。例如

with mlflow.start_run():
for epoch in range(0, 3):
mlflow.log_metric(key="quality", value=2 * epoch, step=epoch)

日志记录函数

以下是跟踪 API (Python) 提供的完整日志记录函数列表。

注意

Java 和 R API 提供了相似但有限的日志记录函数集。

mlflow.set_tracking_uri() 连接到跟踪 URI。您还可以设置 MLFLOW_TRACKING_URI 环境变量,以便 MLflow 从中查找 URI。在这两种情况下,URI 都可以是远程服务器的 HTTP/HTTPS URI、数据库连接字符串或用于将数据记录到目录的本地路径。默认 URI 是 mlruns

mlflow.get_tracking_uri() 返回当前的跟踪 URI。

mlflow.create_experiment() 创建一个新的实验并返回实验 ID。可以通过将实验 ID 传递给 mlflow.start_run() 或使用 mlflow.set_experiment() 设置活动实验(将创建的实验的实验 ID 传入)来在该实验下启动运行。

mlflow.set_experiment() 将一个实验设置为活动实验并返回活动实验实例。如果您在 mlflow.start_run() 中未指定实验,则新的运行将在此实验下启动。

注意

如果按名称设置的实验不存在,将使用给定的名称创建一个新实验。创建实验后,它将被设置为活动实验。在某些平台,例如 Databricks 上,实验名称必须是绝对路径,例如 "/Users/<username>/my-experiment"

mlflow.start_run() 启动一个新的运行并返回一个可作为当前运行的上下文管理器的 mlflow.ActiveRun 对象。如果已有活动运行正在进行,您应该在启动新运行之前结束当前运行,或者使用 nested=True 将新运行嵌套在当前运行中。您无需显式调用 start_run:在没有活动运行的情况下调用任何日志记录函数都会自动启动一个新的运行。

注意

除非在 mlflow.start_run() 中设置了参数 run_name,否则将为每个运行生成一个唯一的运行名称。

mlflow.end_run() 结束当前活动的运行(如果有),可选择接受一个运行状态。

mlflow.active_run() 返回与当前活动运行(如果有)对应的 mlflow.entities.Run 对象。

注意

您无法通过 mlflow.active_run 返回的运行对象访问当前活动运行的属性(参数、指标等)。

为了访问这些属性,请使用 MlflowClient,如下所示

client = mlflow.MlflowClient()
data = client.get_run(mlflow.active_run().info.run_id).data

mlflow.last_active_run() 返回与当前活动运行(如果有)对应的 mlflow.entities.Run 对象。否则,它返回与当前 Python 进程启动的最后一个达到终止状态(即 FINISHEDFAILEDKILLED)的运行对应的 mlflow.entities.Run 对象。

mlflow.get_parent_run() 返回与给定运行 ID 的父运行(如果存在)对应的 mlflow.entities.Run 对象。否则,它返回 None

mlflow.log_param() 在当前活动运行中记录单个键值参数。键和值都是字符串。使用 mlflow.log_params() 一次记录多个参数。

mlflow.log_metric() 记录单个键值指标。值必须始终是数字。MLflow 会记住每个指标的值历史记录。使用 mlflow.log_metrics() 一次记录多个指标。

mlflow.log_input() 记录与当前活动运行对应的单个 mlflow.data.dataset.Dataset 对象。您还可以记录数据集上下文字符串和一个键值标签字典。

mlflow.set_tag() 在当前活动运行中设置单个键值标签。键和值都是字符串。使用 mlflow.set_tags() 一次设置多个标签。

mlflow.log_artifact() 将本地文件或目录记录为工件,可以选择接受一个 artifact_path 以将其放置在运行的工件 URI 内。运行工件可以组织成目录,因此您可以通过这种方式将工件放置在目录中。

mlflow.log_artifacts() 将给定目录中的所有文件记录为工件,同样接受一个可选的 artifact_path

mlflow.get_artifact_uri() 返回当前运行应将工件记录到的 URI。

跟踪技巧

使用显式步长和时间戳进行日志记录

log 方法支持两种替代方法来区分 x 轴上的指标值:timestamp(时间戳)和 step(训练迭代次数、 epoch 次数等)。默认情况下,timestamp 设置为当前时间,step 设置为 0。您可以通过将 timestampstep 参数传递给 log 方法来覆盖这些值。例如

mlflow.log_metric(key="train_loss", value=train_loss, step=epoch, timestamp=now)

step 具有以下要求和属性

  • 必须是有效的 64 位整数值。
  • 可以是负数。
  • 在连续的写操作调用中可以无序。例如,(1, 3, 2) 是一个有效序列。
  • 在连续的写操作调用中指定的值序列可以有“间隙”。例如,(1, 5, 75, -20) 是一个有效序列。

如果您同时指定了时间戳和步长,指标将独立地针对两个轴进行记录。

在实验中组织运行

MLflow 允许您在实验下对运行进行分组,这对于比较旨在解决特定任务的运行非常有用。您可以通过多种方式创建实验 - MLflow UI、命令行界面 (mlflow experiments) 或 mlflow.create_experiment() Python API。您可以使用 CLI (例如,mlflow run ... --experiment-name [name]) 或 MLFLOW_EXPERIMENT_NAME 环境变量为单个运行传递实验名称。另外,您也可以使用实验 ID,通过 --experiment-id CLI 标志或 MLFLOW_EXPERIMENT_ID 环境变量。

# Set the experiment via environment variables
export MLFLOW_EXPERIMENT_NAME=fraud-detection

mlflow experiments create --experiment-name fraud-detection
# Launch a run. The experiment is inferred from the MLFLOW_EXPERIMENT_NAME environment
# variable, or from the --experiment-name parameter passed to the MLflow CLI (the latter
# taking precedence)
with mlflow.start_run():
mlflow.log_param("a", 1)
mlflow.log_metric("b", 2)

创建子运行

您还可以在单个运行中创建多个运行。这对于超参数调优、交叉验证折叠等场景非常有用,因为您需要在实验内进行另一级别的组织。您可以通过将 parent_run_id 传递给 mlflow.start_run() 函数来创建子运行。例如

# Start parent run
with mlflow.start_run() as parent_run:
param = [0.01, 0.02, 0.03]

# Create a child run for each parameter setting
for p in param:
with mlflow.start_run(nested=True) as child_run:
mlflow.log_param("p", p)
...
mlflow.log_metric("val_loss", val_loss)

您可以使用标签获取父运行下的所有子运行。

child_runs = mlflow.search_runs(
experiment_ids=[YOUR_EXPERIMENT_ID],
filter_string=f"tags.mlflow.parentRunId = '{parent_run.info.run_id}'",
)
print("child runs:")
print(results[["run_id", "params.p", "metrics.val_loss"]])
# child runs:
# run_id params.p metrics.val_loss
# 0 0c0b... 0.01 0.1234
# 0 c2a0... 0.02 0.0890
# 0 g2j1... 0.03 0.1567

在一个程序中启动多个运行

有时您想在同一个程序中启动多个 MLflow 运行:例如,您可能正在本地执行超参数搜索,或者您的实验运行速度非常快。执行此操作的方式取决于您是想 顺序 运行它们还是 并行 运行它们。

顺序运行

逐个运行多个运行很容易,因为 mlflow.start_run() 返回的 ActiveRun 对象是一个 Python 上下文管理器。您可以将每个运行“作用域”到单个代码块,如下所示

# First run
with mlflow.start_run():
mlflow.log_param("x", 1)
mlflow.log_metric("y", 2)
...

# Another run
with mlflow.start_run():
...

运行在 with 语句期间保持打开状态,并在语句退出时自动关闭,即使由于异常退出也会如此。

并行运行

MLflow 也支持使用 多进程 或多线程并行运行多个运行。

多进程很简单:只需在单独的进程中调用 mlflow.start_run(),它将为每个进程创建一个新的运行。例如

import mlflow
import multiprocessing as mp


def train_model(param):
with mlflow.start_run():
mlflow.log_param("p", param)
...


if __name__ == "__main__":
mlflow.set_experiment("multi-process")
params = [0.01, 0.02, ...]
with mp.Pool(processes=4) as pool:
pool.map(train_model, params)
注意

上述代码仅在使用 fork 方法创建子进程时有效。如果您使用 spawn 方法,子进程将不会自动继承父进程的全局状态,包括活动实验和跟踪 URI,从而导致子进程无法记录到同一个实验。为了克服这个限制,您可以在每个子进程中显式设置活动实验和跟踪 URI,如下所示。在 POSIX 系统(除了 MacOS)上,fork 是默认方法,但在其他情况下默认使用 spawn 方法。

import mlflow
import multiprocessing as mp


def train_model(params):
# Set the experiment and tracking URI in each child process
mlflow.set_tracking_uri("http://localhost:5000")
mlflow.set_experiment("multi-process")
with mlflow.start_run():
...


if __name__ == "__main__":
params = [0.01, 0.02, ...]
pool = mp.get_context("spawn").Pool(processes=4)
pool.map(train_model, params)

多线程稍微复杂一些,因为 MLflow 使用全局状态来跟踪当前活动的运行,即在同一进程中拥有多个活动运行可能会导致数据损坏。但是,您可以通过使用 子运行 来避免这个问题并使用多线程。您可以通过将 nested=True 传递给 mlflow.start_run() 来在每个线程中启动子运行,如上一节所述。例如

import mlflow
import threading


def train_model(param):
# Create a child run by passing nested=True
with mlflow.start_run(nested=True):
mlflow.log_param("p", param)
...


if __name__ == "__main__":
params = [0.01, 0.02, ...]
threads = []
for p in params:
t = threading.Thread(target=train_model, args=(p,))
threads.append(t)
t.start()

for t in threads:
t.join()

另外,这里 是使用多线程和子运行进行超参数调优的完整示例。

为运行添加标签

您可以使用任意标签来注释运行,以便将它们组织成组。这允许您使用 过滤器表达式 在跟踪 UI 中轻松过滤和搜索运行。

系统标签

mlflow. 开头的标签键保留供内部使用。

注意

mlflow.note.content 是一个例外情况,该标签不会自动设置,用户可以覆盖它以包含有关运行的附加信息。

以下标签在适当的情况下由 MLflow 自动设置

描述
mlflow.docker.image.id用于执行此运行的 Docker 镜像 ID。
mlflow.docker.image.name用于执行此运行的 Docker 镜像名称。
mlflow.log-model.history通过 log-model 调用收集的模型元数据。包括记录到运行的 MLModel 模型文件的序列化形式,尽管捕获的确切格式和信息可能会发生变化。
mlflow.note.content关于此运行的描述性说明。
这个保留标签不会自动设置,用户可以覆盖它以包含有关运行的附加信息。内容显示在运行页面上的“备注”部分下。
mlflow.parentRunId如果这是嵌套运行,则为父运行的 ID。
mlflow.project.entryPoint与当前运行关联的项目入口点名称(如果有)。
mlflow.project.envMLflow 项目使用的运行时环境。可能的值:"docker""conda"
mlflow.source.git.branch如果位于 git 仓库中,则为执行代码的分支名称。此标签仅在 MLflow ProjectsMLflow Recipe 的上下文内记录。
mlflow.source.git.commit如果位于 git 仓库中,则为执行代码的提交哈希值。此标签仅当代码以 Python 脚本(如 python train.py)或项目形式执行时记录。如果代码在笔记本中执行,则不记录此标签。
mlflow.source.git.repoURL执行代码克隆自的 URL。此标签仅在 MLflow ProjectsMLflow Recipe 的上下文内记录。
mlflow.source.name源标识符(例如,GitHub URL、本地 Python 文件名、笔记本名称)
mlflow.source.type源类型。可能的值:"NOTEBOOK""JOB""PROJECT""LOCAL""UNKNOWN"
mlflow.user创建运行的用户的标识符。

自定义标签

MlflowClient.set_tag() 函数允许您为运行添加自定义标签。一个标签一次只能映射到一个唯一的键值对。例如

client.set_tag(run.info.run_id, "tag_key", "tag_value")

从自动日志记录结果获取 MLflow Run 实例

在某些情况下,您可能需要访问与自动日志记录结果关联的 MLflow Run 实例。您可以通过 mlflow.last_active_run() 函数访问最近的自动日志记录运行。

以下是使用此函数的 sklearn 自动日志记录示例

import mlflow

from sklearn.model_selection import train_test_split
from sklearn.datasets import load_diabetes
from sklearn.ensemble import RandomForestRegressor

mlflow.autolog()

db = load_diabetes()
X_train, X_test, y_train, y_test = train_test_split(db.data, db.target)

# Create and train models.
rf = RandomForestRegressor(n_estimators=100, max_depth=6, max_features=3)
rf.fit(X_train, y_train)

# Use the model to make predictions on the test dataset.
predictions = rf.predict(X_test)
autolog_run = mlflow.last_active_run()
print(autolog_run)
# <Run:
# data=<RunData:
# metrics={'accuracy': 0.0},
# params={'n_estimators': '100', 'max_depth': '6', 'max_features': '3'},
# tags={'estimator_class': 'sklearn.ensemble._forest.RandomForestRegressor', 'estimator_name': 'RandomForestRegressor'}
# >,
# info=<RunInfo:
# artifact_uri='file:///Users/andrew/Code/mlflow/mlruns/0/0c0b.../artifacts',
# end_time=163...0,
# run_id='0c0b...',
# run_uuid='0c0b...',
# start_time=163...0,
# status='FINISHED',
# user_id='ME'>
# >