使用 Python 和 DVC 进行数据版本控制详解

机器学习和数据科学带来了一系列与传统软件工程不同的问题。

版本控制系统帮助开发人员管理对源代码的更改。但是数据版本控制\管理模型和数据集的变化并没有那么完善。

因此许多团队正在积极开发工具和框架来解决这些问题。

这部分内容可能在编程初级阶段用不上,建议收藏保留以备以后使用。
在这里插入图片描述

数据版本控制

在标准软件工程中,许多人需要在共享代码库上工作并处理同一代码的多个版本。为了解决这个问题,开发人员使用版本控制系统,例如Git,帮助保持团队成员的组织性。
在这里插入图片描述

在版本控制系统中,有一个代表项目当前官方状态的中央代码库。开发人员可以复制该项目,进行一些更改,并要求他们的新版本成为正式版本。然后在将其部署到生产环境之前对他们的代码进行审查和测试。

在传统的开发项目中,这些快速的反馈周期每天可能发生多次。但商业数据科学和机器学习很大程度上缺少类似的约定和标准。数据版本控制是一组试图使版本控制过程适应数据世界的工具和过程。

建立允许人们快速工作并从其他人停止的地方继续工作的系统将提高交付结果的速度和质量。它将使人们能够透明地管理数据、有效地进行实验并与他人协作。

一种帮助研究人员管理数据和模型并运行可重复实验的工具是DVC,它代表数据版本控制。

什么是 DVC

DVC 是一个用 Python 编写的命令行工具。它模仿 Git 命令和工作流程以确保用户可以快速将其融入到他们的常规 Git 实践中。

DVC 旨在与 Git 一起运行。事实上 gitanddvc 命令经常一个接一个地串联使用。虽然 Git 用于存储和版本代码,但 DVC 对数据和模型文件执行相同的操作。

Git 可以在本地存储代码,也可以在GitHub、Bitbucket 或 GitLab 等托管服务上存储代码。同样 DVC 使用远程存储库来存储您的所有数据和模型。可以获取远程存储库的本地副本、修改文件,然后上传更改以与团队成员共享。可以在任何服务器上设置 DVC 远程存储库并将其连接到电脑,防止成员损坏或删除远程数据的保护措施。

当将数据和模型存储在远程存储库中时,会创建一个 .dvc 文件。文件 .dvc 是指向远程存储中的实际数据文件的小文本文件。该.dvc文件是轻量级的,旨在与代码一起存储在 GitHub 中。当你下载一个 Git 存储库时,也会得到这些 .dvc 文件。然后可以使用这些文件来获取与该存储库关联的数据。大数据和模型文件放在 DVC 远程存储中 .dvc 指向数据的小文件放在 GitHub 中。

设置工作环境

由于 DVC 是一个命令行工具,因此需要熟悉在操作系统的命令行中工作。如果是 Windows 用户,请查看在Windows 上运行 DVC。

  • 创建并激活虚拟环境。
  • 安装 DVC 及其必备 Python 库。
  • Fork 并克隆一个包含所有代码的 GitHub 存储库。
  • 下载免费数据集以在示例中使用。

环境配置

创建和激活虚拟环境

$ conda create --name dvc python=3.8.2 -y

$ conda activate dvc

安装三方库

$ conda config --add channels conda-forge

$ conda install dvc scikit-learn scikit-image pandas numpy

数据和代码

DVC 管理Fork源码

ImageNet Data

Imagenette Data
在这里插入图片描述
目录结构和说明

data-version-control/
|
├── data/ # 数据获取来源
│   ├── prepared/ # 内部修改数据
│   └── raw/ # 外部数据
|
├── metrics/ # 模型指标评估
├── model/ # 模型保存目录
└── src/ # 存储源代码
    ├── evaluate.py # 评估模型
    ├── prepare.py # 数据预处理
    └── train.py # 训练模型

Imagenette 是一个分类数据集,这意味着每个图像都有一个关联的类来描述图像中的内容。因此需要将数据转存到对应的文件夹下。

在10个类别的数据中可以提取2个模型的数据信息进行一个二元分类的模型判断。

data-version-control/
|
├── data/
│   ├── prepared/
│   └── raw/
│       ├── train/
│       │   ├── n01440764/
│       │   ├── n02102040/
│       │   ├── n02979186/
│       │   ├── n03000684/
│       │   ├── n03028079/
│       │   ├── n03394916/
│       │   ├── n03417042/
│       │   ├── n03425413/
│       │   ├── n03445777/
│       │   └── n03888257/
|       |
│       └── val/
│           ├── n01440764/
│           ├── n02102040/
│           ├── n02979186/
│           ├── n03000684/
│           ├── n03028079/
│           ├── n03394916/
│           ├── n03417042/
│           ├── n03425413/
│           ├── n03445777/
│           └── n03888257/
|
├── metrics/
├── model/
└── src/
    ├── evaluate.py
    ├── prepare.py
    └── train.py

基本 DVC 工作流程

DVC 如何与 Git 协同工作来管理的代码和数据。
在这里插入图片描述

准备工作

为实验创建一个分支

$ git checkout -b "first_experiment"

初始化 DVC

$ dvc init

备份数据和模型。dvc remote add 将位置远程存储并为其命名remote_storage。

$ dvc remote add -d remote_storage path/to/your/dvc_remote

此期间会生成一个 config 文件。

[core]
    analytics = false
    remote = remote_storage
['remote "remote_storage"']
    url = /path/xxxxxx/remote_storage

跟踪文件

将 train 和 val 目录添加到 DVC 控件

$ dvc add data/raw/train
$ dvc add data/raw/val

在执行过程中做了下面3件事:

  1. 将 train/ 和 val/ 文件夹添加到.gitignore
  2. 创建两个带有 .dvc 扩展名的文件,train.dvc 和 val.dvc
  3. 将train/ 和 val/ 复制到暂存区

运行 dvc add train/ 时,大文件的文件夹由DVC控制,小文件 .dvc 和 .gitignore 文件由Git控制。该 train/ 文件夹还进入暂存区域或缓存。
在这里插入图片描述
将大图像文件置于 DVC 控制下后,可以使用以下命令将所有代码和小文件添加到 Git 控制 git add。

$ git add --all # --all开关将 Git 可见的所有文件添加到暂存区域。

在这里插入图片描述
总结来说,大图像文件受 DVC 控制,小文件受 Git 控制。如果想处理的项目并使用 train/ 和 val/ 数据,那么首先需要下载的 Git 存储库。然后他们可以使用这些 .dvc 文件来获取数据。

上传文件

使用 git add创建快照 commit 提交上传。

$ git commit -m "First commit with setup and DVC files"

$ dvc push

DVC 将查看所有存储库文件夹以查找.dvc文件。这些文件会告诉 DVC 哪些数据需要备份,DVC 会将它们从缓存复制到远程存储。

在这里插入图片描述
将 Git 控制下的文件推送到 GitHub。

$ git push --set-upstream origin first_experiment

在这里插入图片描述

下载文件

先尝试这删除一些文件。

可以删除整个 val/ 文件夹,确保该.dvc文件不会被删除。

$ rm -rf data/raw/val

被删除的内容在缓存中取回数据。

$ dvc checkout data/raw/val.dvc

新机器上克隆 GitHub 存储库时,缓存将为空。可以使用 fetch 命令将远程存储的内容获取到缓存中。

$ dvc fetch data/raw/val.dvc

dvc pull 执行 dvc fetch,然后执行 dvc checkout。 在一次扫描中将您的数据从远程复制到缓存并复制到您的存储库中。 这些命令大致模仿了 Git 的功能,因为 Git 也有 fetch、checkout 和 pull 命令。
在这里插入图片描述

构建机器学习模型

主要步骤:

  1. 准备训练数据 prepare.py。
  2. 训练模型 train.py。
  3. 评估模型的性能 evaluate.py。

准备数据

为了使数据更易于使用将创建一个 CSV 文件,其中将包含用于训练的所有图像及其标签的列表。CSV 文件将有两列,一列包含单个图像的完整路径(filename),另一列将包含实际标签字符串(label)。

filename, label
full/path/to/data-version-control/raw/n03445777/n03445777_5768.JPEG,golf ball
full/path/to/data-version-control/raw/n03445777/n03445777_5768,golf ball
full/path/to/data-version-control/raw/n03445777/n03445777_11967.JPEG,golf ball
  • train.csv将包含用于训练的图像列表。
  • test.csv将包含用于测试的图像列表。

数据处理 prepare.py

from pathlib import Path

import pandas as pd

# 映射文件夹名称
FOLDERS_TO_LABELS = {
    
    
    "n03445777": "golf ball",
    "n03888257": "parachute"
    }

# 获取标签的文件列表
def get_files_and_labels(source_path):
    images = []
    labels = []
    for image_path in source_path.rglob("*/*.JPEG"):
        filename = image_path.absolute()
        folder = image_path.parent.name
        if folder in FOLDERS_TO_LABELS:
            images.append(filename)
            label = FOLDERS_TO_LABELS[folder]
            labels.append(label)
    return images, labels

# 保存label为 CSV 文件
def save_as_csv(filenames, labels, destination):
    data_dictionary = {
    
    "filename": filenames, "label": labels}
    data_frame = pd.DataFrame(data_dictionary)
    data_frame.to_csv(destination)

def main(repo_path):
    data_path = repo_path / "data"
    train_path = data_path / "raw/train"
    test_path = data_path / "raw/val"
    train_files, train_labels = get_files_and_labels(train_path)
    test_files, test_labels = get_files_and_labels(test_path)
    prepared = data_path / "prepared"
    save_as_csv(train_files, train_labels, prepared / "train.csv")
    save_as_csv(test_files, test_labels, prepared / "test.csv")

if __name__ == "__main__":
    repo_path = Path(__file__).parent.parent
    main(repo_path)

脚本完成后将在 data/prepared/ 文件夹中拥有 train.csv 和 test.csv 文件。 需要将这些添加到 DVC 并将相应的 .dvc 文件添加到 GitHub。

$ dvc add data/prepared/train.csv data/prepared/test.csv
$ git add --all
$ git commit -m "Created train and test CSV files"

训练模型

训​​练此模型使用一种称为监督学习的方法。该方法涉及向模型显示图像并使其猜测图像显示的内容。然后向它展示正确的标签。如果猜错了会自行纠正。对数据集中的每个图像和标签执行多次此操作。

模型训练 train.py

from joblib import dump
from pathlib import Path

import numpy as np
import pandas as pd
from skimage.io import imread_collection
from skimage.transform import resize
from sklearn.linear_model import SGDClassifier

# 加载图像数据
def load_images(data_frame, column_name):
    filelist = data_frame[column_name].to_list()
    image_list = imread_collection(filelist)
    return image_list

# 加载标签数据
def load_labels(data_frame, column_name):
    label_list = data_frame[column_name].to_list()
    return label_list

# 数据处理
def preprocess(image):
    resized = resize(image, (100, 100, 3))
    reshaped = resized.reshape((1, 30000))
    return reshape

# 模型加载数据
def load_data(data_path):
    df = pd.read_csv(data_path)
    labels = load_labels(data_frame=df, column_name="label")
    raw_images = load_images(data_frame=df, column_name="filename")
    processed_images = [preprocess(image) for image in raw_images]
    data = np.concatenate(processed_images, axis=0)
    return data, labels

# 模型训练主程序
def main(repo_path):
    train_csv_path = repo_path / "data/prepared/train.csv"
    train_data, labels = load_data(train_csv_path)
    sgd = SGDClassifier(max_iter=10)
    trained_model = sgd.fit(train_data, labels)
    dump(trained_model, repo_path / "model/model.joblib")

if __name__ == "__main__":
    repo_path = Path(__file__).parent.parent
    main(repo_path)

脚本完成后,将在 model/ 文件夹中保存一个经过训练的机器学习模型,名称为 model.joblib。 这是实验中最重要的文件。 需要将其添加到 DVC,并将相应的 .dvc 文件提交到 GitHub。

$ dvc add model/model.joblib
$ git add --all
$ git commit -m "Trained an SGD classifier"

评估模型

利用数字来表示模型的表现情况。

模型训练 evaluate.py

from joblib import load
import json
from pathlib import Path

from sklearn.metrics import accuracy_score

from train import load_data

def main(repo_path):
    test_csv_path = repo_path / "data/prepared/test.csv"
    test_data, labels = load_data(test_csv_path)
    model = load(repo_path / "model/model.joblib")
    predictions = model.predict(test_data)
    accuracy = accuracy_score(labels, predictions)
    metrics = {
    
    "accuracy": accuracy}
    accuracy_path = repo_path / "metrics/accuracy.json"
    accuracy_path.write_text(json.dumps(metrics))

if __name__ == "__main__":
    repo_path = Path(__file__).parent.parent
    main(repo_path)

执行脚本可以获得一个准确率。

{
    
     "accuracy": 0.7781238791545 }

准确率 JSON 文件非常小,将其保存在 GitHub 中很有用,这样可以快速检查每个实验的执行情况。

$ git add --all
$ git commit -m "Evaluate the SGD model accuracy"

版本数据集和模型

首先对 first_experiment 分支所做的所有更改推送到 GitHub 和 DVC 远程存储。

$ git push
$ dvc push

标记提交

创建一个标签以表明有一个现成的模型。

$ git tag -a sgd-classifier -m "SGDClassifier with accuracy 77.81%"

Git 标签不会通过常规提交推送,因此必须单独推送到 GitHub 上的存储库源或使用的任何平台。 使用 --tags 开关将所有标签从本地存储库推送到远程服务器。

$ git push origin --tags

可以随时查看当前存储库中的所有标签。

$ git tag

创建每一个 Git 分支

将模型的最大迭代次数设置为100。可以尝试将该数字设置得更高来观察结果是否改善,创建一个新分支并调用 sgd-100-iterations 。

$ git checkout -b "sgd-100-iterations"

train.py 中修改代码

def main(repo_path):
    train_csv_path = repo_path / "data/prepared/train.csv"
    train_data, labels = load_data(train_csv_path)
    sgd = SGDClassifier(max_iter=100)  # 修改这里
    trained_model = sgd.fit(train_data, labels)
    dump(trained_model, repo_path / "model/model.joblib")

然后进行模型的训练和评估,并且回生成新的模型文件和结果json文件。

$ python src/train.py
$ python src/evaluate.py

提交结果到 DVC 缓存中。

$ dvc commit

添加并提交 Git 中。

$ git add --all
$ git commit -m "Change SGD max_iter to 100"

标记新的实验。

$ git tag -a sgd-100-iter -m "Trained an SGD Classifier for 100 iterations"
$ git push origin --tags

将代码更改推送到 GitHub 并将 DVC 更改推送到您的远程存储。

$ git push --set-upstream origin sgd-100-iter
$ dvc push

从 GitHub 签出代码然后从 DVC 签出数据和模型来在分支之间跳转,也可以查看 first_example 分支并获取关联的数据和模型。

$ git checkout first_experiment
$ dvc checkout

DVC 文件查看

data-version-control/model/model.joblib.dvc

md5: 62bdac455a6574ed68a1744da1505745
outs:
  - md5: 96652bd680f9b8bd7c223488ac97f151
    path: model.joblib
    cache: true
    metric: false
    persist: false

共享程序开发

在计算机的某个位置创建一个新文件夹。调用shared_cache 作为 DVC 为该文件夹用作缓存。现在每次运行 dvc add 或 dvc commit 时,数据都会备份到该文件夹​​中。

$ dvc cache dir path/to/shared_cache

那么所有文件都将位于存储库的 .dvc/cache 文件夹中。数据从默认缓存移动到新的共享缓存中。

$ mv .dvc/cache/* path/to/shared_cache

机器上的所有用户现在可以将他们的存储库缓存指向共享缓存。
在这里插入图片描述
现在您拥有了一个共享缓存,所有其他用户都可以为他们的存储库共享该缓存。如果操作系统 (OS) 不允许所有人使用共享缓存,请确保系统上的所有权限均已正确设置。

数据缓存备份,检查存储库的.dvc/config文件。

[cache]
    dir = /path/to/shared_cache

创建可重现的Pipelines

机器学习的四个基本步骤 获取数据、数据预处理、训练模型、评估模型。

获取数据、数据预处理阶段

创建一个新分支并调用 sgd-pipeline。

$ git checkout -b sgd-pipeline

首先运行 prepare.py 作为 DVC Pipelines阶段。 执行此操作的命令是 dvc run,需要知道依赖项、输出和命令。

"""
依赖关系: prepare.py和data/raw中的数据
输出: train.csv和test.csv
命令: python prepare.py
"""

prepare.py 使用以下命令作为 DVC 管道阶段执行 dvc run。

$ dvc run -n prepare \ # 要运行 pipeline 阶段并将其称为准备
	     -d src/prepare.py -d data/raw \ # pipeline 需要 prepare.py 文件和 data/raw 文件夹
	     -o data/prepared/train.csv -o data/prepared/test.csv \ # pipeline  将生成 train.csv 和 test.csv 文件
	     python src/prepare.py # 执行的命令是 python src/prepare.py

DVC 将创建两个文件,dvc.yaml 和 dvc.lock 。

dvc.yaml

stages:
  prepare:
    cmd: python src/prepare.py
    deps:
      - data/raw
      - src/prepare.py
    outs:
      - data/prepared/test.csv
      - data/prepared/train.csv

dvc.lock

prepare:
  cmd: python src/prepare.py
  deps:
    - path: data/raw
      md5: a8a5252d9b14ab2c1be283822a86981a.dir
    - path: src/prepare.py
      md5: 0e29f075d51efc6d280851d66f8943fe
  outs:
    - path: data/prepared/test.csv
      md5: d4a8cdf527c2c58d8cc4464c48f2b5c5
    - path: data/prepared/train.csv
      md5: 50cbdb38dbf0121a6314c4ad9ff786fe

流程图
在这里插入图片描述

训练模型阶段

依赖项是文件 train.py。

$ dvc run -n train \
        -d src/train.py -d data/prepared/train.csv \
        -o model/model.joblib \
        python src/train.py

创建 Pipeline 的第二阶段并将其记录在 dvc.yml 和 dvc.lock 文件中。

流程图
在这里插入图片描述

评估模型阶段

依赖是 evaluate.py 。

$ dvc run -n evaluate \
        -d src/evaluate.py -d model/model.joblib \
        -M metrics/accuracy.json \
        python src/evaluate.py

使用 DVC 显示指标。

$ dvc metrics show
        metrics/accuracy.json:
            accuracy: 0.6996197718631179

流程图

在这里插入图片描述
一次性流程完整代码

$ git add --all
$ git commit -m "Rerun SGD as pipeline"
$ dvc commit
$ git push --set-upstream origin sgd-pipeline
$ git tag -a sgd-pipeline -m "Trained SGD as DVC pipeline."
$ git push origin --tags
$ dvc push

更换训练模型

我们还可以使用 随机森林分类器(random forest classifier)进行预测业务,可能会产生更好的效果。

$ git checkout -b "random_forest"

train.py 修改

from joblib import dump
from pathlib import Path

import numpy as np
import pandas as pd
from skimage.io import imread_collection
from skimage.transform import resize
from sklearn.ensemble import RandomForestClassifier

def main(path_to_repo):
    train_csv_path = repo_path / "data/prepared/train.csv"
    train_data, labels = load_data(train_csv_path)
    rf = RandomForestClassifier()
    trained_model = rf.fit(train_data, labels)
    dump(trained_model, repo_path / "model/model.joblib")

最后直接看一下结果吧。

$ dvc metrics show -T
sgd-pipeline:
    metrics/accuracy.json:
        accuracy: 0.7781238791545
forest:
    metrics/accuracy.json:
        accuracy: 0.8313875589354

猜你喜欢

转载自blog.csdn.net/qq_20288327/article/details/124009069
今日推荐