YOLO26模型结构修改?yaml文件自定义教程
你是否遇到过这样的问题:想在YOLO26上尝试新的网络结构,比如加一个注意力模块、换掉某个检测头、或者调整特征融合方式,但打开yolo26.yaml文件却一脸懵——里面全是缩进、冒号和一堆没注释的参数?别急,这篇教程不讲抽象理论,不堆晦涩术语,就带你用最直白的方式,把yaml文件真正“看懂、改对、跑通”。
这不是一份照着复制粘贴就能用的模板文档,而是一份写给真实开发者的实操笔记。我会告诉你哪些地方绝对不能乱动,哪些缩进多一个空格就会报错,哪些参数改了反而让模型变差,甚至包括我踩过的坑和调试时的小技巧。无论你是刚接触YOLO的新手,还是想快速验证结构想法的工程师,都能在这里找到能立刻上手的答案。
1. 先搞清楚:YOLO26的yaml到底在管什么
很多人一上来就猛改yolo26.yaml,结果训练直接报错:“KeyError: 'backbone'” 或 “AssertionError: number of anchors mismatch”。其实根本原因在于——你还没理解这个文件在整个训练流程里的角色。
简单说,yolo26.yaml不是配置文件,它是一份模型蓝图。它不存数据、不存权重、不跑代码,它只做一件事:告诉YOLO框架——“请按这个结构,从头搭出一个神经网络”。
你可以把它想象成建筑施工图:
nc: 80是告诉工人“这栋楼要盖80层(80个类别)”scales:下面的n,s,m,l,x是五种标准户型(不同尺寸的模型)backbone:和head:两大部分,就是地基+主体结构+屋顶的详细钢筋排布图
所以,改yaml ≠ 调参,而是重新设计模型骨架。这也是为什么官方yaml里几乎不写中文注释——它默认使用者已经看过源码、理解Ultralytics的模块注册机制。
关键提醒:YOLO26(基于Ultralytics v8.4.2)的yaml解析逻辑非常严格。它要求:
- 所有层级必须用两个空格缩进(不是Tab,不是4个空格)
- 每个模块名后必须跟冒号
:,且冒号后必须有一个空格args:下的参数必须是合法Python字面量(数字、字符串、列表、字典),不能写函数调用或变量名
2. yaml文件结构拆解:从顶层到底层,逐行讲透
我们以镜像中预置的ultralytics/cfg/models/26/yolo26.yaml为例(路径:/root/workspace/ultralytics-8.4.2/ultralytics/cfg/models/26/),逐段解释每个字段的真实含义,以及你什么时候该动、什么时候千万别碰。
2.1 顶层元信息:控制全局行为
# Ultralytics YOLO , AGPL-3.0 license # YOLO26 model configuration for object detection nc: 80 # number of classes scales: # model compound scaling constants, i.e. 'model=yolo26n.yaml' will use yolo26n.yaml n: &n {depth_multiple: 0.33, width_multiple: 0.25} s: &s {depth_multiple: 0.33, width_multiple: 0.50} m: &m {depth_multiple: 0.67, width_multiple: 0.75} l: &l {depth_multiple: 1.00, width_multiple: 1.00} x: &x {depth_multiple: 1.00, width_multiple: 1.25}nc: 80:这是你数据集的类别总数。必须和你的data.yaml里nc值完全一致。如果你的数据集只有3类(猫、狗、鸟),这里必须改成nc: 3,否则训练会崩溃。scales:区块是YOLO26的“模型家族”定义。&n、&s等是YAML锚点(anchor),用于复用。depth_multiple控制网络深度(CSP块重复次数),width_multiple控制通道宽度(所有卷积核数量按比例缩放)。新手建议只改scales下的具体值,不要删或重命名n/s/m/l/x这些key,否则yolo26n.pt等预训练权重将无法加载。
2.2 backbone部分:地基怎么打,决定模型上限
backbone: # [from, repeats, module, args] - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 - [-1, 3, C2f, [128, True]] - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8 - [-1, 6, C2f, [256, True]] - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16 - [-1, 6, C2f, [512, True]] - [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32 - [-1, 3, C2f, [1024, True]] - [-1, 1, SPPF, [1024, 5]] # 9这一段定义了特征提取主干。每行是一个网络层,格式为[from, repeats, module, args]:
from: 表示输入来自哪一层(-1= 上一层输出,-2= 上上层,数字则指索引层)repeats: 当前模块重复次数(如C2f重复6次)module: 模块类名(必须是Ultralytics已注册的类,如Conv,C2f,SPPF)args: 初始化参数列表,顺序必须和类__init__方法一致
你能安全修改的地方:
- 增加新层:比如在
SPPF后面加一行[-1, 1, CBAM, [1024]](前提是CBAM类已正确注册) - 调整参数:把
C2f的通道数从1024改成768,即[768, True] - 修改重复次数:把
C2f的6改成4,减少计算量
绝对禁止的操作:
- 把
C2f写成c2f(大小写敏感) - 在
args里写1024, True, "relu"(多了第三个参数,C2f.__init__只接受两个) - 把
from的-1改成0却忘了前面只有9层(索引越界)
2.3 neck部分:特征怎么融合,影响小目标检测
neck: - [-1, 1, nn.Upsample, [None, 2, 'nearest']] - [[-1, 6], 1, Concat, [1]] # cat backbone P4 - [-1, 3, C2f, [512]] # 12 - [-1, 1, nn.Upsample, [None, 2, 'nearest']] - [[-1, 4], 1, Concat, [1]] # cat backbone P3 - [-1, 3, C2f, [256]] # 15 (P3/8-small) - [-1, 1, Conv, [256, 3, 2]] - [[-1, 12], 1, Concat, [1]] # cat head P4 - [-1, 3, C2f, [512]] # 18 (P4/16-medium) - [-1, 1, Conv, [512, 3, 2]] - [[-1, 9], 1, Concat, [1]] # cat head P5 - [-1, 3, C2f, [1024]] # 21 (P5/32-large)neck负责FPN/PANet式的特征融合。关键点:
Concat的args: [1]表示按channel维度拼接(dim=1),这是固定写法,不要改成[0]或删除Upsample的args: [None, 2, 'nearest']中2是上采样倍数,必须和对应backbone层的下采样倍数匹配(P3是8倍,所以上采样2×再2×才回到8倍)- 每个
C2f后的通道数(如256,512,1024)必须和它拼接的backbone层输出通道一致,否则Concat会报错
实用技巧:想加强小目标检测?把第15行C2f的通道从256提升到384,同时确保第6行Conv输出也是384(即[-1, 1, Conv, [384, 3, 2]]),这样P3特征更丰富。
2.4 head部分:最后一步,怎么输出预测框
head: - [-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1, 0]] # output layer - [[15, 18, 21], 1, Detect, [nc]] # Detect(P3, P4, P5)这才是真正的“检测头”。注意两点:
- 第一行
nn.Conv2d是最终的分类回归卷积层,na是anchor数量(默认3),nc是类别数,5是xywh+obj。这个公式na * (nc + 5)绝对不能手动算错。如果nc=3,结果是3*(3+5)=24,卷积输出通道必须是24。 - 第二行
Detect是Ultralytics的检测头类,[15, 18, 21]对应neck输出的三层特征图索引(P3/P4/P5)。这三个数字必须和neck最后一层的索引完全一致(上面neck最后一层是21,所以这里写21)。改了neck结构,这里必须同步更新。
3. 动手改一个实战案例:给YOLO26n加CBAM注意力
现在我们来做一个真实场景:你想在YOLO26n的C2f模块后插入CBAM(Convolutional Block Attention Module),提升特征表达能力。整个过程分三步,每步都附带避坑指南。
3.1 第一步:注册CBAM模块(代码层面)
先别急着改yaml,得让YOLO认识CBAM这个新模块。打开/root/workspace/ultralytics-8.4.2/ultralytics/nn/modules/__init__.py,在文件末尾添加:
from .cbam import CBAM __all__ += ['CBAM']然后创建/root/workspace/ultralytics-8.4.2/ultralytics/nn/modules/cbam.py,内容如下(精简可运行版):
import torch from torch import nn class CBAM(nn.Module): def __init__(self, channels, reduction=16): super().__init__() self.channel_att = ChannelAttention(channels, reduction) self.spatial_att = SpatialAttention() def forward(self, x): x = self.channel_att(x) * x x = self.spatial_att(x) * x return x class ChannelAttention(nn.Module): def __init__(self, channels, reduction=16): super().__init__() self.avg_pool = nn.AdaptiveAvgPool2d(1) self.max_pool = nn.AdaptiveMaxPool2d(1) self.fc = nn.Sequential( nn.Linear(channels, channels // reduction, bias=False), nn.ReLU(), nn.Linear(channels // reduction, channels, bias=False) ) self.sigmoid = nn.Sigmoid() def forward(self, x): avg_out = self.fc(self.avg_pool(x).view(x.size(0), -1)).view(x.size(0), x.size(1), 1, 1) max_out = self.fc(self.max_pool(x).view(x.size(0), -1)).view(x.size(0), x.size(1), 1, 1) out = avg_out + max_out return self.sigmoid(out) class SpatialAttention(nn.Module): def __init__(self): super().__init__() self.conv = nn.Conv2d(2, 1, 7, padding=3, bias=False) self.sigmoid = nn.Sigmoid() def forward(self, x): avg_out = torch.mean(x, dim=1, keepdim=True) max_out, _ = torch.max(x, dim=1, keepdim=True) x = torch.cat([avg_out, max_out], dim=1) x = self.conv(x) return self.sigmoid(x)避坑提示:Ultralytics要求所有自定义模块必须继承
nn.Module,且forward方法只能有一个输入x。不要加training参数,也不要返回多个值。
3.2 第二步:修改yaml,在合适位置插入CBAM
打开yolo26.yaml,找到backbone部分。我们选择在第一个C2f之后、第二个Conv之前插入CBAM(即增强P2特征)。原结构:
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 - [-1, 3, C2f, [128, True]] # 2 - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8改为:
- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 - [-1, 3, C2f, [128, True]] # 2 - [-1, 1, CBAM, [128]] # 3 ← 新增CBAM,输入通道=上一层C2f输出通道 - [-1, 1, Conv, [256, 3, 2]] # 4-P3/8 (原第3行,现变为第4行)关键检查:
- 新增行的
from: -1正确指向C2f输出 CBAM的args: [128]和C2f输出通道一致- 后续所有层的
from索引都要顺延:原[-1, 1, Conv, [256, 3, 2]]的from从-1变成-1(仍指上一层),但它的层索引从3变成4,所以neck里引用它的位置(如[[ -1, 4], ...])中的4也要同步改成5(因为neck第一行Upsample现在是第10行,而非原第9行)
3.3 第三步:验证与调试——如何快速发现yaml写错了
改完别急着训练,先用Ultralytics内置工具验证yaml语法:
cd /root/workspace/ultralytics-8.4.2 python -c "from ultralytics import YOLO; model = YOLO('ultralytics/cfg/models/26/yolo26.yaml'); print(' YAML解析成功,模型结构:'); print(model.model)"如果报错,常见原因及修复:
yaml.scanner.ScannerError:缩进错误 → 用VS Code打开,显示所有空格(Ctrl+Shift+P → “Toggle Render Whitespace”),确保全是2空格ModuleNotFoundError: No module named 'CBAM':模块未注册 → 检查__init__.py是否添加,文件路径是否正确AssertionError: number of anchors mismatch:head里Detect的输入层数和neck输出层数不匹配 → 数一下neck最后几行的索引,更新Detect的from列表
4. 训练前必做的三件事:避免白跑10小时
即使yaml完全正确,训练也可能失败。以下是我在镜像环境中反复验证过的启动清单:
4.1 检查数据集路径是否真实存在
data.yaml里写的路径,必须是镜像内绝对路径。例如:
train: ../datasets/coco128/train/images val: ../datasets/coco128/val/images但镜像中实际数据集可能放在/root/datasets/mydata/。这时你要:
- 用
ls /root/datasets/mydata/images/确认目录存在 - 把
data.yaml里的路径改成绝对路径:train: /root/datasets/mydata/images
4.2 确认权重文件路径与yaml版本匹配
你在train.py里写的:
model = YOLO(model='/root/workspace/ultralytics-8.4.2/ultralytics/cfg/models/26/yolo26.yaml') model.load('yolo26n.pt') # ← 这里必须是YOLO26n的权重!如果yolo26n.pt是YOLOv8的权重,会报错RuntimeError: size mismatch。镜像中预置的权重在根目录,用ls *.pt查看,确保加载的是yolo26*.pt系列。
4.3 设置合理的batch size,防止CUDA out of memory
镜像默认batch=128,但这是针对A100的配置。如果你用的是RTX 3090(24G显存),建议:
batch=64(双卡)或batch=32(单卡)- 同时把
workers=4(避免CPU成为瓶颈) imgsz=640可保持不变,YOLO26对分辨率不敏感
修改train.py中的参数即可,无需动yaml。
5. 总结:改yaml不是玄学,而是有章可循的工程实践
回看整个过程,你会发现修改YOLO26的yaml文件,核心就三点:
- 理解层级关系:
backbone→neck→head是数据流,from是连接线,args是模块开关 - 遵守注册规则:新加模块必须在Ultralytics的模块系统里“上户口”,否则yaml只是废文本
- 坚持验证闭环:改yaml → 注册模块 → 检查路径 → 小数据试跑 → 查看日志 → 调整参数
你不需要记住所有模块的参数细节,但一定要养成习惯:每次改完,先用python -c "from ultralytics import YOLO; YOLO('your.yaml')"验证能否成功构建模型。这10秒的检查,能帮你省下几小时的无效训练时间。
最后提醒:YOLO26的结构设计已有大量实验验证。盲目增加模块不一定提升精度,有时反而破坏特征分布。建议每次只改一个点,用同一数据集、同一超参对比mAP变化,让数据说话。
获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。