5. 数据导入导出操作

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 是最直接的选择。它类似于 wgetcurl,但专门用于 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,可以直接用 wgetboto3 等工具下载。

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_iddvc 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.csvcustomers_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 选项 利用源存储的版本管理,避免重复上传

数据导入导出的工作流程

一个典型的数据获取流程可能是:

  1. 探索:用 dvc list 浏览数据注册表,找到需要的资源
  2. 导入:用 dvc import 下载并创建跟踪文件
  3. 处理:在流水线中使用数据,生成模型或特征
  4. 更新:定期运行 dvc update 检查源数据变化
  5. 版本控制:将 .dvc 文件提交到 Git,记录数据依赖关系

对于一次性分析任务,dvc getdvc get-url 更合适。对于需要持续同步的生产流水线,dvc importdvc import-url 提供了自动化更新的基础。

总结与展望

本章介绍了 DVC 中处理外部数据的核心命令:dvc list 发现数据,dvc get 下载数据,dvc import 导入并跟踪 DVC 仓库的数据,dvc import-url 处理通用 URL 数据,dvc import-db 对接数据库。每个命令都解决了特定场景下的痛点,从简单的下载到复杂的版本跟踪,从文件到数据库,覆盖了机器学习项目中的主要数据来源。

这些命令的共同点是通过 .dvc 文件记录元数据,将数据操作纳入版本控制体系。无论是 ETag、Git 提交哈希还是数据库查询,DVC 都能捕获足够的信息来判断数据是否过时,并在需要时自动更新。

下一章将深入 DVC 的核心功能——流水线。我们会看到如何把数据导入、预处理、训练、评估等步骤串联成可重现的工作流,如何利用依赖关系图自动执行必要的计算,以及如何通过缓存机制避免重复工作。数据导入导出是起点,流水线则是让机器学习项目真正自动化的关键。