14.Python API 自动化
在命令行之外,DVC 还提供了一套完整的 Python API。这套 API 让我们能在代码中直接操作 DVC 项目,读取数据、查询实验、获取参数和指标,甚至访问模型制品。对于需要构建自动化流程、开发内部工具,或者想在 Jupyter Notebook 里直接分析实验结果的场景,Python API 显得特别实用。
DVCFileSystem 文件操作
DVCFileSystem 是 DVC Python API 中最基础也最强大的组件之一。它提供了一个符合 fsspec 标准的文件系统接口,让我们能用 Pythonic 的方式访问 DVC 仓库中的文件,无论是 Git 追踪的代码,还是 DVC 追踪的数据和模型。
基本用法
DVCFileSystem 的核心是一个只读文件系统,它不支持写入操作,但提供了完整的读取和遍历能力。我们可以用它来访问本地仓库,也可以直接操作远程 Git 仓库,而无需事先克隆到本地。
from dvc.api import DVCFileSystem
# 访问本地仓库
fs = DVCFileSystem("/path/to/local/repository")
# 访问远程仓库
repo = "https://github.com/iterative/example-get-started.git"
fs = DVCFileSystem(repo, rev="main")
初始化 DVCFileSystem 时,主要参数有两个:repo 指定仓库位置,可以是 URL 或本地路径;rev 指定 Git 版本,可以是分支名、标签或提交哈希。如果不指定 repo,默认使用当前工作目录所在的 DVC 项目。
读取文件内容
DVCFileSystem 提供了多种读取文件的方式,最常用的是 open() 方法,它返回一个文件对象,可以像普通文件一样读取。
import pickle
# 打开模型文件
with fs.open("model.pkl") as f:
model = pickle.load(f)
需要注意的是,与 Python 内置的 open() 不同,DVCFileSystem 的 open() 默认使用二进制模式("rb")。如果要读取文本文件,需要显式指定模式和编码。
# 读取文本文件
text = fs.read_text("get-started/data.xml", encoding="utf-8")
# 读取二进制内容
contents = fs.read_bytes("get-started/data.xml")
read_text() 和 read_bytes() 是便捷方法,分别用于读取文本和二进制内容,它们内部调用了 open() 并处理了编码问题。
列出仓库文件
DVCFileSystem 的 find() 方法可以递归列出仓库中的所有文件,这在探索远程仓库结构时非常有用。
# 列出所有 DVC 追踪的文件
dvc_files = fs.find("/", detail=False, dvc_only=True)
print(dvc_files)
输出结果类似这样:
[
'/data/data.xml',
'/data/features/test.pkl',
'/data/features/train.pkl',
'/model.pkl'
]
dvc_only=True 参数确保只返回 DVC 追踪的文件,不包括 Git 追踪的代码文件。如果想查看所有文件,包括 Git 追踪的,可以去掉这个参数。
# 列出所有文件(包括 Git 追踪的)
all_files = fs.find("/", detail=False)
find() 方法返回的是相对于仓库根路径的列表,根路径用 "/" 表示。我们也可以指定子目录来缩小搜索范围。
# 只列出 data 目录下的文件
data_files = fs.find("/data", detail=False, dvc_only=True)
对于非递归列出,可以使用 ls() 方法,它类似于命令行的 ls 命令。
下载文件或目录
除了读取文件内容,DVCFileSystem 还支持将文件或目录下载到本地。
import os
# 下载单个文件
fs.get_file("data/data.xml", "local_data.xml")
# 下载整个目录
fs.get("data", "local_data", recursive=True)
get_file() 用于下载单个文件,第一个参数是远程路径,第二个是本地路径。get() 方法配合 recursive=True 可以下载整个目录树。DVC 会优先从本地缓存读取,如果缓存中没有,则从远程存储流式传输。
处理子仓库
在 monorepo 结构中,DVC 可能在子目录中初始化。这时需要设置 subrepos=True 参数才能访问这些子项目。
repo = "https://github.com/iterative/monorepo-example.git"
# 默认情况下,子目录中的 DVC 项目会被忽略
fs = DVCFileSystem(repo, rev="develop")
print(fs.find("nlp", detail=False, dvc_only=True)) # 返回空列表
# 使用 subrepos=True 来访问子项目
fs = DVCFileSystem(repo, subrepos=True, rev="develop")
print(fs.find("nlp", detail=False, dvc_only=True))
# 返回:['nlp/data/data.xml', 'nlp/model.pkl', ...]
与 fsspec 生态集成
DVCFileSystem 基于 fsspec 构建,这意味着它能无缝集成到 Python 数据生态中。当安装了 DVC 和其他 fsspec 兼容库(如 Hugging Face Datasets)时,可以直接使用 dvc:// URL 方案。
import fsspec
# 使用 dvc:// URL 访问 DVC 追踪的文件
f = fsspec.open(
"dvc://workshop/satellite-data/jan_train.csv",
repo="https://github.com/iterative/dataset-registry.git",
)
需要注意的是,dvc:// URL 只包含文件路径,不包含 Git 仓库地址。仓库地址需要通过 repo 参数单独提供。对于需要 storage_options 的方法,可以将 DVCFileSystem 的参数传递进去。
实验数据查询
在机器学习项目中,我们经常需要程序化地获取实验数据进行分析。dvc.api.exp_show() 提供了这个功能,它返回一个实验列表,每个实验包含参数、指标和其他元数据。
基本用法
最简单的调用方式不需要任何参数,它会返回当前项目 HEAD 的所有实验。
import dvc.api
exps = dvc.api.exp_show()
返回的数据结构是一个字典列表,每个字典代表一个实验:
[
{
"Experiment": "paled-acre",
"rev": "883442c",
"Created": "Apr 19, 2023",
"dice_multi": 0.8590125166103912,
"train.arch": "squeezenet1_1"
},
{
"Experiment": "vocal-suer",
"rev": "231e504",
"Created": "Apr 19, 2023",
"dice_multi": 0.8997336177828745,
"train.arch": "resnet34"
}
]
每个实验字典包含了实验名称、Git 修订版本、创建时间以及所有参数和指标的值。字段名直接对应 params.yaml 中的参数路径和指标文件名。
与 Pandas 集成
返回的列表格式可以直接转换为 Pandas DataFrame,方便进行数据分析和可视化。
import dvc.api
import pandas as pd
df = pd.DataFrame(dvc.api.exp_show())
转换后,我们可以利用 Pandas 强大的数据处理能力来清洗和分析实验数据。
# 只选择需要的列
columns = ["Experiment", "dice_multi", "train.arch"]
df = pd.DataFrame(dvc.api.exp_show(), columns=columns)
# 清理数据
df.dropna(inplace=True)
df.reset_index(drop=True, inplace=True)
# 输出为 Markdown 表格
print(df.to_markdown())
输出结果:
| | Experiment | train.arch | dice_multi |
| --: | :--------- | :------------ | ---------: |
| 0 | paled-acre | squeezenet1_1 | 0.859013 |
| 1 | vocal-suer | resnet34 | 0.891619 |
| 2 | banal-hogs | alexnet | 0.875823 |
筛选特定实验
exp_show() 提供了多个参数来控制返回的实验范围。repo 参数可以指定远程仓库,revs 参数可以指定特定的 Git 版本。
# 获取特定仓库的实验
exps = dvc.api.exp_show(
repo="https://github.com/iterative/example-get-started"
)
# 获取特定版本的实验
exps = dvc.api.exp_show(revs="HEAD~1")
revs 参数可以接受单个版本字符串,也可以接受列表。结合 dvc.api.scm 模块,我们可以方便地获取所有标签或分支对应的实验。
import dvc.api
# 获取所有标签对应的实验
all_tags = dvc.api.scm.all_tags()
exps = dvc.api.exp_show(revs=all_tags)
控制输出细节
param_deps 参数可以限制只返回作为阶段依赖的参数,这在只想查看影响流水线执行的参数时很有用。
# 只返回阶段依赖的参数
exps = dvc.api.exp_show(param_deps=True)
config 参数允许传递配置字典,可以用来提供远程存储的认证信息,类似于其他 API 函数中的 remote_config。
参数读取 params_show
dvc.api.params_show() 专门用于读取 DVC 项目中追踪的参数。与 exp_show() 不同,它专注于参数,提供了更精细的控制。
基本用法
不带参数调用时,params_show() 返回项目中所有的参数。
import dvc.api
params = dvc.api.params_show()
print(params)
返回的字典结构直接对应 params.yaml 的内容:
{
"split": 0.2,
"seed": 20170428,
"train": {
"epochs": 10,
"lr": 0.001
}
}
按阶段筛选
在复杂的流水线中,不同阶段可能使用不同的参数。stages 参数可以限制只返回特定阶段的参数。
# 获取单个阶段的参数
params = dvc.api.params_show(stages="train")
print(params)
输出:
{
"n_est": 50,
"min_split": 0.01
}
stages 也可以接受阶段名称列表,返回多个阶段的参数合并结果。
# 获取多个阶段的参数
params = dvc.api.params_show(stages=["featurize", "train"])
返回的字典可以直接在代码中使用,例如初始化模型时传入超参数。
from sklearn.ensemble import RandomForestClassifier
params = dvc.api.params_show(stages="train")
clf = RandomForestClassifier(
n_estimators=params["n_est"],
min_samples_split=params["min_split"]
)
读取特定参数文件
如果项目使用了多个参数文件,可以通过 targets 参数指定要读取的文件路径。
# 读取特定参数文件
params = dvc.api.params_show("configs/params_dev.yaml")
# 读取多个参数文件
params = dvc.api.params_show(
"params.yaml",
"configs/model.yaml"
)
当指定多个目标时,返回的字典会按文件名组织,避免参数名冲突。
{
"params.yaml": {
"split": 0.2,
"seed": 20170428
},
"configs/model.yaml": {
"arch": "resnet50",
"pretrained": True
}
}
读取依赖参数
deps 参数控制是否只返回被声明为阶段依赖的参数。当设置为 True 时,只有那些在 dvc.yaml 中 params 字段下列出的参数会被返回。
# 只返回阶段依赖的参数
params = dvc.api.params_show(deps=True)
这在只想查看影响流水线执行的参数时非常有用,可以过滤掉一些临时或调试用的参数。
指定版本和仓库
与其他 API 一样,params_show() 支持 repo 和 rev 参数,可以读取远程仓库或特定版本的参数。
# 从远程仓库读取参数
params = dvc.api.params_show(
repo="https://github.com/iterative/example-get-started"
)
# 读取特定版本的参数
params = dvc.api.params_show(rev="v1.0.0")
指标读取 metrics_show
dvc.api.metrics_show() 用于读取 DVC 项目中追踪的指标文件。指标通常是模型评估的结果,如准确率、损失值等。
基本用法
不带参数调用时,metrics_show() 返回所有追踪的指标。
import dvc.api
metrics = dvc.api.metrics_show()
print(metrics)
返回的字典结构以指标文件名为键,指标值为嵌套字典:
{
"avg_prec": { "train": 0.97, "val": 0.92 },
"roc_auc": { "train": 0.98, "val": 0.94 }
}
如果只有一个指标文件,返回的字典会直接包含指标值。
筛选指标文件
targets 参数可以指定要读取的指标文件路径,支持一个或多个文件。
# 读取单个指标文件
metrics = dvc.api.metrics_show("eval/train_val_metrics.json")
# 读取多个指标文件
metrics = dvc.api.metrics_show(
"eval/train_val_metrics.json",
"eval/test_metrics.json"
)
当指定多个目标时,返回的字典会按文件名组织:
{
"eval/train_val_metrics.json": {
"avg_prec": { "train": 0.97, "val": 0.92 },
"roc_auc": { "train": 0.98, "val": 0.94 }
},
"eval/test_metrics.json": {
"avg_prec": { "test": 0.72 },
"roc_auc": { "test": 0.77 }
}
}
指定版本和仓库
metrics_show() 同样支持 repo 和 rev 参数,可以读取远程仓库或特定版本的指标。
# 从远程仓库读取指标
metrics = dvc.api.metrics_show(
repo="https://github.com/iterative/example-get-started"
)
# 读取特定版本的指标
metrics = dvc.api.metrics_show(rev="HEAD~1")
这在比较不同版本的模型性能时非常有用,可以程序化地获取历史实验的结果。
Artifact 访问
Artifact 是 DVC 中用于管理模型和数据集的重要概念。dvc.api.artifacts_show() 提供了程序化访问 artifact 的能力,可以获取 artifact 的路径和 Git 版本信息。
基本用法
artifacts_show() 需要至少指定 artifact 的名称。
import dvc.api
artifact = dvc.api.artifacts_show(
'text-classification',
repo='https://github.com/iterative/example-get-started.git',
)
print(artifact)
返回的字典包含两个关键字段:
{
'path': 'model.pkl',
'rev': 'c7c6ae0',
}
path 是 artifact 在 DVC 仓库中的相对路径,rev 是对应的 Git 修订版本。当不提供 version 或 stage 参数时,默认返回最新版本的 artifact。
按版本或阶段获取
Artifact 可以通过语义化版本或生命周期阶段来访问,这两个参数是互斥的。
# 按版本获取
artifact = dvc.api.artifacts_show(
'text-classification',
version='v1.0.0',
repo='https://github.com/iterative/example-get-started.git',
)
# 按阶段获取
artifact = dvc.api.artifacts_show(
'text-classification',
stage='prod',
repo='https://github.com/iterative/example-get-started.git',
)
这在模型部署场景中特别有用,可以确保获取到特定版本或已部署到生产环境的模型。
读取 Artifact 内容
获取到 artifact 的路径和版本后,可以结合 dvc.api.read() 来读取内容。
import pickle
import dvc.api
# 获取 artifact 信息
artifact = dvc.api.artifacts_show(
'text-classification',
version='v1.0.0',
repo='https://github.com/iterative/example-get-started.git',
)
# 读取 artifact 内容
data = dvc.api.read(
artifact['path'],
rev=artifact['rev'],
repo='https://github.com/iterative/example-get-started.git',
mode='rb',
)
# 反序列化模型
model = pickle.loads(data)
这种方式避免了硬编码模型路径,通过 artifact 名称来访问,使代码更具可维护性。
下载 Artifact
如果需要将 artifact 下载到本地,可以结合 DVCFileSystem 使用。
import os
import dvc.api
# 获取 artifact 信息
artifact = dvc.api.artifacts_show(
'text-classification',
stage='prod',
repo='https://github.com/iterative/example-get-started.git',
)
# 创建文件系统对象
fs = dvc.api.DVCFileSystem(
'https://github.com/iterative/example-get-started.git',
rev=artifact['rev'],
)
# 下载 artifact
fs.get_file(artifact['path'], os.path.basename(artifact['path']))
这段代码将生产环境的模型下载到当前工作目录,文件名保持原样。这在部署脚本中很常见,可以自动化获取最新模型。
Artifact 名称解析
默认情况下,DVC 会在仓库根目录的 dvc.yaml 文件中查找 artifact 定义。如果 artifact 定义在其他位置的 dvc.yaml 中,需要使用特定格式指定名称。
# 指定路径到 dvc.yaml
artifact = dvc.api.artifacts_show(
'path/to/dvc.yaml:artifact_name',
repo='https://github.com/iterative/example-get-started.git',
)
# 或省略 dvc.yaml 文件名
artifact = dvc.api.artifacts_show(
'path/to:artifact_name',
repo='https://github.com/iterative/example-get-started.git',
)
这种灵活的命名方式支持复杂的项目结构,可以在不同子目录中管理多个 artifact。
总结
DVC 的 Python API 为自动化和集成提供了强大工具。DVCFileSystem 让我们能以统一接口访问 Git 和 DVC 追踪的文件,支持本地和远程仓库。实验数据查询 API 使程序化分析实验结果变得简单,特别是与 Pandas 结合时。参数和指标读取 API 为超参数优化和模型评估提供了基础设施。Artifact 访问 API 则简化了模型管理和部署流程。
这些 API 设计遵循 Python 惯例,返回标准数据结构,易于集成到现有工作流中。无论是构建自动化训练流水线、开发实验分析工具,还是实现模型服务,DVC Python API 都能提供必要的支持。
下一章将探讨大规模数据管理,包括大数据集优化、云存储版本控制和缓存迁移等高级主题,这些内容与 Python API 结合使用,能构建出更强大的 MLOps 系统。