数据集跟踪是构建健壮且可复现的机器学习模型的基础。在所有数据类型中,图像由于其高维性、变异性和存储需求而带来了独特的跟踪挑战。在本文中,我们将演示如何使用 MLflow 的实验跟踪功能和用户界面有效地跟踪图像数据集,为您提供实用的技术以增强计算机视觉工作流中对数据和模型的跟踪。
注意:本指南假定读者熟悉 MLflow 及其跟踪功能以及 PyTorch。对于初学者,请参阅 MLflow starters tutorial 和此 PyTorch Vision tutorial。
为什么要跟踪图像数据集?
跟踪图像数据集对于结构化的机器学习项目至关重要。它能确保:
- 高质量的训练数据: 与结构化数据集不同,图像数据集难以理解和解读,但它们对模型性能的重要性相同,仍然需要标注、整理和特征转换。
- 可重复性: 实验可以使用相同的数据集版本和预处理步骤进行复现。
- 数据和模型溯源: 跟踪会保留数据使用记录,这对于遵守数据治理以及追踪在训练、验证和测试中使用的模型和数据的谱系至关重要。
- 调试:跟踪有助于识别与数据质量或预处理相关的问题,这些问题可能影响模型性能。
理解图像数据集格式
在全球机器学习社区中存在许多数据集的格式。在这篇博客文章中,我们将使用一种广泛用于计算机视觉模型的格式 COCO 的传统文件格式以及同一数据集的 Hugging Face 版本。这两种格式各有优缺点,并且在 MLflow 跟踪方面具有不同的可能性。
使用原生文件格式的优点:
- 可直接在
pycocotools/torchvision中使用的数据加载器 - 加载速度快
- 更小的文件大小
- 简单的目录结构
- 工件可以被跟踪并显示,例如作为图像
- 不可追踪
- 不可查询
缺点:
- 需要一个自定义数据加载器
COCO: 常见场景中的物体
COCO 是计算机视觉中广泛使用的数据集格式,以其丰富的注释而闻名。 它支持:
- 目标检测、关键点检测、Stuff 分割、图像描述等。
- 基于 JSON 的注解,用于存储元数据。
我们将在整篇博文中使用此数据集。
Hugging Face 图像数据集
Hugging Face 提供了一个简单的 Image Folder 类型,用于从本地文件创建数据集。它支持:
了解数据集的属性对于有效的机器学习模型训练、测试和评估至关重要。这包括分析类别平衡、标注质量以及其他关键特征。通过彻底审查源数据的这些方面,可以确保数据集符合机器学习任务的要求,并识别可能影响模型性能的偏差或不足。因此,在项目的实验和完善过程中,需要将数据集与模型一起进行跟踪。
- 记录在特征转换/数据增强步骤中使用的参数。
- 跟踪数据集版本以实现可重现性。
- 将数据集与特定的模型训练运行关联。
使我们能够跟踪源数据的关键 API 之一是使用 mlflow.log_artifacts method、mlflow.log_input method,并且我们将看到在与 Hugging Face 一起使用时,加入 mlflow.data module 如何为数据集跟踪增加更多结构。我们将使用 mlflow.pytorch module doc 将模型与我们的数据集跟踪一起记录。
使用计算机视觉模型和图像数据集跟踪的示例
有两种方法可以记录图像数据集:
- 使用
mlflow.data(数据集 API).
COCO 数据集的关键工件是 instances.json 文件,它描述了您的图像数据集的元数据和标注。举例来说,可以通过分析 category 字段来检查数据集中的类别平衡。
安装 MLflow 及其他依赖项
首先在您的 python >= 3.10 环境中为两个代码示例安装依赖。仅使用第一个示例时可以省略 opencv,仅使用第二个示例时可以省略 pycocotools。
pip install mlflow datasets torch torchvision pycocotools opencv-python-headless psutil
如果想要跟踪 GPU 指标,也请安装 pynvml。
要下载 Hugging Face 数据集,还请确保已登录。
huggingface-cli login
其中一个示例需要计算资源;因此,请确保开启 MLflow system metrics,以便在训练期间跟踪计算资源上的情况。
export MLFLOW_ENABLE_SYSTEM_METRICS_LOGGING=true
注意:验证划分用于节省空间,但如果你想在整个数据集上训练/微调模型(需要额外 +25 GB 存储),也可以使用 "train" 划分。训练过程中还会使用训练轮数和数据集的一个子集。
将数据集作为工件与模型一起记录
由于 COCO 数据集是基于文件的,需要先下载文件。我们从官方作者的网站上使用该数据集最新版本中体积最小的一个版本。
# download the COCO val 2017 dataset
wget -P datasets http://images.cocodataset.org/zips/val2017.zip
unzip -q datasets/val2017.zip -d datasets
wget -P datasets http://images.cocodataset.org/annotations/annotations_trainval2017.zip
unzip -q datasets/annotations_trainval2017.zip -d datasets
rm datasets/val2017.zip & rm datasets/annotations_trainval2017.zip
import json
from torchvision.datasets import CocoDetection
from torchvision import models
import mlflow
# Load a COCO Dataset (val used to limit size)
img_folder = "datasets/val2017"
coco_annotation_file = "datasets/annotations/instances_val2017.json"
# Download dataset
dataset = CocoDetection(img_folder, coco_annotation_file)
# Load a pre-trained model from COCO
model = models.detection.fasterrcnn_resnet50_fpn(weights='COCO_V1')
# Set experiment name
mlflow.set_experiment("coco_experiment")
# Save dataset artifacts and model
with mlflow.start_run():
# log dataset
with open(coco_annotation_file, 'r') as f:
dataset_metadata = json.load(f)
mlflow.log_dict(dataset_metadata, "coco_annotation_file")
# log images
mlflow.log_artifact(img_folder, artifact_path="images")
# log model
mlflow.pytorch.log_model(model, "model")
# register model with a meaningful name
mlflow.register_model(
"runs:/{}/model".format(mlflow.active_run().info.run_id),
"fasterrcnn_resnet50_fpn_coco_2017_model"
)
我们可以进入我们的 MLflow UI,并查看在模型的实验运行下注册的数据集。

我们可以通过使用 Hugging Face 数据集以更结构化的方式来实现这一点,并利用一种便捷的方式来读取数据。通过这种方式,我们可以在同一次实验运行中将我们的 MLflow 跟踪的数据集、训练指标和模型全部放在一起!
import numpy as np
import cv2
import io
import mlflow
from torchvision import models
from torchvision.models.detection import FasterRCNN_ResNet50_FPN_Weights
import os
os.environ["MLFLOW_ENABLE_SYSTEM_METRICS_LOGGING"] = "true"
# Load the COCO dataset from Hugging Face
dataset = load_dataset("detection-datasets/coco", split="val")
# Transform to MLFlow Dataset
mlflow_dataset = mlflow.data.huggingface_dataset.from_huggingface(dataset)
# For this example we create a subset of the dataset with the first 100 rows
subset_dataset = dataset.select(range(100))
# Load a pre-trained object detection / segmentation model
model = models.detection.fasterrcnn_resnet50_fpn(weights=FasterRCNN_ResNet50_FPN_Weights.DEFAULT)
# Let’s fine-tune it, log dataset, metrics, and model in an MLflow Experiment run
mlflow.set_experiment("hg_image_experiment")
with mlflow.start_run():
# log training dataset in model training run
mlflow.log_input(mlflow_dataset, context="training")
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=0.0005)
for epoch in range(1): # We train for 1 epoch in this example
print(f"Training object detection model, epoch {epoch+1}...")
for row in subset_dataset: # We run a subset of the dataset to save time
# In this example we are not using a dataloader but just converting image bytes to ndarray
image_bytes = io.BytesIO()
row["image"].save(image_bytes, format="JPEG")
image_bytes = image_bytes.getvalue()
if isinstance(image_bytes, bytes):
image_array = np.frombuffer(image_bytes, np.uint8)
image = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
else:
raise TypeError("Expected bytes object for 'image', got {}".format(type(image_bytes)))
image = np.array(image)
# Prepare annotations as target
annotations = row["objects"]
target = []
for i in range(len(annotations['category'])):
d = {}
d['boxes'] = torch.tensor(annotations['bbox'][i], dtype=torch.float32).reshape(-1, 4) # Ensure shape [N, 4]
d['labels'] = torch.tensor([annotations['category'][i]], dtype=torch.int64) # Wrap in a list for correct shape
target.append(d)
# Convert the image to a PyTorch tensor and normalize it
image_tensor = torch.tensor(image, dtype=torch.float32).permute(2, 0, 1) / 255.0
# Perform forward pass in batches of one
input_batch = [image_tensor]
output = model(input_batch, target)
# Compute loss
loss_dict = output[0] if isinstance(output, list) else output
loss = sum(loss for loss in loss_dict.values())
# Backpropagation and optimization step
optimizer.zero_grad()
loss.backward()
optimizer.step()
# Pretty print the loss
print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
mlflow.log_metrics({"epoch": epoch+1})
mlflow.log_metrics({"loss": loss.item()})
# finally log model
mlflow.pytorch.log_model(
model,
"model",
input_example=input_batch
)

限制
- 存储开销: 记录大型数据集可能需要大量存储。
- 标注复杂度: 管理复杂的标注可能需要自定义脚本,如
pycocotools或开源工具如 CVAT,它也为图像数据集管理提供了丰富的 UI 功能。 - 可视化: MLflow 的用户界面和 Databricks 不适合可视化图像数据集标注,需要像
CVAT或自定义脚本这样的工具。
更多资源
我们希望本指南能帮助您借助 MLflow 简化图像数据集的跟踪,并为图像数据集带来一些新的想法。祝您机器学习模型训练顺利!
并且永远不要让那些 GPU/CPUs 冷却。 在 MLflow UI 中,在模型训练期间检查你的系统指标。


