Ohhnews

分类导航

$ cd ..
Jetbrains Blog原文

在真实世界数据集上微调SOTA目标检测模型

#目标检测#模型微调#计算机视觉#深度学习#pycharm

在本系列的上一篇博客文章中,我们讨论了目标检测领域的最先进模型:架构、理论,以及 YOLO12、YOLO26 和 RF-DETR 的工作原理。如果你想了解这些模型的理论背景,请从那里开始。

这篇文章是实战篇:如何真正使用这些模型,如何在多样化的专业数据集上微调它们(这些数据集与它们的训练数据截然不同),以及如何评估结果——所有这些都在 PyCharm 中完成。

为什么需要微调?

你下载的每个预训练检测器都是在某种图像分布上训练的,几乎都是 COCO——一个包含约 11.8 万张日常场景训练图像、涵盖 80 个常见物体类别(人、汽车、狗、椅子等)的数据集。

现实世界部署中的数据很少与 COCO 相似。目标检测实际可能应用的场景,例如损坏的工业电缆、X 光片上的骨折、或货架上密集堆叠的汽水瓶,往往具有以下特点:

  • 词汇表外:“骨折”不属于 COCO 的 80 个类别之一,因此模型确实没有对应的输出类别。
  • 视觉分布之外:X 光影像、工业特写和严重遮挡的货架场景,在纹理、视角和物体密度方面与消费级照片差异巨大。

因此,在分布外数据上部署检测器需要微调。但在我们对这些模型动手之前,先确认一下:在我们自己的硬件上能否得到与开发者报告相近的结果。

模型

为了本次实验,我们将聚焦于当前三个 SOTA 目标检测系列,并检验每个模型的两种规格:

系列变体实现
YOLO12yolov12n, yolov12m原作者仓库
YOLO26yolo26n, yolo26mUltralytics PyPI 包
RF-DETRRFDETRNano, RFDETRBaseRoboflow PyPI 包

基准校验:复现 COCO val2017 基线

我们将使用六个预训练检查点:三个模型各两种规格。为了检查这些模型的表现是否符合预期,我们在完整的 5,000 张 COCO 验证集(val2017)上对它们全部进行了评估,以验证上一篇博文中报告的数据:

模型参数量 (M)mAP50mAP50-95延迟 (ms)
YOLOv12-N2.550.55480.402123.9
YOLO26-N2.570.54980.395212.3
YOLOv12-M19.670.69530.525972.4
YOLO26-M21.900.69060.518113.9
RF-DETR Nano30.470.67500.483512.4
RF-DETR Base32.170.72100.532512.9

即使我们还没有离开 COCO,有三个发现已经非常突出:

  1. 更大的模型(大多数情况下)性能更好。 RF-DETR Base 领先(mAP50-95 为 0.5325),但中等规模的 YOLO 也相当接近(0.5259 / 0.5181),而且参数量少了约 1,000 万。
  2. YOLO26 免 NMS 的设计在吞吐量上很有优势。 YOLO26-N 是参测模型中最快的(延迟 12.3 ms),其 mAP50-95 与 YOLOv12-N 基本持平;而 YOLOv12-N 虽然在这里是参数量最小的模型,速度却慢得多(延迟 23.9 ms)。注意力机制是有代价的。(如果想了解更多模型架构及其对性能影响的细节,请参阅本系列的上一篇博客文章。)
  3. RF-DETR Nano 按参数量来说并不“nano”(约 3,000 万——比 YOLO26-M 还多),但它的优化非常好:12.4 ms 延迟使其成为整体第二快的模型。

已发表的论文通常报告的是优化后的推理延迟:也就是说,它们只测量模型前向传播本身,剥离了目标检测流程中其他环节。我们有意跳过了这种激进优化,因此我们的数据反映的是在实际部署这些模型时会看到的真实情况。

因此,我们的延迟数据与模型白皮书中的基准并不一致。主要原因有两个:

  • 硬件: 我们使用的硬件不同于作为目标检测基准测试事实标准的 NVIDIA T4 GPU。
  • 未优化的计算图: 我们在模型的原始框架中运行它们,而不是转换为 TensorRT。TensorRT 会将网络编译为特定于硬件的引擎,融合层、为目标 GPU 选择最快的内核,并可选地以降低的精度运行。这可以大幅降低延迟,但生成的引擎与特定 GPU 绑定,并且需要额外的构建步骤,因此它并不代表这些模型开箱即用的性能。

精度则有所不同:虽然我们的延迟与已发布的数据存在差异,但我们的 mAP50-95 结果都落在所报告数值的合理噪声范围内。

既然我们已经看到了预训练模型在 COCO(它们的训练数据集)上的表现,接下来看看当它们在分布外数据上测试时会发生什么。

数据集

为了评估,我们使用了 RF100-VL,这是一个大规模的多模态数据集集合,包含 100 个数据集,刻意选取了在目标检测模型预训练数据中很少见的概念。这些数据集正好包含我们所关注的分布外目标,同时也反映了目标检测的常见现实应用,让我们能够真实地测试这些模型在实际环境中的能力。

我们挑选了三个数据集,从不同角度对模型进行压力测试:

数据集领域难点类别
cable-damage技术/工业视觉相似背景上的细粒度损伤类型break, thunderbolt
bone-fracture医学(X 光)完全不同的成像模态;特征细微angle, fracture, line, messed_up_angle
soda-bottles零售严重遮挡,每张图像中有大量近乎相同的实例coca-cola, fanta, sprite

第 1 步:设置项目

我们在这个项目中必须克服的首要挑战之一,是这三个实现并不共享一组兼容的依赖。具体来说,两代不同的 YOLO 需要不同版本的 ultralytics 包。PyCharm 为此提供了一个简洁的解决方案:一个 PyCharm 项目包含三个隔离的 uv 环境——每个模型家族一个。

我们将在远程 GPU 上运行计算。在 PyCharm 中配置远程解释器与配置本地解释器的工作流程相同:使用与本地情形相同的对话框和下拉菜单。请注意,远程解释器需要 PyCharm Professional;社区版仅支持本地环境。

首先,我们需要通过以下命令创建三个 uv 虚拟环境:

cd yolov12 && uv venv .venv --python 3.11

cd yolov26 && uv venv .venv --python 3.11

cd rf-detr && uv venv .venv --python 3.11

一旦你的 uv 虚拟环境创建好,就将每个环境注册为现有解释器。进入 Settings | Python | Interpreter,点击 Add Interpreter → Add Local Interpreter,将 Environment 选择为 Select existing,并把解释器字段指向该环境的 bin/python。PyCharm 在这里不会创建任何东西,它只是拾取 uv 已经构建好的环境。

对每个环境重复此操作。之后,切换环境只需要从 Settings | Python | Interpreter 下拉菜单,或从右下角状态栏的解释器组件中选择即可。

[LOADING...]

你可以在各个模型的项目仓库中找到每个模型所需的完整依赖列表。你可以选择在 PyCharm 内置的 Terminal 工具窗口中安装所有项目的依赖,也可以使用 Python Packages 工具窗口安装单独的包(包括选择特定版本的包)。点击 PyCharm 工具栏左下角的相关图标即可访问这两个工具窗口。

[LOADING...]

关于为所有三种模型设置环境的逐步指南,请参阅我们本教程的 GitHub 实现

第 2 步:获取数据集

为了获取 COCO 分布之外的数据集,我们可以通过 rf-detr 虚拟环境安装我们的数据集,因为它将 roboflow 作为核心依赖之一。然后我们将 Roboflow API 密钥设置为环境变量,以便在下载数据集时 API 可以使用它。

pip install roboflow

export ROBOFLOW_API_KEY="your_key_here" # you can get API key here: https://docs.roboflow.com/reference/authentication/authentication/find-your-roboflow-api-key 

完成上述设置后,现在你可以运行下面的 Python 脚本,以获取我们将在本教程中使用的三个数据集:

import os
from roboflow import Roboflow

api_key = os.environ.get("ROBOFLOW_API_KEY")

if not api_key:
    raise RuntimeError("ROBOFLOW_API_KEY is not set")

DATASETS = [
    "bone-fracture-7fylg",
    "cable-damage",
    "soda-bottles",
]

VERSION = 2          # RF100 projects are generally published at version 2
FORMAT = "yolov8"    # or "coco", "voc", "yolov5"
rf = Roboflow(api_key=api_key)
workspace = rf.workspace("rf100")

for slug in DATASETS:
    print(f"Downloading {slug} ...")

    try:
        project = workspace.project(slug)
        dataset = project.version(VERSION).download(FORMAT)
        print(f"  -> {dataset.location}")

    except Exception as e:
        print(f"  !! failed: {e}")

该脚本通过其 Python API 客户端连接到 Roboflow 云服务,并以 YOLOv8 格式下载三个指定的 RF100 数据集。它会遍历每个数据集,报告成功下载的保存位置,并在任何下载失败时打印错误。

第 3 步:在自定义数据上使用预训练模型获取零样本基线

在微调之前,我们直接在这三个数据集上评估 COCO 预训练检查点,以判断微调是否确实必要。结果非常明确:模型基本上什么也没有预测出来。

我们三个数据集测试集上的零样本 mAP50-95:

模型cable-damagebone-fracturesoda-bottles
RF-DETR Nano0.00040.00000.0027
RF-DETR Base0.00050.00000.0004
YOLOv12-N0.00070.00000.0266
YOLO26-N0.00000.00000.0033
YOLOv12-M0.00000.00000.0160
YOLO26-M0.00000.00000.0012

这是意料之中的,不是 bug!由于这些模型是封闭词汇检测器,也就是说,它们的目标类别数量是有限的,因此它们在物理上无法输出不在其 80 类 COCO 分类头中的类别,例如 fracture

这就是整篇文章的核心要点:一个在 COCO 上得分 0.72 mAP50 的模型,在骨裂检测上得分 0.00。预训练 ≠ 可部署,即使模型是最先进的。基本机器学习原理仍然适用,即使在人工智能时代!

第 4 步:微调

所有模型都在单个 A100 GPU 上微调了 10 个 epoch。我们使用标准的 Ultralytics/RF-DETR 微调流程,在这三个数据集上对模型进行了微调。我们为每个数据集微调了一个模型。完整的微调流程可以在项目仓库中每个模型对应的文件夹下的 finetune_rf100.py 脚本中找到。

下面你可以看到训练设置的核心。YOLO 和 RF-DETR 底层都基于 PyTorch,但训练循环被抽象在更高级的库 API 后面:YOLO 模型使用 Ultralytics 的 YOLO.train(),RF-DETR 则使用其自有的 train() 功能。

YOLO12 和 YOLO26

train_model = YOLO(args.model)

train_res = train_model.train(
                data=str(yaml_path),
                epochs=args.epochs,
                imgsz=args.imgsz,
                batch=args.batch,
                device=args.device,
                project=args.project,
                name=run_name,
                exist_ok=True,
                verbose=False,
            )

RF-DETR

ModelClass().train(
                dataset_dir=str(coco_dir),
                output_dir=str(output_dir),
                epochs=args.epochs,
                batch_size=args.batch_size,
                grad_accum_steps=args.grad_accum,
                lr=args.lr,
                resolution=resolution,
                early_stopping=True,
                checkpoint_interval=1,
            )

第 5 步:结果

微调改变了局面。你可以看到训练后在测试集上的结果:

[LOADING...]

左侧是预训练模型在 COCO 验证数据集上的结果。正如我们之前展示的,准确率(mAP50-95)介于 0.39 到 0.53 之间,除 YOLOv12-M 外,所有模型都表现出较低的延迟。右侧的微调模型在 cable-damagesoda-bottle 检测任务上显示出相似的准确率范围,仅在 bone-fracture 任务上表现更低。此外,微调模型在其目标任务上的延迟与预训练模型相当,而 YOLOv12-M 甚至更快。这表明,在针对目标领域进行微调后,模型所达到的性能与预训练模型在其原始训练领域上的性能大致相当。

下面让我们更仔细地看看微调模型的表现,按三个不同的 RF-100 数据集分别展示 mAP50 和 mAP50-95:

模型cable-damagebone-fracturesoda-bottles
RF-DETR Nano0.9195 (0.4391)0.2317 (0.1136)0.9617 (0.6223)
RF-DETR Base0.9281 (0.4456)0.4474 (0.1915)0.9688 (0.6332)
YOLOv12-N0.9236 (0.4378)0.0911 (0.0532)0.9677 (0.6343)
YOLO26-N0.8165 (0.3681)0.0193 (0.0064)0.9148 (0.5896)
YOLOv12-M0.8266 (0.3649)0.1500 (0.0635)0.9706 (0.6422)
YOLO26-M0.8707 (0.3896)0.2194 (0.1038)0.9596 (0.6304)

这些数字说明了什么:

  • soda-bottles 目标很容易获胜。 每个模型的 mAP50 都在 0.91--0.97 区间内。这可能是因为该领域(照片中的消费品)在视觉上与 COCO 中的现有类别接近,所以只有词汇表是新的。有趣的是,注意力模型家族在这里表现很好,YOLOv12-M 拔得头筹(0.6422 mAP50-95)。
  • cable-damage:检测容易,但定位困难。 mAP50 达到 0.93,但 mAP50-95 最高也只有 0.446。模型似乎能够可靠地找到损坏,但难以精确框出细长缺陷。如果你的应用需要高 IoU 下的紧密边界框,这个差距将是一个重要问题。
  • bone-fracture 仍然非常困难。 最好的模型(RF-DETR Base,0.447 mAP50)距离生产可用还很远,而且各模型之间的性能差距巨大。从照片到 X 射线的模态转变意味着预训练骨干网络的特征迁移效果很差。不同的图像模态以及细小、有时几乎无法区分的骨裂,使得检测任务比常见物体识别任务困难得多。这个数据集最能从特定领域的预训练、更多数据或更长的微调中受益。
  • RF-DETR Base 是表现最稳定的模型,在三个数据集中赢得了两个,并在第三个数据集上极具竞争力。基于 DETR 的架构似乎能更稳健地迁移到陌生领域。

定性结果

为了直观评估这些模型的表现,我们可以将预测的边界框叠加到图像上。让我们看看模型在每个类别的六张随机图像中检测到的物体:

[LOADING...] [LOADING...] [LOADING...]

我们可以看到,这证实了上面看到的准确率数值:冰箱中苏打水瓶的噪点图像被准确标注,每个物体都有紧密的边界框。电缆损坏的识别一致性较差,一些模型完全找不到损坏,另一些则生成了不必要的大边界框。最后,骨折图像与其他两类形成鲜明对比,不到一半的图像识别出任何骨折,且不同模型识别出不同的潜在骨折点。

结论

基于过去五年模型架构的进步,预训练目标检测器非常强大,但正如我们在这里看到的,预训练并不一定意味着可部署。所有六个模型在 COCO 上都表现良好,但当我们将同样的检查点直接应用于我们的专业数据集时,它们的性能几乎降为零。然而,微调彻底改变了这一局面。

仅仅经过 10 个 epoch 的微调,三个模型家族就能很好地适应 cable-damagesoda-bottle 数据集。正如我们指出的,soda-bottle 任务特别容易迁移,很可能是因为它包含的对象与 COCO 中的对象相似。cable-damage 也被相对可靠地检测出来,尽管 mAP50 与 mAP50-95 之间较大的差距表明,对所有模型来说,精确定位这些微小缺陷仍然具有挑战性。然而,bone-fracture 则是完全不同的情况,很可能是因为从 COCO 中的自然图像转换到 X 射线是一个大得多的领域偏移。虽然 RF-DETR 最好地应对了这一跳跃,但即使是它的性能也显示了微调的局限性,有时你可能需要考虑更多数据、更长时间的训练,甚至领域特定的预训练。

更广泛的启示是,不存在唯一的“最佳”检测器:它取决于具体任务。模型大小、延迟要求、许可限制,以及最重要的——模型预训练数据与目标领域之间的相似性,都会影响最终结果。重要的是,不要不加质疑地相信模型提供商报告的数字,而是要为你的具体任务探索特定模型的适配性。

今天就开始使用 PyCharm

在这篇文章中,我们从在 COCO 上验证预训练的 YOLO12、YOLO26 和 RF-DETR 检查点,到在专业数据上对它们进行零样本测试,再到在三个非常不同的目标检测任务上对它们进行微调,最后比较了由此产生的准确率和延迟。在此过程中,我们看到了 PyCharm 如何帮助管理此类项目的实际方面,即多个模型家族需要不同的依赖集和训练环境。

PyCharm 帮助你在一个项目中整合这些工作流,同时为每个模型家族使用隔离的 Python 环境。它的解释器管理、内置终端、Python Packages 工具窗口以及对远程开发的支持,使得在不同环境之间切换以及在远程 GPU 硬件上运行训练变得更加容易,而无需分别管理工作流的每个部分。

如果你想亲自尝试这些实验,不妨考虑针对你自己的特定目标检测用例微调这些模型!PyCharm 可供下载试用。你可以使用随附的项目代码重现我们的 COCO 基线,下载 RF100 数据集,微调模型,并使用留出的测试集分割来评估它们。

你可以在这个 GitHub 仓库 中找到该项目的完整代码。如果你想了解更多关于目标检测的信息,包括我们在这篇文章中使用的模型背后的架构,请查看本系列的 上一篇文章