搜索运行

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

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

搜索在 MLflow UI 上运行

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

  1. 创建示例 MLflow 运行

  2. 看一个简单的查询示例

  3. 深入探讨查询语法

  4. 提供多种示例查询

创建示例 MLflow 运行

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

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

mlflow ui

让我们在网络浏览器中访问 http://localhost: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”下找到所有这些运行,如下图所示:

测试运行

在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

搜索组件的视觉表示:

搜索组件

左右侧元素的有效语法:

  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

  • 数值字段的非None条件不受支持,例如 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')
    
  1. 参考值:一个数值、字符串或一组字符串。

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

示例查询

在本节中,我们将介绍如何按不同的 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 - 按参数搜索

参数通常是表示模型配置方面的字符串。参数可以包括学习率、批次大小和训练轮数等值。它们可以通过 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')

你也可以在同一个字段上应用多个条件,例如搜索所有损失指标 BETWEEEN 0.1 和 0.15 之间的值,包括 0.1 和 0.15:

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

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

8 - 非空查询

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

params.batch_size != "None"

以编程方式搜索运行

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

Python

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

1 - 复杂过滤器

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

  • 对于复杂的过滤器,特别是那些同时包含单引号和双引号的情况,请使用多行字符串或使用 \” 来转义引号。

  • 在使用列表时,使用 .join() 方法将列表元素与分隔符连接起来。

  • 通常使用流畅的API最为简洁,因此下面我们仅用流畅的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

run_view_type 参数提供了额外的过滤选项,如 mlflow.entities.ViewType 枚举中所述。例如,如果你想只过滤活跃的运行,这在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"],
)

2 - 排序

搜索API中另一个有用的功能是允许对返回的搜索结果进行排序。您可以在 order_by 参数中指定感兴趣的列的列表,并附带 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]

3 - 搜索所有实验

现在你可能会想知道如何搜索所有实验。只需指定 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");