ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

YOLO11网络结构深度解析:从C3k2到C2PSA的核心模块与源码实现

YOLO11网络结构深度解析:从C3k2到C2PSA的核心模块与源码实现 打开yolo11n.yaml那一刻我其实有点懵。YOLOv8里熟悉的C2f和SPPF还在但Backbone里突然多了一排C3k2最深层还挂了个C2PSA连检测头的命名方式都变了。如果你也在做YOLO11迁移或者刚准备上手大概率会遇到同样的问题官方仓库能跑通但想改结构、换backbone、或者排查训练loss异常时对网络结构的理解就成硬门槛了。这篇文章不打算讲怎么调参跑分而是把YOLO11网络结构和ultralytics里的源码放在一起按数据从输入到输出的路径逐段拆开顺便把C3k2、C2PSA、DFL这些“新名字”的来龙去脉说清楚。适合已经跑通YOLOv8、正在切YOLO11的开发者也适合刚入门但不想只会用yolo train的同学。1. YOLO11在模型结构上的四个关键变化1.1 从模型规格先看它的设计取向YOLO11发布时提供了n、s、m、l、x五个尺寸跟YOLOv8的命名习惯一致。以n版本为例官方给出的参数量在2.6M左右计算量约6.5 GFLOPs输入640x640而YOLOv8n大约是3.2M参数、8.1 GFLOPs。这个缩水不是随意的它直接反映了YOLO11的结构调整思路在更少的参数下尽量保持甚至提升精度。几个版本对比如下模型参数量计算量(GFLOPs)主要用途YOLO11n2.6M6.5移动端/实时推理YOLO11s9.4M21.5边缘设备/轻量服务YOLO11m20.1M68.0通用检测YOLO11l25.3M86.9高精度场景YOLO11x56.9M194.9最高精度我自己的体会是YOLO11n在GPU上跑640输入时推理速度比YOLOv8n还要快一截这在做一些实时视频流检测项目时很重要。结构上它确实不是简单地把YOLOv8的C2f换成C3k2就完事而是在模块组合、检测头、损失策略上都有调整。1.2 四个不能忽略的结构调整点第一个变化是Backbone中的C3k2替代C2f。C3k2并不是从零发明的新模块而是基于C2f的一次改造。C2f是YOLOv8里“split之后过多个Bottleneck再concat”的结构C3k2保留了这套split-concat框架但内部Bottleneck的卷积核组合可以切换默认用3x33x3迭代参数c3k为True时则使用C3k模块。简单说它是C2f的“积木替换版”。第二个变化是C2PSA模块。Backbone最深层紧接着SPPF后多了一个带自注意力机制的模块全称是Cross Stage Partial with Position-Sensitive Attention。光看名字就知道是把CSP结构和自注意力结合。它的作用是让网络在处理深层特征时能建立远距离依赖不是只盯着局部区域。第三个变化是Detect检测头依然是解耦结构但回归分支的DFL参数和通道组织方式值得注意。YOLO11的Detect模块中分类分支输出nc个通道回归分支输出4 * reg_max个通道其中reg_max默认为16。也就是说每个预测框的四个坐标都用16个bin的分布来表示最后通过期望值解码成具体的bbox。YOLOv8里也用了同样的DFL思路但YOLO11在整体通道计算上做了压缩推理时输出维度更紧凑。第四个变化是anchor-free和标签分配策略的延续。YOLO11没有回到anchor-based路线依然是每个特征图位置直接预测一个目标配合TaskAlignedAssigner做正样本分配。这个延续性很重要意味着从YOLOv8切到YOLO11时很多数据处理和后处理逻辑是共用的。把四个变化放到一起看YOLO11的设计语言其实很统一保留CSP风格用轻量模块替换重模块在深层引入注意力用解耦头和DFL做检测。理解了这个大方向后面看yaml和源码就不会觉得东一块西一块了。2. 从yaml到nn.Module前向传播链路逐层拆解2.1 模型定义文件到底在描述什么ultralytics里YOLO11的模型结构不是直接写成一行行PyTorch代码而是定义在ultralytics/cfg/models/11/yolo11.yaml中再由DetectionModel去解析并构建。这种做法的好处是换结构时不用改源码只改yaml即可。以YOLO11n为例我整理了一份逻辑等价的yaml结构具体小版本可能在通道数上有细微差异以你本机yolo11n.yaml为准。它分backbone和head两个部分# Backbone backbone: - [-1, 1, Conv, [64, 3, 2]] # 0-P1/2 - [-1, 1, Conv, [128, 3, 2]] # 1-P2/4 - [-1, 2, C3k2, [256, False, 0.25]] # 2 - [-1, 1, Conv, [256, 3, 2]] # 3-P3/8 - [-1, 2, C3k2, [512, False, 0.25]] # 4 - [-1, 1, Conv, [512, 3, 2]] # 5-P4/16 - [-1, 2, C3k2, [1024, True, 0.25]] # 6 - [-1, 1, SPPF, [1024, 5]] # 7 - [-1, 2, C2PSA, [1024]] # 8每一行的格式是[from, repeats, module, args]from表示输入来自哪一层-1代表上一层输出repeats表示模块重复次数module是模块名args是模块参数。比如[-1, 2, C3k2, [256, False, 0.25]]的意思就是输入来自上一层用C3k2模块重复2次输出通道数为256c3kFalse通道扩展比例e0.25。Head部分就是FPNPAN的融合路径# Head head: - [-1, 1, nn.Upsample, [None, 2, nearest]] # 9 - [[-1, 6], 1, Concat, [1]] # 10 - [-1, 2, C3k2, [512, False, 0.25]] # 11 - [-1, 1, nn.Upsample, [None, 2, nearest]] # 12 - [[-1, 4], 1, Concat, [1]] # 13 - [-1, 2, C3k2, [256, False, 0.25]] # 14 - [-1, 1, Conv, [256, 3, 2]] # 15 - [[-1, 11], 1, Concat, [1]] # 16 - [-1, 2, C3k2, [512, False, 0.25]] # 17 - [-1, 1, Conv, [512, 3, 2]] # 18 - [[-1, 8], 1, Concat, [1]] # 19 - [-1, 2, C3k2, [1024, True, 0.25]] # 20 - [[14, 17, 20], 1, Detect, [nc]] # Detect(P3, P4, P5)这里Concat的from字段是列表例如[[-1, 6], 1, Concat, [1]]表示把上一层输出和第6层输出做拼接[1]表示沿通道维度拼接。2.2 parse_model如何把yaml变成PyTorch网络DetectionModel的parse_model函数是核心中的核心。它会遍历yaml每一行根据module字符串到类型映射表里找对应的Python类。比如Conv、C3k2、SPPF、C2PSA都会在ultralytics/nn/modules/block.py和conv.py中查表。构建时最关键的一步是ch这个列表的维护。ch保存了每一层输出的通道数初始是输入图像的通道数3。每解析一层就根据参数算出该层输出通道数并追加到ch列表末尾。当遇到Concat时会根据from列表取出所有输入层的通道数求和作为当前层的输入通道。我实际调试时经常用torchinfo来核对网络结构import torch from ultralytics import YOLO from torchinfo import summary model YOLO(yolo11n.yaml).model summary(model, input_size(1, 3, 640, 640), devicecpu, depth5)这样能看到每一层的输出形状和参数量比单纯看yaml直观得多。YOLO11在640输入下三个检测头输出特征图尺寸分别是80x80、40x40、20x20总共对应8400个预测框。这个数字是从80*80 40*40 20*20算出来的。2.3 特征图尺寸流动的完整时间线以640x640输入为例我列一下主干路径中的特征图变化层号模块输入尺寸输出尺寸0Conv k3 s2640x640x3320x320x641Conv k3 s2320x320x64160x160x1282C3k2 x2160x160x128160x160x2563Conv k3 s2160x160x25680x80x2564C3k2 x280x80x25680x80x5125Conv k3 s280x80x51240x40x5126C3k2 x240x40x51240x40x10247SPPF k540x40x102440x40x10248C2PSA x240x40x102440x40x10249Upsample x240x40x102480x80x102410Concat(9,4)-80x80x153611C3k2 x280x80x153680x80x51212Upsample x280x80x512160x160x51213Concat(12,2)-160x160x76814C3k2 x2160x160x768160x160x25615Conv k3 s2160x160x25680x80x25616Concat(15,11)-80x80x76817C3k2 x280x80x76880x80x51218Conv k3 s280x80x51240x40x51219Concat(18,8)-40x40x153620C3k2 x240x40x153640x40x1024注意第14层输出是160x160x256但它在最后只作为P3的一个中间特征传给检测头。实际上YOLO11在Neck部分有一个很巧妙的设计FPN上采样到160x160做了一次浅层特征融合再通过PAN下采样回到80x80和40x40这样小目标信息能通过这条路径保留下来。因为第2层C3k2的输出是160x160x256stride 4我最初以为这里直接进Detect直到看到Detect接收的是[14, 17, 20]才反应过来。YOLO11并不是简单复用了v8的P3/P4/P5划分而是多走了一条浅层融合分支让P3层80x80同时带着来自160x160浅层特征的信息。这对小目标检测有明显帮助代价是多了一些计算量但YOLO11通过压缩通道把它抵消了。3. 三个核心算子源码解读C3k2、C2PSA、SPPF3.1 C3k2C2f的“积木替换版”C3k2的源码并不复杂它直接继承自C2fclass C3k2(C2f): def __init__(self, c1, c2, n1, c3kFalse, e0.5, g1, b1, shortcutTrue): super().__init__(c1, c2, n, shortcut, g, e) c_ int(c2 * e) # 根据c3k参数决定内部积木类型 self.m nn.Sequential( *(C3k(c1, c_, n, shortcut, g, b) for _ in range(n)) if c3k else *(Bottleneck(c1, c_, shortcut, g, k((3, 3), (3, 3)), e1.0) for _ in range(n)) )C2f父类的核心逻辑是这样的class C2f(nn.Module): def __init__(self, c1, c2, n1, shortcutFalse, g1, e0.5): super().__init__() self.c int(c2 * e) self.cv1 Conv(c1, 2 * self.c, 1, 1) self.cv2 Conv((2 n) * self.c, c2, 1) self.m nn.ModuleList( Bottleneck(self.c, self.c, shortcut, g, k((3, 3), (3, 3)), e1.0) for _ in range(n) ) def forward(self, x): y list(self.cv1(x).chunk(2, 1)) y.extend(m(y[-1]) for m in self.m) return self.cv2(torch.cat(y, 1))前向流程可以这样理解输入先过一个1x1卷积把通道数变成2 * c然后沿通道分成两份一份作为shortcut支路直接往后传另一份依次经过n个Bottleneck每一级的输出都跟之前的内容拼接在一起最后再通过1x1卷积压缩到目标通道。这种“两条支路加多条中间路径”的设计相当于一条信息高速公路梯度能更顺畅地流到浅层。C3k2比C2f更灵活的地方就在于c3k参数。当c3kFalse时内部用的是两个3x3卷积的Bottleneck当c3kTrue时内部换成C3k模块而C3k本质上是一个CSP结构内部Bottleneck的卷积核组合是1x1和3x3搭配。为什么yolo11n.yaml中只有深层第6层、第20层设置了True因为深层特征图分辨率低使用1x13x3的C3k可以在不显著增加计算量的情况下扩大感受野而浅层用两个3x3会带来更多计算负担。3.2 C2PSA把自注意力塞进CSP框架C2PSA是我认为YOLO11结构中最值得细看的模块。它的完整名字是Cross Stage Partial with Position-Sensitive Attention。先看整体结构class C2PSA(nn.Module): def __init__(self, c1, c2, n1, e0.25): super().__init__() assert c1 c2 self.c int(c1 * e) self.cv1 Conv(c1, 2 * self.c, 1, 1) self.cv2 Conv(2 * self.c, c1, 1) self.m nn.Sequential(*(PSA(self.c, self.c, n) for _ in range(n))) def forward(self, x): a, b self.cv1(x).split((self.c, self.c), dim1) b self.m(b) return self.cv2(torch.cat((a, b), 1))看到没它和C2f的骨架非常像都是1x1卷积、split、主路处理、concat、再1x1卷积。区别在于C2f主路用的是普通Bottleneck而C2PSA主路用的是PSA模块。PSA内部才是真正的注意力部分class PSA(nn.Module): def __init__(self, c1, c2None, e0.5): super().__init__() assert c1 c2 self.c int(c1 * e) self.cv1 Conv(c1, 2 * self.c, 1, 1) self.cv2 Conv(2 * self.c, c1, 1) self.attn Attention(self.c, num_heads8) self.ffn nn.Sequential( Conv(self.c, self.c * 2, 1), Conv(self.c * 2, self.c, 1, actFalse) ) def forward(self, x): a, b self.cv1(x).split((self.c, self.c), dim1) b self.attn(b) b self.ffn(b) return self.cv2(torch.cat((a, b), 1))这里Attention就是多头自注意力的轻量实现class Attention(nn.Module): def __init__(self, dim, num_heads8, biasFalse): super().__init__() self.num_heads num_heads self.temperature nn.Parameter(torch.ones(num_heads, 1, 1)) self.qkv Conv(dim, dim * 3, kernel_size1, biasbias) self.proj Conv(dim, dim, kernel_size1, biasbias) def forward(self, x): qkv self.qkv(x) q, k, v qkv.chunk(3, dim1) # 将通道维度拆成多头计算注意力分数 # 缩放点积注意力 temperature缩放 return self.proj(attn_output)为什么C2PSA能提升效果我打个比方普通卷积像是只看自己周围几个邻居3x3卷积只看一圈自注意力则是把整张特征图都扫一遍找到“和当前位置最相关的远处位置”。在目标检测里人判断一个物体是不是“船”可能要看周围有没有“水”判断一个模糊的小目标是什么可能要看它和大物体的上下文关系——这些都需要长距离依赖普通卷积做不到。但自注意力计算量很大YOLO11只把它放在40x40分辨率的深层因为这层每个位置的感受野已经足够大且40x40只有1600个位置自注意力的复杂度是O(n²)1600的平方也只有256万完全能接受。如果放在80x80层那是6400个位置计算量直接翻16倍这就划不来了。3.3 SPPF三个池化串联的“感受野放大器”SPPF全称Spatial Pyramid Pooling - Fast它和SPP的主要区别是把不同核尺寸的池化串联起来。class SPPF(nn.Module): def __init__(self, c1, c2, k5): super().__init__() c_ c1 // 2 self.cv1 Conv(c1, c_, 1, 1) self.cv2 Conv(c_ * 4, c2, 1, 1) self.m nn.MaxPool2d(kernel_sizek, stride1, paddingk // 2) def forward(self, x): x self.cv1(x) y1 self.m(x) y2 self.m(y1) y3 self.m(y2) return self.cv2(torch.cat((x, y1, y2, y3), 1))一个5x5的MaxPool再接一个5x5的MaxPool等效于一个9x9的池化感受野再接一次就等效于13x13。这样只用了三个5x5池化就实现了多尺度感受野的融合比SPP直接放三个不同核的池化层省下大量参数。在YOLO11里SPPF位于C2PSA之前它的作用是把高层语义信息多尺度地汇聚一次然后交给C2PSA做注意力建模。顺序上YOLO11是SPPF后接C2PSA而不是反过来这个细节也值得注意先做多尺度池化可以让注意力模块输入的信息更丰富。4. Detect检测头与损失函数的计算细节4.1 解耦头和DFL通道设计YOLO11的Detect模块在ultralytics/nn/modules/head.py中定义。它延续了YOLOv8的解耦头设计分类和回归各走一条分支class Detect(nn.Module): def __init__(self, nc80, ch()): super().__init__() self.nc nc # 类别数 self.reg_max 16 # DFL分布bin数量 self.no nc self.reg_max * 4 # 每个anchor的输出通道数 self.stride torch.zeros(self.nl) c2, c3 max((16, ch[0] // 4, self.reg_max * 4)), max(ch[0], self.nc) self.cv2 nn.ModuleList( nn.Sequential(Conv(x, c2, 3), Conv(c2, c2, 3), nn.Conv2d(c2, 4 * self.reg_max, 1)) for x in ch ) self.cv3 nn.ModuleList( nn.Sequential(Conv(x, c3, 3), Conv(c3, c3, 3), nn.Conv2d(c3, self.nc, 1)) for x in ch ) self.dfl DFL(self.reg_max)每个检测层都有两个分支cv3是分类分支输出nc个通道cv2是回归分支输出4 * reg_max个通道。如果nc80那每个位置的输出总通道数是4*1680144三个尺度加起来就是(80*80 40*40 20*20) * 144 8400 * 144。回归分支为什么要用16个bin来编码一个坐标这是Distribution Focal Loss的思路。传统的bbox回归直接预测一个数值比如框的左边距是12.3像素DFL则是预测一个长度为16的概率分布每个bin代表一个离散的偏移量最终通过Softmax加权求和得到预测值。这样做的好处是回归目标不再是一个点而是一个分布模型可以表达“不确定度”信息收敛更稳。解码过程大致如下class DFL(nn.Module): def __init__(self, c116): super().__init__() self.conv nn.Conv2d(c1, 1, 1, biasFalse).requires_grad_(False) x torch.arange(c1, dtypetorch.float) self.conv.weight.data[:] nn.Parameter(x.view(1, c1, 1, 1)) def forward(self, x): b, c, a x.shape return self.conv(x.view(b, 4, self.c1, a).transpose(2, 1).softmax(1)).view(b, 4, a)这个实现非常巧妙把16个bin的通道做Softmax然后用一个权值固定的1x1卷积对bin索引做加权求和等价于计算分布的期望。如果某个bin的概率最大那预测值就接近这个bin对应的偏移量。4.2 正样本分配TaskAlignedAssigner怎么挑正样本YOLO11使用的还是TaskAlignedAssigner核心思想是“分类分数高且和GT框重叠度高的anchor才是正样本”而不是单独看IoU或单独看分类分数。对于每个GT框先计算它与所有anchor的IoU以及anchor处预测的分类分数然后用alignment_metric cls_score^α * iou^β作为对齐度。α和β一般取0.5和6.0这样能让分类和定位相互促进。然后每个GT框选topk个对齐度最高的anchor作为候选正样本。选完之后还要做一步去重如果某个anchor被多个GT框选中就把它分配给对齐度最高的那个GT。剩下的anchor就是负样本分类目标直接是0。这套分配策略的好处是网络在训练初期会倾向于学习“分类置信度高的位置”而不是单纯追求IoU这对边缘模糊的目标比较友好。4.3 损失函数BCE CIoU DFLYOLO11的检测损失主要包括三部分损失项计算方法作用分类损失BCEWithLogitsLoss让分类分支学会区分目标和背景回归损失CIoU Loss让回归分支输出的bbox更贴近GT框DFL损失Distribution Focal Loss让16个bin的分布更集中、更准确总损失可以理解为loss cls_loss box_loss(CIoU) dfl_loss(DFL)BCE分类损失不用多说。CIoU在IoU基础上增加了中心点距离和宽高比惩罚能解决预测框和GT框中心不重合的问题。DFL损失则是让分布集中在真实偏移量附近避免概率分布过于分散。这三部分加起来YOLO11在训练初期loss一般在10-20随着训练降到3以下具体数值取决于数据集难度。5. 跑通YOLO11训练与推理的实操流程及避坑记录5.1 环境准备和第一个Demo安装ultralytics很简单pip install ultralytics安装后验证是否成功yolo predict modelyolo11n.pt sourcehttps://ultralytics.com/images/bus.jpg如果能在终端看到检测结果并保存图片说明环境没问题。这里要注意Python版本建议3.9到3.12之间PyTorch版本建议2.0以上配合CUDA 11.8或12.1使用CPU也能跑但速度会慢很多。我第一次跑的时候遇到过一个很隐晦的问题numpy版本太高2.x导致部分兼容性问题后来固定numpy为1.26.4就好了。遇到类似问题不要急着重装环境先看报错栈是不是在数据加载阶段。5.2 自定义数据集的格式与配置YOLO11训练自己的数据集目录结构长这样dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml标签文件是txt格式文件名和图片名保持一致每行内容为class_id x_center y_center width height注意坐标是归一化到0-1的x_center和y_center是框中心点相对于图片宽高的比例width和height是框宽高相对于图片宽高的比例。这个很容易写错我见过有人把像素坐标直接写进去训练loss直接爆炸。data.yaml内容train: dataset/images/train val: dataset/images/val nc: 2 names: [cat, dog]nc和names的顺序要严格对应标签文件里的class_id是按names列表索引的。如果names写错模型就会把猫认成狗这类问题排查起来真的很花时间。5.3 训练命令和关键参数yolo train modelyolo11n.pt datadata.yaml epochs100 imgsz640 batch16 device0几个常用参数modelyolo11n.pt基于预训练权重继续训练收敛更快。如果想从零训练改成modelyolo11n.yaml。imgsz640训练分辨率默认是640。显存不够可以降到416或512但会影响最终精度。batch16显存不够就降低batch或者用batch-1让ultralytics自动探测最大batch。device0指定GPU编号CPU训练就写devicecpu。epochs100通常小数据集100轮已经足够。训练过程中还可以加上patience20做早停验证集loss连续20轮不下降就停止。5.4 我踩过的几个坑第一个坑是中文路径。数据集路径和图片路径如果包含中文ultralytics在读取时可能会出问题报错往往很莫名其妙。解决方法是把数据集放到纯英文路径下。第二个坑是标签类别越界。如果标签txt里的class_id大于data.yaml中的nc-1训练时会直接报错或者强行截断。建议训练前写个小脚本扫描所有标签文件检查最大类别id。第三个坑是预训练权重和数据集不匹配。如果你用yolo11n.pt在只有2类的自定义数据集上训练最后一层的输出通道数会自动调整不要担心“类别数不同不能用”。但如果出现形状不匹配的报错可以先跑一遍yolo train modelyolo11n.yaml datadata.yaml确认数据集配置没问题再换回预训练权重。第四个坑是训练过程中loss变成NaN。多数情况下是学习率太大或batch size太大导致梯度爆炸。可以先降低到lr00.001试一下。另一个原因是开启AMP混合精度时某些GPU驱动版本不稳定关掉ampFalse再跑。训练结束后推理yolo predict modelruns/detect/train/weights/best.pt sourcetest_images/输出会保存到runs/detect/predict目录下每张图带着置信度和类别标签。6. 我拆解YOLO11结构时用的几个调试方法6.1 用torchinfo和hook观察每一层网络结构光看yaml不够直观我通常配合torchinfo和hook来观察import torch from ultralytics import YOLO from torchinfo import summary model YOLO(yolo11n.yaml).model print(model) summary(model, input_size(1, 3, 640, 640), depth3, devicecpu) activations {} def hook_fn(name): def hook(module, input, output): activations[name] output.shape return hook for name, module in model.named_modules(): if name in [model.2.m, model.8.m, model.20]: module.register_forward_hook(hook_fn(name)) model(torch.zeros(1, 3, 640, 640)) for k, v in activations.items(): print(k, v)这样能非常清楚地看到C3k2和C2PSA的输出尺度变化。我建议你注册hook时重点关注第8层C2PSA的输入输出因为它内部有split和concatshape的变化能帮你验证对模块结构的理解是否正确。6.2 用Netron看ONNX导出图跑通训练后把模型导出为ONNXyolo export modelyolo11n.pt formatonnx然后用Netron打开生成的yolo11n.onnx可以看到每个算子的数据流向。Netron上看YOLO11最直观的一点是你能清楚看到SPPF的三次MaxPool串行结构以及C2PSA内部的QKV分支。对于想深入修改网络结构的人来说这一步几乎是必修课。6.3 小数据集先验证再全量训练我想再三强调一个习惯任何结构改动先用一个只有几百张图片的小数据集跑10-20个epoch验证而不是直接上全量数据。原因很简单结构改动导致loss不收敛时小数据集能让你快速定位是数据问题还是模型问题。我之前改C2PSA中Attention的头数时训练loss一直不降后来在小数据集上加了调试输出才定位到是维度permute写错了。另外如果你改动了yaml中的结构一定要跑通一次model.named_parameters()检查参数是否按预期更新否则可能某个模块根本没有被forward调用参数一直是初始值却没人发现。6.4 注意力模块的效果怎么看C2PSA到底有没有发挥作用可以做一个简单的消融实验把yaml中的C2PSA替换成普通C2f在相同小数据集上训练同样epoch对比mAP。如果加了注意力的版本没有明显提升那就要检查数据集是否真的需要长距离依赖。在目标密集、尺度变化大的场景里C2PSA的提升通常比在简单场景里明显。我还习惯在推理时把C2PSA的输出特征图保存下来可视化看注意力到底关注了哪里。如果特征响应集中在目标边缘而不是目标内部说明Sigmoid或者Attention的归一化可能有问题。写在最后YOLO11看起来名字比YOLOv8只是加了个“1”但结构上其实藏了不少值得细看的设计。C3k2告诉我们轻量化不一定非得发明新结构在成熟模块上换积木也能出效果C2PSA则展示了怎么把自注意力这种“重武器”巧妙地嵌到深层特征中DFL的解码方式更是把“分布即信息”这个思想用得很彻底。我实际用下来的感受是YOLO11在小目标检测和边缘设备推理这两条赛道上比YOLOv8更顺手但它的很多收益来自结构细节而非某一个单独模块。想基于YOLO11做改进的同学建议先从C3k2的通道比例e、C2PSA的num_heads和是否叠加这两处入手效果最直接。而想彻底吃透它的人不妨按我上面的方式把源码读一遍再用小数据集做几组消融你收获的不只是一个模型的用法而是对目标检测网络设计一整条思路的理解。
返回列表