跳到主要内容

搜索运行

本指南将引导您了解如何通过 MLflow UI 和 Python API 搜索您的 MLflow 运行。如果您有兴趣根据运行的指标、参数、标签、数据集信息或运行元数据查询特定运行,此资源将非常有价值。

简而言之,您可以利用类似 SQL 的语法,根据各种条件过滤您的运行。请注意,不支持 OR 关键字,并且与 SQL 还有一些其他差异(如下所述),但尽管存在这些限制,运行搜索功能仍然非常强大。

在 MLflow UI 上搜索运行

MLflow UI 提供了一个强大的搜索界面,允许您过滤运行。下面我们将...

  1. 创建示例 MLflow 运行
  2. 查看一个简单的查询示例
  3. 深入了解查询语法
  4. 提供各种示例查询

创建示例 MLflow 运行

首先,让我们创建一些示例 MLflow 运行。本文档基于使用以下脚本创建的实验。如果您不想在您的机器上以交互方式探索它,请跳过此部分。

在运行脚本之前,让我们先在本地主机上启动 MLflow UI。

mlflow ui

让我们在我们的 Web 浏览器中访问 https://:5000/。这样做之后,您会注意到我们没有任何实验或模型。让我们通过使用下面的脚本创建一些 MLflow 运行来解决这个问题。

请注意,当您运行此脚本时,您需要从您运行 mlflow ui 命令的同一目录中运行它。

import mlflow
import numpy as np

mlflow.set_experiment("search-run-guide")

accuracy = np.arange(0, 1, 0.1)
loss = np.arange(1, 0, -0.1)
log_scale_loss = np.log(loss)
f1_score = np.arange(0, 1, 0.1)

batch_size = [2] * 5 + [4] * 5
learning_rate = [0.001, 0.01] * 5
model = ["GPT-2", "GPT-3", "GPT-3.5", "GPT-4"] + [None] * 6

task = ["classification", "regression", "causal lm"] + [None] * 7
environment = ["notebook"] * 5 + [None] * 5

dataset_name = ["custom"] * 5 + ["also custom"] * 5
dataset_digest = ["s8ds293b", "jks834s2"] + [None] * 8
dataset_context = ["train"] * 5 + ["test"] * 5

for i in range(10):
with mlflow.start_run():
mlflow.log_metrics(
{
"loss": loss[i],
"accuracy": accuracy[i],
"log-scale-loss": log_scale_loss[i],
"f1 score": f1_score[i],
}
)

mlflow.log_params(
{
"batch_size": batch_size[i],
"learning rate": learning_rate[i],
"model": model[i],
}
)

mlflow.set_tags(
{
"task": task[i],
"environment": environment[i],
}
)

dataset = mlflow.data.from_numpy(
features=np.random.uniform(size=[20, 28, 28, 3]),
targets=np.random.randint(0, 10, size=[20]),
name=dataset_name[i],
digest=dataset_digest[i],
)
mlflow.log_input(dataset, context=dataset_context[i])

上面的代码创建了 10 个具有不同指标、参数、标签和数据集信息的 MLflow 运行。成功执行后,如果您返回浏览器中的 MLflow UI,您应该在实验“search-run-guide”下找到所有这些运行,如下面的屏幕截图所示。

testing runs

在 MLflow 的实际生产部署中,通常有数千甚至数十万个运行。在这种情况下,能够基于特定标准过滤和搜索运行非常重要。

搜索查询示例

为了过滤您的 MLflow 运行,您需要编写**搜索查询**,这些查询是以独特的语法表达的伪 SQL 条件。

为了展示这个功能,让我们看看下面的代码示例。

import mlflow

all_runs = mlflow.search_runs(search_all_experiments=True)
print(all_runs)
输出
                             run_id  ... tags.mlflow.user
0 5984a3488161440f92de9847e846b342 ... michael.berk
1 41160f238a5841998dda263794b26067 ... michael.berk
2 babe221a676b4fa4b204f8240f2c4f14 ... michael.berk
3 45eb4f02c5a1461aa6098fa550233be6 ... michael.berk
4 1c7c459486c44b23bb016028aee1f153 ... michael.berk
5 4453f59f1ab04491bb9582d8cba5f437 ... michael.berk
6 22db81f070f6413588641c8c343cdd72 ... michael.berk
7 c3680e37d0fa44eb9c9fb7828f6b5481 ... michael.berk
8 67973142b9c0470d8d764ada07c5a988 ... michael.berk
9 59853d5f17f946218f63de1dc82de07b ... michael.berk

[10 rows x 19 columns]

其次,让我们尝试过滤我们真正糟糕的模型运行:metrics.loss > 0.8

import mlflow

bad_runs = mlflow.search_runs(
filter_string="metrics.loss > 0.8", search_all_experiments=True
)
print(bad_runs)
输出
                             run_id  ... tags.mlflow.source.name
0 67973142b9c0470d8d764ada07c5a988 ... delete.py
1 59853d5f17f946218f63de1dc82de07b ... delete.py

[2 rows x 19 columns]

您会注意到我们现在显示的是 2 个运行,而不是 10 个。非常简单,对吧?

搜索语法概述

MLflow 的搜索功能利用域特定语言 (DSL) 进行查询。它受到 SQL 的启发,但不提供 SQL 的全部功能。

本节描述语法格式,重点关注搜索查询中的“左侧”和“右侧”元素。“左侧”指的是要过滤的字段,例如 metrics.loss,而“右侧”指的是与该字段进行比较的值,例如 0.8

搜索组件的可视化表示

search components

左侧和右侧元素的有效语法

  1. 左侧语法

    • 没有特殊字符或保留关键字的字段可以直接引用(例如,tag.test)。
    • 对于包含特殊字符或属于保留关键字的字段,请使用反引号。
    • 双引号也可以接受用于包围字段名称(例如,tag."test")。

    不支持

    • 单引号对于包围字段名称是**无效的**(例如,tag.'test' 会导致语法错误)。
  2. 右侧语法

    • 根据内容要求,将值括在单引号或双引号中(例如,tag.`test` = 'abc'tag.`test` = "abc")。
    • 非指标值,**包括可能存储为标签或参数的数字值**,必须用引号括起来。

    不支持

    • 不允许对值使用反引号或不进行包装。无效语法的例子包括

    这会导致语法错误,因为反引号不能用于右侧值。

    tag.`test` = `abc`

    这会导致语法错误,因为如果值不是指标,则必须用双引号括起来。

    tag.`test` = abc

搜索查询语法深入探讨

如上所述,MLflow 搜索语法与 SQL 类似,但有一些值得注意的例外。

  • 不支持 SQL 的 OR 关键字。
  • 对于包含特殊字符或以数字开头的字段,这些字段应该用**反引号**括起来。
- Bad:  metrics.cross-entropy-loss < 0.5
+ Good: metrics.`cross-entropy-loss` < 0.5

- Bad: params.1st_iteration_timestamp = "2022-01-01"
+ Good: params.`1st_iteration_timestamp` = "2022-01-01"
  • 对于 SQL 的 IN 关键字,您必须用**单引号**包围列表中的值。
- Bad:  attributes.run_id IN ("5984a3488161440f92de9847e846b342", "babe221a676b4fa4b204f8240f2c4f14")
+ Good: attributes.run_id IN ('5984a3488161440f92de9847e846b342', 'babe221a676b4fa4b204f8240f2c4f14')
  • 对于 SQL 的 IN 关键字,您只能搜索以下字段

    • datasets.name
    • datasets.digest
    • datasets.context
    • attributes.run_id
  • 不支持数值字段的非空条件,例如 metrics.accuracy != "None" 将会失败。

除此之外,对于任何使用过 SQL 的人来说,语法都应该是直观的。要组装一个单独的搜索条件,您必须使用以下组件组装一个不等式...

  1. 一个 MLflow 字段:一个指标、参数、标签、数据集或运行元数据。

  2. 一个比较器:一个不等式运算符。

    • 对于数值,MLflow 支持 =!=>>=<<=。例子包括

      metrics.accuracy > 0.72
      metrics.loss <= 0.15
      metrics.accuracy != 0.15
    • 对于字符串,MLflow 支持 =!=LIKE(区分大小写)和 ILIKE(不区分大小写)。例子包括

      params.model = "GPT-4o"
      params.model LIKE "GPT%"
      params.model ILIKE "gpt%"
    • 对于集合,MLflow 支持 IN。例子包括

      datasets.name IN ('custom', 'also custom', 'another custom name')
      datasets.digest IN ('s8ds293b', 'jks834s2')
      attributes.run_id IN ('5984a3488161440f92de9847e846b342')
  3. 一个参考值:一个数值、字符串或字符串集。

让我们看一些更多的例子。

示例查询

在本节中,我们将介绍如何按不同的 MLflow 字段类别进行搜索。对于每个类别,我们提供了一些示例查询。如果您已经执行了我们提供的运行创建脚本,这些查询应该获取某些运行,但有时需要修改以获取特定于运行的信息,例如 start_time

1 - 按指标搜索

指标是通常用于评估模型在训练期间或之后性能的定量度量。指标可以包括诸如准确率、精确率、召回率、F1 分数等值,并且可以随着模型的训练而随时间变化。它们通过 mlflow.log_metricmlflow.log_metrics 手动记录,或通过自动日志记录自动记录。

要通过过滤指标来搜索运行,您必须在不等式的左侧包含 metrics 前缀。请注意,它们**存储为数字**,因此您必须使用数值比较器。

metrics.accuracy > 0.72
metrics."accuracy" > 0.72
metrics.loss <= 0.15
metrics.`log-scale-loss` <= 0
metrics.`f1 score` >= 0.5
metrics.accuracy > 0.72 AND metrics.loss <= 0.15

2 - 按参数搜索

参数是通常表示模型配置方面的字符串。参数可以包括诸如学习率、批量大小和 epoch 数等值。它们通过 mlflow.log_parammlflow.log_params 手动记录,或通过自动日志记录自动记录。

要通过过滤参数来搜索运行,您必须在不等式的左侧包含 params 前缀。请注意,它们**存储为字符串**,因此您必须使用字符串比较器,例如 =!=

请注意,存储为参数的数值在跟踪存储中被转换为字符串。在查询数值参数时,您必须通过将它们括在**双引号**中来指定它们为字符串。

params.batch_size = "2"
params.model LIKE "GPT%"
params.model ILIKE "gPt%"
params.model LIKE "GPT%" AND params.batch_size = "2"

3 - 按标签搜索

标签是通常提供关于运行的附加上下文的元数据。标签可以包括诸如用户名、团队等值。它们通过 mlflow.set_tagmlflow.set_tags 手动记录。此外,系统标签,例如 mlflow.user,会被自动记录。

要通过过滤标签来搜索运行,您必须在不等式的左侧包含 tagsmlflow 前缀。请注意,标签**存储为字符串**,因此您必须使用字符串比较器,例如 =!=

tags."environment" = "notebook"
tags.environment = "notebook"
tags.task = "Classification"
tags.task ILIKE "classif%"

4 - 按数据集信息搜索

数据集表示在模型训练或评估中使用的数据,包括特征、目标、预测以及元数据,例如数据集的名称、摘要(哈希)、模式、配置文件和来源。它们通过 mlflow.log_input 记录,或通过自动日志记录自动记录。

要通过过滤数据集信息来搜索运行,您必须过滤以下字段之一

  1. datasets.name,它是数据集的名称。
  2. datasets.digest,它是数据集的唯一标识符。
  3. datasets.context,它表示数据集是用于训练、评估还是测试。

请注意,数据集信息**存储为字符串**,因此您必须使用字符串比较器,例如 =!=。另请注意,数据集支持集合比较器,例如 IN

datasets.name LIKE "custom"
datasets.digest IN ('s8ds293b', 'jks834s2')
datasets.context = "train"

5 - 按运行的元数据搜索

运行元数据是各种用户指定和系统生成的属性,它们提供了关于运行的附加上下文。

要通过过滤运行的元数据来搜索运行,您必须在不等式的左侧包含 attributes 前缀。请注意,运行元数据可以是字符串或数字,具体取决于属性,因此您必须使用适当的比较器。 有关属性的完整列表,请参见

mlflow.entities.RunInfo,但请注意,并非 RunInfo 对象中的所有字段都可搜索。

要通过过滤标签来搜索运行,您必须在不等式的左侧包含 tagsmlflow 前缀。请注意,标签**存储为字符串**,因此您必须使用字符串比较器,例如 =!=

字符串的示例
attributes.status = "ACTIVE"
attributes.user_id LIKE "user1"
attributes.run_name = "my-run"
attributes.run_id = "a1b2c3d4"
attributes.run_id IN ('a1b2c3d4', 'e5f6g7h8')
数字的示例
attributes.start_time >= 1664067852747
attributes.end_time < 1664067852747
attributes.created > 1664067852747

6 - 在集合上搜索

您可以通过 IN 关键字过滤一组可接受的值来搜索运行。如上所述,这仅支持以下字段

  • datasets.{any_attribute}
  • attributes.run_id
datasets.name IN ('custom', 'also custom')
datasets.digest IN ('s8ds293b', 'jks834s2')
attributes.run_id IN ('a1b2c3d4', 'e5f6g7h8')

7 - 链式查询

您可以使用 AND 关键字将多个查询链接在一起。例如,要搜索具有各种条件的运行,您可以使用以下查询

metrics.accuracy > 0.72 AND metrics.loss <= 0.15
metrics.accuracy > 0.72 AND metrics.batch_size != 0
metrics.accuracy > 0.72 AND metrics.batch_size != 0 AND attributes.run_id IN ('a1b2c3d4', 'e5f6g7h8')

您还可以在同一字段上应用多个条件,例如搜索所有 BETWEEN 0.1 和 0.15(包括 0.1 和 0.15)之间的损失指标

metrics.loss <= 0.15 AND metrics.loss >= 0.1

最后,在继续之前,重要的是要重申您不能在查询中使用 OR 关键字。

8 - 非空查询

要搜索字段(仅支持字符串类型)不为 null 的运行,请使用 field != "None" 语法。 例如,要搜索 batch_size 不为 null 的运行,您可以使用以下查询

params.batch_size != "None"

以编程方式搜索运行

在扩展到大型生产系统时,通常您希望在 MLflow UI 之外与您的运行交互。 这可以使用 MLflow 客户端 API 以编程方式完成。

Python

mlflow.client.MlflowClient.search_runs()mlflow.search_runs() 接受与上述 UI 示例相同的参数,甚至更多! 它们返回所有与指定过滤器匹配的运行。 您最好的资源是每个函数的文档字符串,但这里有一些有用的例子。

1 - 复杂过滤器

Python 提供了以编程方式构建这些查询的强大方法。 一些提示

  • 对于复杂的过滤器,特别是那些既有单引号又有双引号的过滤器,使用多行字符串或 \\" 来转义引号。
  • 当处理列表时,使用 .join() 方法用分隔符连接列表元素。
  • 使用 fluent API 通常是最简洁的,因此下面我们仅使用 fluent API 进行演示。
import mlflow

run_ids = ["22db81f070f6413588641c8c343cdd72", "c3680e37d0fa44eb9c9fb7828f6b5481"]
run_id_condition = "'" + "','".join(run_ids) + "'"

complex_filter = f"""
attributes.run_id IN ({run_id_condition})
AND metrics.loss > 0.3
AND metrics."f1 score" < 0.5
AND params.model LIKE "GPT%"
"""

runs_with_complex_filter = mlflow.search_runs(
experiment_names=["search-run-guide"],
filter_string=complex_filter,
)
print(runs_with_complex_filter)

输出将是一个 pandas DataFrame,其中包含与指定过滤器匹配的运行,如下所示。

                             run_id  ... tags.mlflow.runName
0 22db81f070f6413588641c8c343cdd72 ... orderly-quail-568
1 c3680e37d0fa44eb9c9fb7828f6b5481 ... melodic-lynx-301

[2 rows x 19 columns]

2 - run_view_type

mlflow.entities.ViewType 枚举中所述,run_view_type 参数公开了额外的过滤选项。 例如,如果您只想过滤活动运行(这是 UI 中的一个下拉列表),只需传递 run_view_type=ViewType.ACTIVE_ONLY

import mlflow
from mlflow.entities import ViewType

active_runs = mlflow.search_runs(
experiment_names=["search-run-guide"],
run_view_type=ViewType.ACTIVE_ONLY,
order_by=["metrics.accuracy DESC"],
)

3 - 排序

搜索 API 中提供的另一个有用功能是允许对返回的搜索结果进行排序。 您可以在 order_by kwarg 中指定感兴趣的列的列表以及 DESCASC。 请注意,DESCASC 值是可选的,因此当未提供该值时,默认值为 ASC。 另请注意,当省略 order_by 参数时,默认排序是按 start_time DESC 排序,然后按 run_id ASC 排序。

import mlflow
from mlflow.entities import ViewType

active_runs_ordered_by_accuracy = mlflow.search_runs(
experiment_names=["search-run-guide"],
run_view_type=ViewType.ACTIVE_ONLY,
order_by=["metrics.accuracy DESC"],
)

一个常见的用例是获得前 n 个结果,例如,按准确率排序的前 5 个运行。 当与 max_results 参数结合使用时,您可以获得与您的查询匹配的前 n 个结果。

import mlflow
from mlflow.entities import ViewType

highest_accuracy_run = mlflow.search_runs(
experiment_names=["search-run-guide"],
run_view_type=ViewType.ACTIVE_ONLY,
max_results=1,
order_by=["metrics.accuracy DESC"],
)[0]

4 - 搜索所有实验

现在您可能想知道如何搜索所有实验。 就像指定 search_all_experiments=True 并省略 experiment_ids 参数一样简单。

import mlflow
from mlflow.entities import ViewType

model_of_interest = "GPT-4"
gpt_4_runs_global = mlflow.search_runs(
filter_string=f"params.model = '{model_of_interest}'",
run_view_type=ViewType.ALL,
search_all_experiments=True,
)

最后,mlflow.client.MlflowClient.search_runs()mlflow.search_runs() 方法中还有其他有用的功能,因此请务必查看文档以获取更多详细信息。

R

R API 与 Python API 类似,区别在于过滤器条件必须用字符串包装。 由于这种行为,右侧条件元素对于参数、属性和标签必须用单引号包装。

library(mlflow)
mlflow_search_runs(
filter = "metrics.rmse < 0.9 and tags.production = 'true'",
experiment_ids = as.character(1:2),
order_by = "params.lr DESC"
)

Java

Java API 与 Python API 类似,区别在于整个条件过滤器语法都被字符串封装。 这是因为 Java API 是 Python 核心 API 周围的一个薄包装器,因此将在两种语言之间进行翻译。

List<Long> experimentIds = Arrays.asList("1", "2", "4", "8");
List<RunInfo> searchResult = client.searchRuns(experimentIds, "metrics.accuracy_score < 99.90");