YOLO Vision 2026:

如何在自定义数据集上微调 YOLO#

微调通过从已学习的权重开始,而不是随机初始化,使预训练模型适应识别新类别。与从头开始训练数百个 epoch 不同,微调利用预训练的 COCO 特征,只需很短时间即可在自定义数据上收敛。

本指南介绍如何在自定义数据集上微调 YOLO26,涵盖从基本用法到冻结层两阶段训练等高级技术。

微调与从头开始训练的对比#

预训练模型已经从数百万张图像中学习了通用视觉特征——边缘检测、纹理识别和形状理解。迁移学习通过微调复用这些知识,只教模型识别新类别的外观,因此收敛更快且所需数据更少。从头开始训练会丢弃所有这些知识,迫使模型从像素级模式开始学习一切,因此需要显著更多的资源。参阅模型 YAML 配置指南,了解仅包含架构的 .yaml 文件与检查点的区别。

微调从头开始训练
起始权重在 COCO 上预训练(80 个类别)随机初始化
命令YOLO("yolo26n.pt")YOLO("yolo26n.yaml")
收敛更快——主干网络已经训练完成更慢——所有层都从零开始学习
数据需求更低——预训练特征弥补了数据量较少的不足更高——模型必须仅从数据集中学习所有特征
适用场景包含自然图像的自定义类别与 COCO 有根本差异的领域(医疗、卫星、雷达)
微调无需额外代码

使用 YOLO("yolo26n.pt") 加载 .pt 文件时,预训练权重会存储在模型中。之后调用 .train(data="custom.yaml") 会自动将所有兼容权重传输到新的模型架构中,重新初始化不匹配的层(例如类别数量不同时的检测头),并开始训练。无需手动加载权重、操作层或编写自定义迁移学习代码。

预训练权重传输的工作原理#

当预训练模型在类别数量不同的数据集上进行微调时(例如将 COCO 的 80 个类别改为 5 个自定义类别),Ultralytics 会执行形状感知的权重传输:

  1. 主干网络和颈部网络完整传输——这些层提取通用视觉特征,其形状与类别数量无关。
  2. 检测头部分重新初始化——分类输出层(cv3one2one_cv3)的形状与类别数量相关(80 对 5)。在初始化不匹配的行之前,会重新映射类别名称匹配的兼容行。检测头中的边界框回归层(cv2one2one_cv2)无论类别数量如何,形状都固定,因此会正常传输。
  3. 更改类别数量时,绝大多数权重都会传输。例如,将 YOLO26n 从 COCO(80 个类别)微调到 5 类数据集时,会传输 708 个权重张量中的 606 个,此外还会传输按名称匹配的兼容分类行。

对于类别数量与预训练模型相同的数据集(例如在另一个 80 类数据集上微调 COCO 预训练权重),包括检测头在内的 100% 权重都会传输。

使用名称别名传输类别#

Ultralytics 会跨数据集按类别名称匹配分类头行,忽略大小写和首尾空格。当等价类别使用不同名称时,在加载前于内存中重命名源检查点中的类别。这样可以保留共享概念的预训练分类权重,否则这些权重会被视为不匹配并随机初始化。

Objects365 v2 到 COCO 的示例会重命名已加载检查点中的源类别,随后 train() 会将其作为预训练权重传递下去:

from ultralytics import YOLO

# Source Objects365 v2 name (lowercased) -> target COCO name
ALIASES = {
    "wild bird": "bird",
    "handbag/satchel": "handbag",
    "luggage": "suitcase",
    "bowl/basin": "bowl",
    "orange/tangerine": "orange",
    "monitor/tv": "tv",
    "stuffed toy": "teddy bear",
    "hair dryer": "hair drier",
}

model = YOLO("path/to/yolo26s-objects365.pt")
# Objects365 class names are Title-Cased, so match on the lowercased name
model.model.names = {i: ALIASES.get(name.lower(), name) for i, name in model.model.names.items()}
model.train(data="coco.yaml", epochs=100, imgsz=640)

cls_remap 设置默认启用基于名称的传输。训练会在复制兼容行时打印 Remapped N/M cls head rows from pretrained weights by class name;设置 cls_remap=False 可将其禁用。

基本微调示例#

示例
from ultralytics import YOLO

model = YOLO("yolo26n.pt")  # load pretrained model
model.train(data="custom.yaml", epochs=50, imgsz=640)

选择模型大小#

更大的模型容量更高,但需要更新的参数也更多;当训练数据有限时,这可能增加过拟合风险。从较小的模型(YOLO26n 或 YOLO26s)开始,仅在验证指标趋于平稳时再扩大模型规模,是一种实用方法。最佳模型大小取决于任务复杂度、类别数量、数据集多样性以及可用于部署的硬件。参阅完整的 YOLO26 模型页面,了解可用大小和性能基准。

优化器和学习率选择#

默认的 optimizer=auto 设置会根据训练迭代总数选择优化器和学习率:

  • 10,000 次迭代或更少(小型数据集或较少 epoch):使用较低的自动计算学习率和 AdamW
  • 超过 10,000 次迭代(大型数据集):使用 MuSGD(Muon+SGD 混合优化器),lr=0.01

对于大多数微调任务,默认设置无需手动调优即可良好运行。在以下情况下,可以考虑显式设置优化器:

  • 训练不稳定(损失突增或发散):尝试使用 optimizer=AdamW, lr0=0.001 以获得更稳定的收敛
  • 在小型数据集上微调大型模型:使用较低学习率的显式优化器(例如 optimizer=AdamW, lr0=0.001)有助于保留预训练特征
自动优化器会覆盖手动 lr0

当使用 optimizer=auto 时,lr0momentum 的值会被忽略。要手动控制学习率,请显式设置优化器:optimizer=SGD, lr0=0.005

冻结层#

冻结可防止特定层在训练期间更新。当数据集相对于模型容量较小时,这可以加快训练并减少过拟合

freeze 参数接受整数或列表。整数 freeze=10 会冻结前 10 层(索引 0-9),覆盖 YOLO26 主干网络的大部分。主干网络跨越第 0-10 层,因此 freeze=10 会保留最后的 C2PSA 模块(第 10 层)以供训练;使用 freeze=11 可冻结整个主干网络。列表可以包含类似 freeze=[0, 3, 5] 的层索引,用于部分冻结主干网络;也可以包含类似 freeze=["23.cv2", "23.one2one_cv2"] 的模块名称字符串,以精细控制层内的特定分支(此处为检测头的两个边界框回归分支)。

示例
from ultralytics import YOLO

model = YOLO("yolo26n.pt")
model.train(data="custom.yaml", epochs=50, freeze=10)

合适的冻结深度取决于目标领域与预训练数据的相似程度,以及可用训练数据的多少:

场景建议理由
大型数据集、相似领域freeze=None(默认)数据充足,可以调整所有层而不会过拟合
小型数据集、相似领域freeze=10保留主干网络特征,减少可训练参数
非常小型的数据集freeze=23仅训练检测头,最大限度降低过拟合风险
与 COCO 差异很大的领域freeze=None主干网络特征可能无法良好迁移,需要重新训练

冻结深度也可以作为超参数处理——尝试几个值(0、5、10)并比较验证 mAP,是为特定数据集找到最佳设置的实用方法。

微调的关键超参数#

微调通常比从头开始训练需要调整的超参数更少。最重要的参数包括:

  • epochs:微调比从头开始训练收敛更快。从中等值开始,并使用 patience 在验证指标趋于平稳时提前停止。
  • patience:默认值 100 是为长时间训练运行设计的。将其降低到 10-20,可以避免在已经收敛的运行上浪费时间。
  • warmup_epochs:预热会在最初几个 epoch 内将学习率平滑调整到计划值,从而降低早期批次扰乱预训练特征的可能性。微调时应保持其非零,但不需要完整的 3-epoch 默认值:官方 YOLO26 COCO 微调背后的进化搜索——从 Objects365 权重开始的多 epoch 续训——为每种模型大小确定的值约为 1 个 epoch。

完整的训练参数列表请参阅训练配置参考。对于参数未提供的行为——每层学习率、梯度裁剪或自定义验证指标——请继承 trainer实现。

两阶段微调#

两阶段微调将训练分为两个阶段。第一阶段冻结主干网络,仅训练颈部网络和检测头,使检测层能够适应新类别而不扰乱预训练特征。第二阶段解冻所有层,并使用较低学习率训练完整模型,以针对目标领域优化主干网络。

当目标领域与 COCO 差异显著时(医疗图像、航空图像、显微镜图像),这种方法尤其有用,因为主干网络可能需要适应,而一次性训练所有层会导致不稳定。有关基于回调的方法自动解冻,请参阅冻结和解冻主干网络

两阶段微调
from ultralytics import YOLO

# Stage 1: freeze backbone, train head and neck
model = YOLO("yolo26n.pt")
model.train(data="custom.yaml", epochs=20, freeze=10, name="stage1", exist_ok=True)

# Stage 2: unfreeze all, fine-tune with lower lr
model = YOLO("runs/detect/stage1/weights/best.pt")
model.train(data="custom.yaml", epochs=30, optimizer="AdamW", lr0=0.001, name="stage2", exist_ok=True)

常见问题#

模型不生成预测结果#

  • 训练数据不足:使用极少样本进行训练是最常见的原因——数据太少时,模型无法学习或泛化。在调查其他原因之前,请确保每个类别都有足够且多样的示例。

  • 检查数据集路径:无效的图像路径会引发数据集错误。单个缺失或空标签文件会被视为背景图像,并在扫描期间报告;没有标签的训练分割会引发错误。训练前请验证数据集:

    yolo detect val model=yolo26n.pt data=custom.yaml
  • 降低置信度阈值:如果存在预测结果但被过滤掉,请在推理时尝试使用 conf=0.1

  • 验证类别数量:确保 data.yaml 中的 nc 与标签文件中的实际类别数量一致。

验证 mAP 过早趋于平稳#

  • 添加更多数据:微调可以显著受益于额外的训练数据,尤其是包含不同角度、光照和背景的多样化示例。
  • 检查类别平衡:代表性不足的类别会有较低的 AP。添加更多示例,或在验证集上调节 cls_pw
  • 减少增强:对于非常小的数据集,过强的数据增强可能弊大于利。尝试使用 mosaic=0.5mosaic=0.0
  • 提高分辨率:对于包含小目标的数据集,尝试使用 imgsz=1280 以保留细节。

微调后原有类别的性能下降#

这称为灾难性遗忘——模型仅在新数据上进行微调时,会丢失之前学到的知识。如果不将原始数据集图像与新数据一起纳入训练,遗忘大多无法避免。要缓解这一问题:

  • 合并数据集:微调时将原有类别的示例与新类别一起纳入。这是防止遗忘的唯一可靠方法。
  • 冻结主干网络和颈部网络:冻结主干网络和颈部网络,仅训练检测头,有助于在非常低的学习率下进行短时间微调。
  • 减少训练 epoch:模型仅在新数据上训练的时间越长,遗忘就越严重。

常见问题#

  • 没有固定的最低数量——结果取决于任务复杂度、类别数量以及领域与 COCO 的相似程度。更多样化的图像(不同的光照、角度和背景)比单纯的数量更重要。先从现有数据开始,如果验证指标不足,再逐步增加数据。

  • 加载预训练的 .pt 文件,并使用自定义 data.yaml 的路径调用 .train()。Ultralytics 会自动处理权重传输、检测头重新初始化和优化器选择。完整代码示例请参阅基本微调部分。

  • 最常见的原因包括无效的图像路径、缺失或空标签文件、YAML 中的 nc 与实际标签文件不匹配,或置信度阈值过高。完整的故障排查清单请参阅常见问题

  • 这取决于数据集大小和领域相似度。对于与 COCO 领域相似的小型数据集,冻结主干网络(freeze=10)可以防止过拟合。对于与 COCO 差异很大的领域,保持所有层解冻(freeze=None)可以让主干网络进行适应。详细建议请参阅冻结层

  • 在训练数据中将原有类别的示例与新类别一起纳入。如果无法做到这一点,冻结更多层(freeze=10 或更高值)并使用较低学习率,有助于保留预训练知识。更多详情请参阅原有类别的性能下降

评论