如何在自定义数据集上微调 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 会根据形状进行权重迁移:
- 主干网络和颈部网络会完整迁移——这些层提取通用视觉特征,其形状与类别数量无关。
- 检测头会部分重新初始化——分类输出层(
cv3、one2one_cv3)的形状取决于类别数量(80 与 5)。初始化未匹配的行之前,系统会重新映射类别名称匹配的兼容行。检测头中的边界框回归层(cv2、one2one_cv2)形状固定,不受类别数量影响,因此会正常迁移。 - 更改类别数量时,绝大多数权重仍会迁移。例如,将 YOLO26n 从 COCO(80 个类别)微调到 5 类数据集时,会迁移 708 个权重张量中的 606 个,此外还会迁移按类别名称匹配的兼容分类行。
对于类别数量与预训练模型相同的数据集(例如,在另一个包含 80 个类别的数据集上微调 COCO 预训练权重),所有权重都会迁移,包括检测头。
使用名称别名迁移类别#
Ultralytics 会跨数据集按类别名称匹配并迁移分类头对应行,且匹配时忽略大小写和首尾空白字符。如果等价类别使用了不同名称,请在加载前于内存中重命名源检查点的类别。这样可以保留共享概念的预训练分类权重,否则这些权重会被视为未匹配并随机初始化。
此 Objects365 v2 到 COCO 的示例会重命名已加载检查点中的源类别,随后 train() 会将其作为预训练权重传入:
from ultralytics import YOLO
# 源 Objects365 v2 名称(转为小写)-> 目标 COCO 名称
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 类别名称采用首字母大写格式,因此按小写名称匹配
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") # 加载预训练模型
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,有助于保留预训练特征
使用 optimizer=auto 时,lr0 和 momentum 的值会被忽略。要手动控制学习率,请显式指定优化器: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——最终为每种模型尺寸选定了约一个 epoch 的预热时间。
完整的训练参数列表请参阅训练配置参考。如果需要参数未提供的行为——例如逐层学习率、梯度裁剪或自定义验证指标——请继承训练器类。
两阶段微调#
两阶段微调将训练分为两个阶段。第一阶段冻结主干网络,仅训练颈部网络和检测头,让检测层适应新类别,同时不影响预训练特征。第二阶段解冻所有层,并使用较低学习率训练整个模型,从而针对目标领域优化主干网络。
当目标领域与 COCO 差异很大(例如医疗图像、航空图像、显微图像)时,这种方法尤其有用,因为主干网络可能需要调整,而一次性训练所有层会导致不稳定。要通过基于回调的方法自动解冻,请参阅冻结和解冻主干网络。
from ultralytics import YOLO
# 阶段 1:冻结主干网络,训练检测头和颈部网络
model = YOLO("yolo26n.pt")
model.train(data="custom.yaml", epochs=20, freeze=10, name="stage1", exist_ok=True)
# 阶段 2:解冻所有层,使用较低 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.5或mosaic=0.0。 - 提高分辨率:对于包含小目标的数据集,请尝试
imgsz=1280,以保留细节。
微调后,原有类别上的性能下降#
这称为灾难性遗忘——模型仅在新数据上微调时会丢失先前学到的知识。如果不将原始数据集图像与新数据一起使用,遗忘几乎不可避免。可通过以下方式缓解:
- 合并数据集:微调时同时加入原有类别和新类别的示例。这是避免遗忘的唯一可靠方法。
- 冻结主干网络和颈部网络:冻结主干网络和颈部网络、只训练检测头,对使用极低学习率进行的短时间微调有所帮助。
- 减少训练 epoch 数量:模型仅在新数据上训练的时间越长,遗忘就越严重。
常见问题#
没有固定的最低数量——效果取决于任务复杂度、类别数量,以及目标领域与 COCO 的相似程度。与图像总量相比,图像的多样性(不同的光照、角度和背景)更重要。可以先用现有数据开始训练,如果验证指标不理想,再逐步增加数据。
最常见的原因包括图像路径无效、标签文件缺失或为空、YAML 中的
nc与实际标签文件不一致,或者置信度阈值过高。请参阅常见问题,查看完整的故障排查清单。这取决于数据集大小和领域相似度。对于与 COCO 领域相似的小型数据集,冻结骨干网络(
freeze=10)可以防止过拟合。对于与 COCO 领域差异很大的数据集,保持所有层解冻(freeze=None)可让骨干网络进行适配。有关详细建议,请参阅冻结层。在训练数据中加入原有类别的样本,与新类别的样本一起训练。如果无法做到这一点,冻结更多层(
freeze=10或更高层数)并降低学习率,有助于保留预训练知识。有关详情,请参阅原有类别上的性能下降。