Ultralytics YOLO27: 加入候补名单。
Get Started
使用 Ultralytics Platform 构建·训练与评估·课程 6/10
课程中级

比较运行并选出最佳模型

在 Platform 项目中比较运行,记录每次运行的目的,并通过脚本化的晋级门槛决定是否发布候选模型。

更大的模型通常能在主要指标上胜出,但可能会在没人检查的某个类别上落败。如果不提前制定规则,最终发布的就会是某个人最喜欢的那个模型。本课会记录每次运行,以便之后查明决策依据,并将“它更好吗?”转化为输出 PASS 或 FAIL 的门槛脚本。

学习成果

你可以在 Platform 项目中比较运行、记录每次运行的情况,并运行晋级门槛,在候选模型发布前检查整体指标、每个类别的指标和延迟。

快速通道
如果你已经熟悉相关内容,以下是简要版。
  1. 将相关运行放在同一个项目中;使用带有 Diff 的 Table 视图查看变更内容。

  2. 将运行命名为 <dataset-version>_<change>,并将假设和结果保存为 Custom Metadata。

  3. 查看结果前先声明门槛:mAP50-95 的提升要超过噪声范围,每个类别的 AP 至少保留 90%,且满足延迟预算。

  4. 使用门槛脚本在留出数据上确认入围模型;测试集拆分只在最后使用一次。

实践操作

在项目中比较运行#

Ultralytics Platform 物流项目的 Table 视图,侧边栏中列出模型,旁边并排显示各模型的训练参数和最终指标

一个 项目会列出模型,并提供复选框。选中模型后,其数据会叠加显示在 Charts 仪表板中;Table 视图会并排列出训练参数和最终指标;Diff 会隐藏相同的列,让你一眼看出两次运行只有 imgsz 不同。比较只能在同一个项目内进行,因此请将相关实验放在一起(模型可以在项目之间剪切和粘贴)。查看曲线时,要比较最终值、每次运行达到该值的速度,以及曲线的平滑程度。

Ultralytics Platform 物流项目的 Charts 视图,叠加显示多个模型的精确率和召回率曲线,Ask AI 面板则在表格中比较已完成的模型

有两个或更多已完成的模型时,Compare with AI 会要求 Ask AI 总结差异并提出下一项实验。请将它的建议视为下一次运行的假设,而不是结论。

让每次运行都有据可查#

名称承载着含义:run_5 没有说明任何信息,而 v3_pallets_added 和 v4_imgsz1024 则说明了数据集版本和变更内容。要记录决策依据,请在模型页面打开 More actions > Information,并将 JSON 说明保存为 Custom Metadata:

{
  "hypothesis": "Pallet AP is low because val holds only 7 pallets",
  "change": "Dataset v3: +80 pallet images from cam03 and cam07; fine-tuned from v2",
  "result": "Pallet AP 0.31 -> 0.58, overall mAP50-95 0.51 -> 0.59",
  "next": "Forklift recall drops at night; collect dawn and dusk footage"
}

Platform 会记录其他信息:Run Information 卡片会显示 GPU、每小时费率、总费用、ultralytics 版本、主机、父模型、固定的数据集版本、Git 提交记录(如果有),以及可复制的 yolo train 命令。只有使用相同数据集版本的运行才能公平比较,因此每次运行都应该固定一个数据集版本。

查看结果前先选定指标和门槛#

Ultralytics Platform 模型的 Validation 视图,包含 Precision-Recall、F1-Confidence、Precision-Confidence 和 Recall-Confidence 曲线,每个类别对应一条曲线

指标适用场景
mAP50-95检测、分割、姿态和 OBB 任务的默认指标:既能奖励紧致的边界框,也足够稳定,适合用于比较
mAP50目标较大且彼此间隔明显,对边界框紧致度要求不高
单个类别的 AP某个类别决定了业务价值
固定精确率下的召回率安全和报警系统
目标设备上的推理延迟始终设置为硬性上限

YOLO 性能指标指南解释了各项指标。候选模型只有通过所有门槛才能发布:

操作示例:编写晋级门槛脚本#

下载入围模型的 best.pt 文件,并使用相同代码在相同数据上评分。下面的示例使用两个官方检查点,因此可以在任何地方运行:

from ultralytics import YOLO

DATA = "coco8.yaml"  # identical held-out data for both runs; use your test split for the final call
BASELINE, CANDIDATE = "yolo26n.pt", "yolo26s.pt"  # e.g. best.pt from two Platform runs
NOISE, CLASS_FLOOR, LATENCY_BUDGET_MS = 0.005, 0.90, 100.0

def evaluate(weights):
    m = YOLO(weights).val(data=DATA, plots=False, verbose=False)
    per_class = {m.names[int(c)]: ap for c, ap in zip(m.box.ap_class_index, m.box.ap)}
    return m.box.map, per_class, m.speed["inference"]

(base_map, base_ap, _), (cand_map, cand_ap, cand_ms) = evaluate(BASELINE), evaluate(CANDIDATE)
weak = [n for n, ap in base_ap.items() if cand_ap.get(n, 0) < CLASS_FLOOR * ap]
gates = {
    f"mAP50-95 {base_map:.3f} -> {cand_map:.3f} beats noise": cand_map - base_map > NOISE,
    f"every class keeps {CLASS_FLOOR:.0%} of its AP (weak: {', '.join(weak) or 'none'})": not weak,
    f"inference {cand_ms:.1f} ms within {LATENCY_BUDGET_MS:.0f} ms": cand_ms <= LATENCY_BUDGET_MS,
}
for name, ok in gates.items():
    print("PASS" if ok else "FAIL", name)
print("verdict:", "ship the candidate" if all(gates.values()) else "keep the baseline, run more experiments")
raise SystemExit(0 if all(gates.values()) else 1)  # non-zero exit blocks a CI release step
PASS mAP50-95 0.669 -> 0.693 beats noise
FAIL every class keeps 90% of its AP (weak: elephant, potted plant)
PASS inference 44.1 ms within 100 ms
verdict: keep the baseline, run more experiments

结果:较大的模型在整体指标上领先 2.4 个百分点,但有两个类别的 AP 下降超过 10%,因此未通过。这就是只比较主要指标时会遗漏的性能退化。每类别字典来自 ap_class_index 和 ap,只包含数据中存在的类别;延迟是在运行脚本的机器上测得的,因此请在与你的部署目标类似的硬件上运行门槛检查。COCO8 只有 8 张验证图像,远不足以支持真正的决策。请使用至少数百张图像的留出集,并且只在最终评估时传入一次 split="test"。

深入检查表现较差的图像#

对于使用已保存数据集版本训练的检测任务,Validation 视图会提供 Validation insights:按每张图像的 F1 排序、叠加真实标注的表现最差和最好的图像;展示 F1 与图像尺寸、宽高比和实例数量之间相关性的特征探索器;以及最常与失败相关联的类别。在表现最差的图像上使用 Find similar,可以将相似图像作为未标注图像添加到训练集中,把门槛检查失败转化为明确的数据改进任务。

常见问题#

测试集变成了调参集

症状:报告的测试 mAP 很高,生产环境中的表现却更差。解决方法:使用验证集调参。如果你在测试集上比较了十次运行,那么测试集已经被用于调参,测得的数值也就偏乐观。只在最后阶段对最终候选模型进行一次测试集评分。

在不同数据集版本上比较运行结果

症状:某次运行“领先”了 4 分,却没人能复现原因。解决方法:检查每次运行的 Run Information 卡片中固定的数据集版本。如果版本不同,请在新版本上重新运行基线,然后再进行比较。

把噪声当成提升

症状:每次换一个随机种子重新训练,胜出的模型就会改变。解决方法:在大多数数据集上,差异低于约 0.5 个 mAP 点都属于不同运行之间的噪声。请在门控中保留噪声阈值;当结果如此接近时,应使用更大的留出集。

试试看

训练两次运行,每次只改变一项设置(图像尺寸、模型尺寸或数据集版本),在 Table 视图中使用 Diff 确认只有该参数不同,并为每次运行保存一条 Custom Metadata 说明。然后使用两份 best.pt 文件、留出数据和目标设备的延迟预算运行 gate.py,并记录结论。

完成条件
满足以下所有条件,即表示你已完成本课程。
  • 两次运行位于同一个项目中,Diff 显示只有一个参数发生变化。

  • 每次运行都有一条 Custom Metadata 说明,记录假设、变更和结果。

  • gate.py 已针对所有三个门槛输出 PASS 或 FAIL,你也已写下接下来的行动。

显示解答

药片模型的示例答案:基线模型 v1-baseline(10 个 epoch)对比候选模型 v2-finetune(再训练 5 个 epoch,SGD,lr0 0.001)。设置 DATA = "medical-pills.yaml" 和 gate.py 中的两个权重。输出:PASS mAP50-95 0.659 -> 0.687 beats noise、PASS every class keeps 90% of its AP (weak: none)、PASS inference 4.0 ms within 100 ms,结论为“发布候选模型”。在 v2 的 Custom Metadata 中记录:假设“v1 在第 10 个 epoch 时仍在提升”,变更“使用 SGD、lr0 0.001 微调 5 个 epoch”,结果“mAP50-95 0.659 -> 0.687”。请在元数据中注明注意事项:medical-pills 的验证集也用于指导训练,因此在投入生产前,请使用单独的留出集进行确认。

接下来做什么

你已经选出了最佳模型。下一步,你将为它配置专用端点,并通过能够应对冷启动的代码调用该端点。

登录以保存学习进度