入门:数据管道
对数据科学中的大型数据文件和目录进行版本控制功能强大,但往往还不够。在训练机器学习模型之前,数据需要经过过滤、清洗和转换——为此,DVC 引入了一个构建系统,用于定义、执行和跟踪数据管道(data pipelines),即一系列数据处理阶段,最终生成一个结果。
💫 DVC 是机器学习项目的“Makefile”系统!
DVC 管道通过 Git 进行版本控制,使你能够更好地组织项目,并随时复现完整的工作流和结果。你可以记录一个简单的 ETL 工作流,组织你的项目结构,或构建一个复杂的 DAG(有向无环图)管道。
稍后,我们将发现 DVC 允许你在这些管道的基础上管理机器学习实验——控制其执行过程、注入参数等。
设置
在已初始化的 DVC 项目中,我们先获取一些示例代码以进行后续步骤:
$ wget https://code.dvc.org/get-started/code.zip
$ unzip code.zip && rm -f code.zip
通过以下方式获取示例代码:
$ tree
.
├── params.yaml
└── src
├── evaluate.py
├── featurization.py
├── prepare.py
├── requirements.txt
└── train.py
运行此示例所需的数据可通过 dvc get
下载,并使用 dvc add
跟踪(如果你是从数据版本控制章节一路跟过来的,可能已经拥有这些数据):
$ dvc get https://github.com/iterative/dataset-registry \
get-started/data.xml -o data/data.xml
$ dvc add data/data.xml
现在,让我们完成一些常见的项目设置步骤(虚拟环境、依赖项、Git)。
首先,创建并使用一个虚拟环境(这不是必须的,但我们强烈推荐这样做):
$ virtualenv venv && echo "venv" > .gitignore
$ source venv/bin/activate
接下来,安装 Python 依赖项:
$ pip install -r src/requirements.txt
最后,现在是将代码提交到 Git 的好时机:
$ git add .github/ data/ params.yaml src .gitignore
$ git commit -m "Initial commit"
管道阶段
使用 dvc stage add
创建阶段(stages)。这些阶段代表处理步骤(通常是用 Git 跟踪的脚本/代码),并组合形成完整的管道。阶段可以将代码与其对应的数据输入和输出连接起来。让我们将一个 Python 脚本转换为一个阶段:
$ dvc stage add -n prepare \
-p prepare.seed,prepare.split \
-d src/prepare.py -d data/data.xml \
-o data/prepared \
python src/prepare.py data/data.xml
会生成一个 dvc.yaml
文件。其中包含我们要运行的命令(python src/prepare.py data/data.xml
)、它的依赖项和输出项的信息。
DVC 使用管道定义来自动跟踪每个阶段所使用的和生成的数据,因此无需手动对 data/prepared
执行 dvc add
!
上述命令选项的详细说明:
-
-n prepare
指定阶段的名称。如果你打开dvc.yaml
文件,会看到一个名为prepare
的部分。 -
-p prepare.seed,prepare.split
定义了一种特殊类型的依赖项——参数。任何阶段都可以依赖参数文件中的参数值(默认为params.yaml
)。我们将在指标、参数和图表页面中进一步讨论这些内容。
prepare:
split: 0.20
seed: 20170428
-
-d src/prepare.py
和-d data/data.xml
表示该阶段依赖于这些文件(依赖项)才能运行。请注意,源代码本身也被标记为依赖项。如果其中任何一个文件发生变化,当执行管道时,DVC 就会知道该阶段需要被重新运行。 -
-o data/prepared
指定该脚本的输出目录,脚本会在其中写入两个文件。这是运行后工作区的结构:
. ├── data │ ├── data.xml │ ├── data.xml.dvc +│ └── prepared +│ ├── test.tsv +│ └── train.tsv +├── dvc.yaml +├── dvc.lock ├── params.yaml └── src ├── ...
-
最后一行,
python src/prepare.py data/data.xml
是此阶段要运行的命令,它会被保存到dvc.yaml
文件中,如下所示。
生成的 prepare
阶段包含了上述所有信息:
stages:
prepare:
cmd: python src/prepare.py data/data.xml
deps:
- src/prepare.py
- data/data.xml
params:
- prepare.seed
- prepare.split
outs:
- data/prepared
DVC 可以通过将所有数据保留在项目内来简化工作流,但如果已有大型数据集存储在其他位置且不想复制,或某个阶段直接将数据写入云存储时,这种方式并不总是实用。DVC 仍能检测到这些外部数据集的变化。你的管道依赖项可以指向任意位置,而不仅限于项目内的本地路径。输出也是如此,但你需要设置 cache: false
来告诉 DVC 不要对这些外部输出创建本地副本。请参见下文示例,或阅读 外部依赖项和输出 了解更多信息。
stages:
prepare:
cmd:
- wget
https://sagemaker-sample-data-us-west-2.s3-us-west-2.amazonaws.com/autopilot/direct_marketing/bank-additional.zip
-O bank-additional.zip
- python sm_prepare.py --bucket mybucket --prefix project-data
deps:
- sm_prepare.py
- https://sagemaker-sample-data-us-west-2.s3-us-west-2.amazonaws.com/autopilot/direct_marketing/bank-additional.zip
outs:
- s3://mybucket/project-data/input_data:
cache: false
添加阶段后,你可以使用 dvc repro
运行整个管道。
依赖图
通过多次使用 dvc stage add
,并将某个阶段的输出定义为另一个阶段的依赖项,我们可以描述一系列相互依赖的命令,最终得到期望的结果。这就是我们所说的依赖图,它构成了一个完整且连贯的管道。
让我们创建第二个与 prepare
阶段输出相连的阶段,用于执行特征提取:
$ dvc stage add -n featurize \
-p featurize.max_features,featurize.ngrams \
-d src/featurization.py -d data/prepared \
-o data/features \
python src/featurization.py data/prepared data/features
现在 dvc.yaml
文件将被更新以包含这两个阶段。
最后,让我们添加第三个 train
阶段:
$ dvc stage add -n train \
-p train.seed,train.n_est,train.min_split \
-d src/train.py -d data/features \
-o model.pkl \
python src/train.py data/features model.pkl
最终,我们的 dvc.yaml
应该包含全部三个阶段。
此时最好使用 Git 提交这些更改。这些更改包括 .gitignore
文件和 dvc.yaml
—— 后者描述了我们的管道。
$ git add .gitignore data/.gitignore dvc.yaml
$ git commit -m "pipeline defined"
很好!现在我们可以运行这个管道了。
重新运行(Reproducing)
dvc.yaml
中的管道定义使我们能够轻松地重现整个流程:
$ dvc repro
你会注意到系统创建了一个 dvc.lock
文件(即“状态文件”),用于记录此次运行的结果。
dvc repro
依赖于 依赖图(在 dvc.yaml
中定义),并利用 dvc.lock
来确定具体需要运行哪些部分。
dvc.lock
文件类似于 .dvc
文件 —— 它记录了所用依赖项的哈希值(大多数情况下是 md5
)以及参数的取值。它可以被视为管道的状态:
schema: '2.0'
stages:
prepare:
cmd: python src/prepare.py data/data.xml
deps:
- path: data/data.xml
md5: 22a1a2931c8370d3aeedd7183606fd7f
size: 14445097
- path: src/prepare.py
md5: f09ea0c15980b43010257ccb9f0055e2
size: 1576
params:
params.yaml:
prepare.seed: 20170428
prepare.split: 0.2
outs:
- path: data/prepared
md5: 153aad06d376b6595932470e459ef42a.dir
size: 8437363
nfiles: 2
可以使用 dvc status
命令来比较工作区与当前实际状态之间的差异。
最佳实践是在 dvc.lock
创建或修改后立即提交到 Git,以记录当前的状态和结果:
$ git add dvc.lock && git commit -m "first pipeline repro"
让我们尝试稍微玩点花样。首先,更改训练阶段的一个参数:
- 打开
params.yaml
文件,将n_est
改为100
,然后 - (重新)运行
dvc repro
。
你会看到:
$ dvc repro
Stage 'prepare' didn't change, skipping
Stage 'featurize' didn't change, skipping
Running stage 'train' with command: ...
DVC 检测到只需运行 train
阶段,其余部分都被跳过!所有中间结果都被复用了。
现在,让我们将其改回 50
并再次运行 dvc repro
:
$ dvc repro
Stage 'prepare' didn't change, skipping
Stage 'featurize' didn't change, skipping
和之前一样,无需重新运行 prepare
、featurize
等步骤。但这一次,它也不会重新运行 train
!此前使用相同输入集(参数和数据)的运行结果已被保存在 DVC 的 运行缓存 中,并被直接复用。
可视化
构建完我们的流水线后,我们需要一种有效的方式来理解其结构。将其可视化为连接各阶段的图有助于实现这一点。DVC 允许你在不离开终端的情况下完成该操作!
$ dvc dag
+---------+
| prepare |
+---------+
*
*
*
+-----------+
| featurize |
+-----------+
*
*
*
+-------+
| train |
+-------+
请参考 dvc dag
了解此命令可视化的其他方式。
总结
DVC 流水线(dvc.yaml
文件、dvc stage add
和 dvc repro
命令)解决了几个重要问题:
- 自动化:以“智能”方式运行一系列步骤,从而加快项目迭代速度。DVC 能自动确定项目中需要重新运行的部分,并对“运行”及其结果进行缓存,避免不必要的重复执行。
- 可复现性:
dvc.yaml
和dvc.lock
文件描述了应使用的数据以及生成流水线结果(例如机器学习模型)所需的命令。将这些文件存储在 Git 中,便于版本控制和共享。 - 面向机器学习的持续交付与持续集成(CI/CD):以可构建和可复现的方式描述项目,是引入 CI/CD 系统的前提条件。可查看我们的姊妹项目 CML 获取一些示例。