教程:数据与模型版本控制
本示例的目标是让你亲身体验一个基本的机器学习版本控制场景:使用 DVC 管理多个数据集和 ML 模型。我们将使用 教程,该教程由 François Chollet 编写,用于展示如何利用一个非常小的数据集构建一个强大的图像分类器。
我们强烈建议您阅读 François 的原始教程。这是一个绝佳的示例,展示了如何利用通用的预训练模型,在资源极其有限的情况下构建出高性能的新模型。
我们首先使用 1000 张带标签的图像训练一个分类模型,然后将图像数量翻倍(2000 张)并重新训练模型。我们会记录下这两个数据集以及对应的模型结果,并演示如何使用 dvc checkout
在不同 工作区 版本之间切换。
用于训练和验证分类器的具体算法并不重要,也不需要事先掌握 Keras 的知识。我们将把原始博客文章中的 脚本 当作一个黑箱来复用——它接收一些数据并生成一个模型文件。
准备工作
本教程最近一次测试使用的是 Python 3.7。
运行本教程中的命令需要安装 Git。此外,如果尚未安装 DVC,请按照这些说明安装 DVC。
请参阅 在 Windows 上运行 DVC 以获取提升 Windows 使用体验的重要提示。
好了!让我们先下载代码并设置一个 Git 仓库:
$ git clone https://github.com/iterative/example-versioning.git
$ cd example-versioning
该命令拉取了一个包含单个脚本 train.py
的DVC 项目,该脚本将用于训练模型。
现在我们来安装依赖项。但在开始之前,我们强烈建议创建一个 虚拟环境:
$ python3 -m venv .env
$ source .env/bin/activate
$ pip install -r requirements.txt
您克隆的仓库已经初始化了 DVC。其中已包含一个 .dvc/
目录,内含 config
和 .gitignore
文件。这些以及其他文件和目录对用户是隐藏的,因为通常不需要直接与它们交互。
第一个模型版本
准备工作完成后,我们现在添加一些数据并训练第一个模型。我们将使用 DVC 记录所有内容,包括输入数据集和模型指标。
$ dvc get https://github.com/iterative/dataset-registry \
tutorials/versioning/data.zip
$ unzip -q data.zip
$ rm -f data.zip
该命令会下载并解压我们的原始数据集,其中包括 1000 张用于训练的带标签图像和 800 张用于验证的带标签图像。总共是一个 43 MB 的数据集,目录结构如下所示:
data
├── train
│ ├── dogs
│ │ ├── dog.1.jpg
│ │ ├── ...
│ │ └── dog.500.jpg
│ └── cats
│ ├── cat.1.jpg
│ ├── ...
│ └── cat.500.jpg
└── validation
├── dogs
│ ├── dog.1001.jpg
│ ├── ...
│ └── dog.1400.jpg
└── cats
├── cat.1001.jpg
├── ...
└── cat.1400.jpg
(谁不爱 ASCII 风格的目录图呢?)
让我们使用 dvc add
来记录当前数据集的状态:
$ dvc add data
你可以使用此命令来替代对过大而无法用 Git 跟踪的文件或目录执行 git add
:通常是输入数据集、模型、某些中间结果等。该命令会告诉 Git 忽略该目录,并将其放入缓存中(同时在工作区保留一个文件链接,以便你可以像以前一样继续工作)。这是通过创建一个微小且人类可读的.dvc
文件实现的,该文件作为指向缓存的指针。
接下来,我们使用 train.py
训练我们的第一个模型。由于数据集较小,此训练过程应该足够轻量,可以在大多数计算机上合理时间内完成(几分钟)。该命令输出一系列文件,其中包括 model.weights.h5
和 metrics.csv
,即训练后模型的权重和指标历史记录。捕获当前模型版本最简单的方法是再次使用dvc add
:
$ python train.py
$ dvc add model.weights.h5
我们在这里手动添加了模型输出,这并不理想。捕获命令输出的推荐方式是使用
dvc stage add
。稍后会详细介绍这一点。
让我们提交当前状态:
$ git add data.dvc model.weights.h5.dvc metrics.csv .gitignore
$ git commit -m "First model, trained with 1000 images"
$ git tag -a "v1.0" -m "model v1.0, 1000 images"
请注意,执行
train.py
会产生其他中间文件。这没有问题,我们稍后会使用它们。$ git status ... bottleneck_features_train.npy bottleneck_features_validation.npy`
第二个模型版本
假设我们的图像数据集大小翻倍。以下命令将提取 500 张新的猫图片和 500 张新的狗图片到 data/train
中:
$ dvc get https://github.com/iterative/dataset-registry \
tutorials/versioning/new-labels.zip
$ unzip -q new-labels.zip
$ rm -f new-labels.zip
为了简化,我们保持验证子集不变。现在我们的数据集有 2000 张用于训练的图片和 800 张用于验证的图片,总大小为 67 MB:
data
├── train
│ ├── dogs
│ │ ├── dog.1.jpg
│ │ ├── ...
│ │ └── dog.1000.jpg
│ └── cats
│ ├── cat.1.jpg
│ ├── ...
│ └── cat.1000.jpg
└── validation
├── dogs
│ ├── dog.1001.jpg
│ ├── ...
│ └── dog.1400.jpg
└── cats
├── cat.1001.jpg
├── ...
└── cat.1400.jpg
我们现在希望利用这些新标签重新训练模型:
$ dvc add data
$ python train.py
$ dvc add model.weights.h5
让我们提交第二个版本:
$ git add data.dvc model.weights.h5.dvc metrics.csv
$ git commit -m "Second model, trained with 2000 images"
$ git tag -a "v2.0" -m "model v2.0, 2000 images"
就这样!我们已经在 DVC 中跟踪了数据集、模型和指标的第二个版本,并通过 Git 提交了指向它们的.dvc
文件。现在让我们看看如果需要,DVC 如何帮助我们回退到之前的版本。
在工作区版本之间切换
DVC 中用于获取特定已提交数据版本的命令设计得类似于 git checkout
。在我们的情况下,只需额外运行dvc checkout
,即可将正确的数据恢复到工作区。
有两种方式可以实现这一点:完整工作区检出,或特定数据或模型文件的检出。我们先来看完整的检出。它非常直接:
$ git checkout v1.0
$ dvc checkout
这些命令会将工作区恢复到我们最早创建的第一个快照:代码、数据文件、模型,全部内容都会恢复。DVC 对此操作进行了优化,避免每次复制数据或模型文件。因此即使你拥有大型数据集、数据文件或模型,dvc checkout
也很快。
另一方面,如果我们想保留当前代码,但回退到之前的数据集版本,我们可以针对特定数据进行操作,如下所示:
$ git checkout v1.0 data.dvc
$ dvc checkout data.dvc
如果运行 git status
,你会看到 data.dvc
已被修改,当前指向数据集的 v1.0
版本,而代码和模型文件则来自 v2.0
标签。
自动化捕获
当你需要跟踪来自源项目的不同版本的数据集或模型文件时,使用 dvc add
是合理的。上面的 data/
目录(包含猫和狗的图像)就是一个很好的例子。
另一方面,有些文件是运行某些代码后生成的结果。在我们的示例中,train.py
生成了二进制文件(例如 bottleneck_features_train.npy
)、模型文件 model.weights.h5
以及指标文件 metrics.csv
。
当你有一个脚本,接收某些数据作为输入并产生其他数据输出时,更好的方式是使用 dvc stage add
来捕获它们:
如果你尝试过在工作区版本之间切换章节中的命令,请先回到主分支的代码和数据,并删除
model.weights.h5.dvc
文件,命令如下:$ git checkout master $ dvc checkout $ dvc remove model.weights.h5.dvc
$ dvc stage add -n train -d train.py -d data \
-o model.weights.h5 -o bottleneck_features_train.npy \
-o bottleneck_features_validation.npy -M metrics.csv \
python train.py
$ dvc repro
dvc stage add
会在 dvc.yaml
中写入一个名为 train
的管道阶段(通过 -n
选项指定)。它会像 dvc add
一样跟踪所有输出(-o
)。但与 dvc add
不同的是,dvc stage add
还会跟踪依赖项(-d
)以及用于生成结果的命令(python train.py
)。
此时,你可以运行
git add .
和git commit
,将train
阶段及其输出保存到仓库中。
当任何一个依赖项(-d
)发生变化时,dvc repro
就会重新运行 train
阶段。例如,当我们添加新图像以构建模型的第二个版本时,这就属于依赖项变更。该命令还会更新输出并将它们放入缓存中。
为了让事情更清晰一些:dvc add
和 dvc checkout
提供了模型和大型数据集版本管理的基本机制;而 dvc stage add
和 dvc repro
则为机器学习模型提供了一套构建系统,类似于软件构建自动化工具 Make。
接下来做什么?
在本示例中,我们的重点是让你亲身体验数据集和机器学习模型的版本控制。我们特别关注了 dvc add
和 dvc checkout
命令。此外,我们也想简要介绍一些你可能感兴趣的后续主题和思路,帮助你进一步了解 DVC 及其如何简化机器学习项目的管理。
首先,你可能已经注意到,训练模型的脚本是以一种整体化的方式编写的。每次运行时,它都会调用 save_bottleneck_feature
函数来预计算网络底层“冻结”的部分,并将特征写入文件。其初衷可能是第一次运行后可以注释掉 save_bottleneck_feature
,但每当数据集发生变化时都需要手动记住这一点,这并不方便。
DVC 的 pipelines 功能在此派上用场。我们在介绍 dvc stage add
和 dvc repro
时曾简要提及它。下一步是将脚本拆分为两部分并使用数据管道。请参阅 入门:数据管道 以动手实践管道功能,并尝试在此处应用它。如有疑问,欢迎随时加入我们的 社区 提问!
我们在这里只是简单提到的另一个细节,是通过 dvc stage add
命令的 -M
选项来捕获 metrics.csv
指标文件的方式。将此 输出 标记为指标后,我们便可以跨 Git 标签或分支(例如代表不同实验)比较其数值。更多关于使用 DVC 管理指标的信息,请参阅 dvc metrics
、比较变更 和 比较多个实验。