5.数据导入导出操作
在机器学习项目中,数据往往分散在各个角落:公共数据集托管在云端,团队的数据集注册表存放在远程仓库,生产环境的数据库持续产生新数据。如何把这些外部数据高效地引入本地项目,同时保持与源头的连接,是日常工作中绕不开的话题。DVC 提供了一套专门处理数据导入导出的工具集,让我们不必手动下载、解压、记录版本,也不必担心数据更新后如何同步。
发现数据:dvc list
在导入数据之前,首先需要知道数据在哪里、有什么。dvc list 命令类似于 Linux 的 ls,但它能同时显示 Git 和 DVC 跟踪的文件,即使这些数据实际存储在远程。
基本用法
查看远程仓库的内容:
$ dvc list https://github.com/iterative/dataset-registry get-started
.gitignore
data.xml
data.xml.dvc
这个命令会列出 get-started 目录下的所有文件。注意 data.xml 在 GitHub 网页上是看不到的,因为它被 DVC 跟踪,实际内容存储在远程存储中。dvc list 的优势就在于能穿透这种抽象,让我们看到完整的数据视图。
递归列出整个仓库的结构:
$ dvc list -R https://github.com/iterative/dataset-registry
.gitignore
README.md
get-started/.gitignore
get-started/data.xml
get-started/data.xml.dvc
images/.gitignore
images/dvc-logo-outlines.png
images/dvc-logo-outlines.png.dvc
...
这在探索数据注册表时特别有用。可以想象一个团队维护的中央数据仓库,里面按项目、按数据类型分类存放了大量数据集。通过 dvc list -R 能快速浏览全貌,找到需要的资源。
关键选项
--dvc-only:只显示 DVC 跟踪的文件,过滤掉纯 Git 管理的代码文件--rev:指定 Git 分支、标签或提交哈希,查看历史版本中的数据--json:以 JSON 格式输出,方便脚本处理
在 DVC 项目中,也可以列出本地工作区的数据:
$ dvc list . -R --dvc-only
data/features/train.pkl
data/features/test.pkl
model.pkl
这相当于快速查看当前项目中所有被跟踪的数据制品。
下载数据:dvc get
当只需要把数据下载到本地,而不关心后续更新时,dvc get 是最直接的选择。它类似于 wget 或 curl,但专门用于 DVC 仓库。
从 DVC 仓库下载模型
把远程仓库的模型文件下载到当前目录:
$ dvc get https://github.com/iterative/example-get-started model.pkl
$ ls
model.pkl
model.pkl 并不存在于 Git 仓库中,而是作为 train 阶段的输出被 DVC 跟踪。DVC 会自动从源项目的默认远程存储中拉取数据。这在部署场景下非常实用:CI/CD 流水线可以直接从模型注册表下载指定版本的模型,无需克隆整个仓库。
指定版本和输出路径
下载特定版本的文件,并重命名保存:
$ dvc get https://github.com/iterative/example-get-started \
model.pkl --rev baseline-experiment --out model.monograms.pkl
--rev 参数支持任意 Git 修订版本:分支名、标签、提交哈希。结合 --out 可以同时保留多个版本进行对比,这是 git checkout 无法直接做到的。
查看存储地址
有时只想知道数据实际存储在哪里,而不下载:
$ dvc get --show-url \
https://github.com/iterative/example-get-started model.pkl
https://remote.dvc.org/get-started/c8/d307aa005d6974a8525550956d5fb3
返回的是 DVC 远程存储中的完整 URL,可以直接用 wget 或 boto3 等工具下载。
Python API 方式
在代码中直接读取远程文件,无需下载到磁盘:
import dvc.api
import pickle
data = dvc.api.read(
'model.pkl',
repo='https://github.com/iterative/example-get-started',
mode='rb'
)
model = pickle.loads(data)
dvc.api.read() 一次性加载全部内容到内存,适合小型文件。对于大文件,使用 dvc.api.open() 以流式方式处理:
import dvc.api
from xml.sax import parse
with dvc.api.open(
'get-started/data.xml',
repo='https://github.com/iterative/dataset-registry'
) as f:
parse(f, handler)
这种方式不会占用本地磁盘空间,数据直接从远程存储流式传输到内存。
导入并跟踪:dvc import
dvc get 解决了"下载"问题,但它创建的是一个孤立的副本,与源数据失去了联系。如果源数据更新了,本地无法感知。dvc import 在下载的同时,会创建一个 .dvc 文件,记录数据来源,为后续更新留下可能。
基本工作流程
从外部 DVC 仓库导入数据:
$ dvc import https://github.com/iterative/dataset-registry \
get-started/data.xml -o data/data.xml
这会创建 data/data.xml.dvc 文件,内容如下:
deps:
- path: get-started/data.xml
repo:
url: https://github.com/iterative/dataset-registry
rev_lock: 96fdd8f12c14fa58a1b7354f15c7adb50e4e8542
outs:
- md5: 22a1a2931c8370d3aeedd7183606fd7f
path: data.xml
关键在 deps 字段:它保存了源仓库的 URL 和版本信息。rev_lock 是导入时源仓库的 Git 提交哈希,相当于给数据拍了个快照。
更新导入的数据
当源数据更新后,用 dvc update 同步:
$ dvc update data/data.xml.dvc
DVC 会检查源仓库的最新版本,如果有变化,就下载新数据并更新 .dvc 文件中的 rev_lock。这相当于 git pull 的数据版本。
固定版本
有时不想自动跟踪最新版,而是固定在某个标签:
$ dvc import --rev cats-dogs-v1 \
git@github.com:iterative/dataset-registry.git \
use-cases/cats-dogs
生成的 .dvc 文件会包含 rev 字段。后续执行 dvc update 不会生效,因为版本被明确指定。要切换到新版本,需要:
$ dvc update --rev cats-dogs-v2 cats-dogs.dvc
导入整个目录
dvc import 支持目录:
$ dvc import git@github.com:iterative/dataset-registry.git \
use-cases/cats-dogs
这会递归下载整个目录结构,并在本地创建对应的 .dvc 文件。目录导入在管理数据集时特别常见。
链式导入
DVC 支持从导入的数据中再次导入。假设有三个仓库 A、B、C:
- 仓库 A 用
dvc add跟踪data.csv - 仓库 B 用
dvc import从 A 导入data.csv - 仓库 C 用
dvc import从 B 导入training目录(包含data.csv)
在 C 中执行 dvc pull training.dvc 时,DVC 会自动解析链条,最终从 A 的远程存储下载数据。这要求所有中间仓库的远程存储都可访问,且权限配置正确。
从 URL 导入:dvc import-url
dvc import 处理的是 DVC 仓库之间的数据流转。如果数据存储在普通的云存储或 HTTP 服务器上,就用 dvc import-url。
跟踪 Web 数据
导入一个在线数据集:
$ dvc import-url https://data.dvc.org/get-started/data.xml \
data/data.xml
生成的 data.xml.dvc 文件:
deps:
- etag: '"f432e270cd634c51296ecd2bc2f5e752-5"'
path: https://data.dvc.org/get-started/data.xml
outs:
- md5: a304afb96060aad90176268345e10355
path: data.xml
这里用 etag 而不是 rev_lock 来判断数据是否变化。当源文件更新后,ETag 会改变,dvc update 就能检测到。
检测外部变化
模拟一个定期更新的数据源:
$ mkdir /tmp/dvc-import-url-example
$ wget https://data.dvc.org/get-started/data.xml -P /tmp/dvc-import-url-example/
$ dvc import-url /tmp/dvc-import-url-example/data.xml data/data.xml
编辑源文件后,更新导入:
$ dvc update data.xml.dvc
Importing '.../tmp/dvc-import-url-example/data.xml' -> 'data/data.xml'
这在构建依赖外部数据的流水线时很有用。可以把 dvc update 放在 dvc repro 之前,确保总是使用最新数据。
直接导入到远程
本地磁盘空间有限时,可以跳过下载,直接把数据传到 DVC 远程存储:
$ dvc import-url https://data.dvc.org/get-started/data.xml \
--to-remote
只创建 data.xml.dvc 文件,数据本身通过流式传输直接到达远程存储。团队成员执行 dvc pull 时再从远程存储下载到本地。
版本感知的云存储
如果源数据在 S3、Azure 或 GCS 上,并且启用了版本控制,DVC 可以跟踪版本 ID:
$ dvc import-url --version-aware s3://mybucket/data
生成的 .dvc 文件会记录每个文件的 version_id。dvc pull 时会从源存储的特定版本读取,而不是 DVC 远程。dvc push 也不会重复上传,因为 DVC 假设源存储已经负责版本管理。
从数据库导入:dvc import-db
对于存储在关系型数据库中的数据,DVC 提供了 dvc import-db 命令,可以把表或查询结果快照为 CSV 或 JSON 文件。
配置数据库连接
首先配置连接信息:
$ dvc config db.pgsql.url postgresql://user@hostname:port/database
$ dvc config --local db.pgsql.password password
密码必须用 --local 写入 .dvc/config.local,这个文件默认在 .gitignore 中,不会被提交。
支持的连接字符串格式遵循 SQLAlchemy 标准:
| 数据库 | 连接字符串示例 |
|---|---|
| PostgreSQL | postgresql://user:pass@host/db |
| MySQL | mysql://user:pass@host/db |
| SQLite | sqlite:///path/to/file.db |
| Snowflake | snowflake://user:pass@account/DB?warehouse=WH |
| Databricks | databricks://token:token@host:port/db?http_path=path |
需要安装对应的数据库驱动,DVC 使用 SQLAlchemy 作为底层引擎。
导入整张表
快照 customers_table 表:
$ dvc import-db --table customers_table --conn pgsql
生成 customers_table.csv 和 customers_table.csv.dvc:
deps:
- db:
file_format: csv
connection: pgsql
table: customers_table
outs:
- md5: 131543a828b297ce0a5925800bd88810
path: customers_table.csv
导入查询结果
执行 SQL 查询并保存结果:
$ dvc import-db --sql "SELECT * FROM customers WHERE active = true" --conn pgsql
默认保存为 results.csv,可以用 -o 指定输出文件名。
更新数据库快照
当数据库内容变化后,更新本地快照:
$ dvc update customers_table.csv.dvc
DVC 会重新执行查询,比较数据哈希,如果有变化就下载新数据并更新 .dvc 文件。
选择合适的导入策略
面对不同的数据源和使用场景,如何选择合适的命令?
| 场景 | 推荐命令 | 理由 |
|---|---|---|
| 临时下载,无需跟踪 | dvc get |
简单直接,不产生 .dvc 文件 |
| 需要跟踪更新,源是 DVC 仓库 | dvc import |
保留与源仓库的连接,支持 dvc update |
| 需要跟踪更新,源是云存储或 HTTP | dvc import-url |
支持 ETag 和版本 ID,检测源变化 |
| 数据在数据库中 | dvc import-db |
直接执行 SQL,生成文件快照 |
| 本地磁盘不足 | --to-remote 选项 |
跳过本地缓存,直接传到远程存储 |
| 云存储已启用版本控制 | --version-aware 选项 |
利用源存储的版本管理,避免重复上传 |
数据导入导出的工作流程
一个典型的数据获取流程可能是:
- 探索:用
dvc list浏览数据注册表,找到需要的资源 - 导入:用
dvc import下载并创建跟踪文件 - 处理:在流水线中使用数据,生成模型或特征
- 更新:定期运行
dvc update检查源数据变化 - 版本控制:将
.dvc文件提交到 Git,记录数据依赖关系
对于一次性分析任务,dvc get 或 dvc get-url 更合适。对于需要持续同步的生产流水线,dvc import 和 dvc import-url 提供了自动化更新的基础。
总结与展望
本章介绍了 DVC 中处理外部数据的核心命令:dvc list 发现数据,dvc get 下载数据,dvc import 导入并跟踪 DVC 仓库的数据,dvc import-url 处理通用 URL 数据,dvc import-db 对接数据库。每个命令都解决了特定场景下的痛点,从简单的下载到复杂的版本跟踪,从文件到数据库,覆盖了机器学习项目中的主要数据来源。
这些命令的共同点是通过 .dvc 文件记录元数据,将数据操作纳入版本控制体系。无论是 ETag、Git 提交哈希还是数据库查询,DVC 都能捕获足够的信息来判断数据是否过时,并在需要时自动更新。
下一章将深入 DVC 的核心功能——流水线。我们会看到如何把数据导入、预处理、训练、评估等步骤串联成可重现的工作流,如何利用依赖关系图自动执行必要的计算,以及如何通过缓存机制避免重复工作。数据导入导出是起点,流水线则是让机器学习项目真正自动化的关键。