YOLO26实操记录(自用)
YOLO的早期发展
YOLOv1版本:在当时主流目标检测模型都使用两阶段的方法:滑动窗口生成候选再进行分类和回归,YOLOv1提出了一次前向传播输出所有检测结果,无候选框生成的单阶段目标检测方法,具体来说是将图像分成S×S的网格,然后每个网格预测2个预测框。
YOLOv2版本:YOLOv1 的训练过程存在梯度不稳定、收敛慢、对初始化敏感的问题,且容易出现过拟合,导致模型泛化能力受限--->YOLOv2 在所有卷积层后都添加了 BN 层,移除了原有的 Dropout,训练收敛速度提升3 倍以上,泛化能力提升较大。
YOLOv1直接回归预测框的效果较差--->YOLOv2 不再直接回归框的绝对坐标,而是回归预测框相对于 Anchor 的偏移量,大幅降低学习难度。Anchor 生成方式:不再手动设定,而是用k-means 聚类对训练集的真实框(GT)聚类,得到 5 个最优 Anchor。(注:训练集是带标签的)
YOLOv2为了让模型适应不同尺度的特征,通过每训练10 个批次,随机选择一个尺寸作为下一批次的输入,这种多尺度训练能让用户推理时可根据需求选择尺寸。
YOLOv3版本:YOLOv1/v2 仅用单一尺度特征图做预测--->YOLOv3使用FPN的上采样融合技术,输出 3 个尺度的特征图(步长分别为 32、16、8),对应不同大小的目标检测。
YOLOv2使用Darknet-19作为骨干网络,网络深度不足,语义特征提取能力弱--->YOLOv3设计了Darknet-53作为骨干网络,尽管速度略降,但是精度稳定提升,且支持了多标签分类,是真正实现了让YOLO能用的关键版本。

YOLO的中期发展
注:由于YOLOv4和YOLOv5发布时间非常将近,两者都采用 “SPP + PAN” 的特征融合组合,然而前者还是C语言,而后者是基于 PyTorch 框架,因此我们直接来看2020年6月发布的YOLOv5。
YOLOv5:相较于YOLOv3版本纯残差块堆叠的Darknet-53,YOLOv5升级为了CSPDarknet53,核心改动是CSP 残差块(CSPResidual Block):将输入拆分为两部分,一部分走残差分支,一部分直接短路,最后拼接融合,大大减少了计算量。
C3模块详细介绍:输入特征图(假设维度为 [B, C, H, W],B = 批次,C = 通道数,H/W = 高 / 宽)首先被按通道维度拆分为主分支和短路分支,各占一半的通道数,主分支会经过 n 个残差块(Bottleneck)后和短路分支进行特征融合(Concat),最终经过一个1*1卷积层输出。
YOLOv3 仅用 FPN 做 “自上而下” 融合,浅层位置信息无法传递到深层--->
YOLOv5的骨干网络输出3个尺度特征 → SPPF增强深层特征 → FPN(自上而下)融合 → PAN(自下而上)融合 → 预测头

SPPF通过多次池化叠加,形成多尺度感受野,可同时捕捉小、中、大目标的特征,流程如下:
- 输入特征图先经过一次 5×5 最大池化;
- 输出结果再经过一次 5×5 最大池化;
- 若需要,可再经过一次 5×5 最大池化;
- 将原始特征、第一次池化、第二次池化、第三次池化的结果按通道拼接(Concat);
- 最后通过 1×1 卷积整合通道数,输出增强后的特征。
除了网络结构的改善,YOLOv5还提供了以下的方法提升模型性能和使用效果:
引入 Mosaic、MixUp、CutMix 等增强策略,自动对图像进行拼接、裁剪、颜色扰动;
采用 CIoU / DIoU Loss等损失,同时优化边界框回归和置信度损失;
支持自动锚框聚类、自动学习率调整、混合精度训练(FP16)、多尺度训练;
通过深度 / 宽度系数衍生出 nano/s/m/l/x 等多个版本;(yaml文件可直接修改)
基于 PyTorch 框架,提供一键训练 / 推理 / 导出脚本,支持 ONNX/TensorRT/OpenVINO 等格式。
YOLOv8于2023年1月发布,依然是 Backbone → Neck → Head 三段式结构,但相较于YOLOv5版本有了精度速度双提升。
YOLOv8:
主干网络依旧是CSPDarknet,相较于YOLOv5版本的C3模块,YOLOv8采用了更先进的C2f模块,核心区别是C3 是 “两路融合”,C2f 是 “多路堆叠融合”,特征更密、梯度更顺。
C2f 模块详细介绍:输入特征图(假设维度为 [B, C, H, W])
首先通过 1×1 卷积将通道数调整为 C',随后按通道维度拆分为主分支和短路分支(各占 C'/2 通道数);主分支会依次经过 n 个串联的残差块(Bottleneck),且每个残差块的输出都会被保留,最终将「短路分支 + 原始主分支输入 + 所有残差块的输出」按通道维度拼接(Concat),再通过 1×1 卷积层整合通道数并输出,输出维度仍为 [B, C, H, W]。
C2f 的计算量略高于 C3,但特征表达能力提升远大于计算量增加,实现了精度的飞跃
Head部分也有了革命性的改进:从 Anchor-Based → Anchor-Free
YOLOv8的预测头是直接预测中心点和宽高,输出少、计算少、后处理极快
此外YOLOv8的预测头通过解耦实现任务互不干扰,可单独优化且收敛更快:
- 分类头:独立分支,只预测类别概率
- 回归头:独立分支,只预测框坐标
YOLOv5(Anchor-Based)的冗余计算,核心来自 Anchor 的 “预设 + 匹配 + 筛选” 全流程,
Anchor-Free直接预测目标的「中心点 + 宽高缩放」,无需预设 Anchor,输出维度简化为:[B, (4+1+NC), H, W](每个网格仅预测 1 组值),且无需 Anchor 匹配 / NMS 的大量筛选计算,实现了速度的飞跃
引入 DFL(Distribution Focal Loss):
- 对边界框坐标做分布建模(离散成 16 个 bin),用 softmax 输出概率分布,再加权求和得到连续坐标
- 优势:框回归更鲁棒、精度更高,尤其对小目标和密集场景
- 劣势:有一定的计算量,牺牲了速度
YOLOv8可以直接回归而YOLOV1不行的原因:
首先肯定是网络架构天差地别,其次是损失函数设计。
YOLOv1 用 MSE 损失直接回归坐标和置信度:
- 对宽高的 MSE 损失:大目标的宽高误差(如 ±10 像素)相对占比小,小目标的 ±10 像素误差是致命的,损失函数无法兼顾;
- 置信度损失:将 “是否包含目标” 和 “IoU” 混在一起,模型难以优化。
而 YOLOv8 用 DFL+CIoU Loss预测中心点偏移和宽高缩放:
- DFL:将坐标建模为概率分布,用 softmax 输出,对小目标的坐标误差更敏感;
- CIoU:同时考虑 IoU、长宽比、中心距离,回归更精准。
此外YOLOv8 采用 “预热(Warmup)+ 余弦衰减 + 自适应重启”:前 5 个 epoch 小学习率预热(避免梯度爆炸),中期余弦衰减(稳定收敛),后期小幅重启(跳出局部最优)—— 训练收敛速度提升 20%,最终精度更高。
YOLO的近期发展
YOLOV11于2024年9月发布,在 “精度、速度、轻量化、多任务适配” 上做了精准的工程化优化
YOLO2026于2026 年1 月 14 日,随 Ultralytics 框架 v8.4.0 版本在 GitHub 正式发布,模型权重与代码全面开放,针对边缘部署进行了优化,具有端到端无 NMS 推理功能。
相较于YOLOV8,这两个版本在网络上个主要改进在于两点:C3K2模块和C2PSA模块
C3K2模块
C3K2 是 C3 模块的优化版,核心是将 3×3 卷积替换为 2×2 卷积(K2),在降低计算量的同时保持特征提取能力;
官方代码 modules\block.py
C2f 模块(YOLOv8)- 第 288-320 行
class C2f(nn.Module):
def __init__(self, c1: int, c2: int, n: int = 1, shortcut: bool = False, g: int = 1, e: float = 0.5):
super().__init__()
self.c = int(c2 * e) # hidden channels
self.cv1 = Conv(c1, 2 * self.c, 1, 1)
self.cv2 = Conv((2 + n) * self.c, c2, 1) # optional act=FReLU(c2)
self.m = nn.ModuleList(Bottleneck(self.c, self.c, shortcut, g, k=((3, 3), (3, 3)), e=1.0) for _ in range(n))def forward(self, x: torch.Tensor) -> torch.Tensor:
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))C3k2 模块(YOLOv11)- 第 1069-1107 行
class C3k2(C2f):
def __init__(
self,
c1: int,
c2: int,
n: int = 1,
c3k: bool = False, # 新增参数
e: float = 0.5,
attn: bool = False, # 新增参数
g: int = 1,
shortcut: bool = True,
):
super().__init__(c1, c2, n, shortcut, g, e)
self.m = nn.ModuleList(
nn.Sequential(
Bottleneck(self.c, self.c, shortcut, g),
PSABlock(self.c, attn_ratio=0.5, num_heads=max(self.c // 64, 1)),
)
if attn
else C3k(self.c, self.c, 2, shortcut, g)
if c3k
else Bottleneck(self.c, self.c, shortcut, g)
for _ in range(n)
)C3k 模块 - 第 1109-1128 行
class C3k(C3):#可自定义卷积核k的大小
def __init__(self, c1: int, c2: int, n: int = 1, shortcut: bool = True, g: int = 1, e: float = 0.5, k: int = 3):
super().__init__(c1, c2, n, shortcut, g, e)
c_ = int(c2 * e)
self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, k=(k, k), e=1.0) for _ in range(n)))

注:C3K2的注意力机制是YOLO26版本加入的,因此YOLOv11官方版本默认是False,不使用
相当于对于YOLOV11的C3K2模块只有两种模式:
c3k=False (使用 Bottleneck)完全等价于使用YOLOv8的C2f模块
输入 → cv1 → [chunk成2份]
├─ 直接通过
└─ Bottleneck(3x3) → Bottleneck(3x3) → ...
→ concat → cv2 → 输出
- 特点:轻量、计算效率高
- 适用:浅层、需要快速处理的位置
c3k=True (使用 C3k,默认卷积核3×3大小)
输入 → cv1 → [chunk成2份]
├─ 直接通过
└─ C3k模块
├─ cv1分支 → Bottleneck(k) → Bottleneck(k) → ...
└─ cv2分支 → 直接通过
→ concat → cv2 → 输出
- 特点:更大的感受野、更强的特征提取
- 适用:深层、需要语义信息的位置
YOLOv26: 只有最后一层 P5/32-large 使用了 attn=True,因为加上 PSABlock 注意力机制 能捕捉全局语义信息,增强大目标检测
C2PSA模块
class C2PSA(nn.Module):
"""C2PSA: C2f + PSA (Position-Sensitive Attention)"""def __init__(self, c1: int, c2: int, n: int = 1, e: float = 0.5):
super().__init__()
assert c1 == c2
self.c = int(c1 * e) # 隐藏通道数
self.cv1 = Conv(c1, 2 * self.c, 1, 1) # 1x1卷积,通道翻倍
self.cv2 = Conv(2 * self.c, c1, 1) # 1x1卷积,恢复通道# 🔑 核心:堆叠 n 个 PSABlock
self.m = nn.Sequential(
*(PSABlock(self.c, attn_ratio=0.5, num_heads=self.c // 64) for _ in range(n))
)def forward(self, x):
a, b = self.cv1(x).split((self.c, self.c), dim=1) # 分成两路
b = self.m(b) # b 路经过 PSA 注意力
return self.cv2(torch.cat((a, b), 1)) # 拼接后输出class PSABlock(nn.Module):
"""Position-Sensitive Attention Block"""def __init__(self, c: int, attn_ratio: float = 0.5, num_heads: int = 4):
super().__init__()
# 🔑 多头自注意力
self.attn = Attention(c, attn_ratio=attn_ratio, num_heads=num_heads)
# 🔑 前馈网络
self.ffn = nn.Sequential(
Conv(c, c * 2, 1), # 扩展
Conv(c * 2, c, 1, act=False) # 压缩
)
self.add = shortcutdef forward(self, x):
x = x + self.attn(x) # 残差 + 注意力
x = x + self.ffn(x) # 残差 + FFN
return x


左边是PSABlock,右边是注意力机制
C2PSA = C2f 结构 + PSA 注意力机制
核心创新:
1. 双分支设计:一路保留原始特征(a分支),一路进行注意力增强(b分支)
2. PSA 堆叠:可以堆叠多个 PSABlock,逐步增强特征
3. 位置敏感:通过深度卷积保留空间位置信息
4. 高效平衡:只在最深层 P5 使用一次,避免过大的计算开销
作用:增强模型对全局上下文的理解,特别有助于大目标检测和复杂场景的理解。


两个模块都不改变输入输出的空间尺寸和通道数,它们的作用是增强特征表达:
- SPPF: 多尺度局部特征融合
- C2PSA: 全局上下文建模(注意力)
- YOLOv26 的改进: SPPF 加入残差连接
def __init__(self, c1: int, c2: int, k: int = 5, n: int = 3, shortcut: bool = False):
# c1: 输入通道
# c2: 输出通道
# k: 池化核大小(默认5)
# n: 池化迭代次数(默认3)
# shortcut: 是否用残差连接(默认False)
YOLO26的其他改进
移除了DFL模块
reg_max: 1 # 控制DFL (Distribution Focal Loss) 的分布区间数量,用于边界框回归。
取1则相当于移除了DFL,直接减少了计算量
无需NMS的端到端
nn\modules\head.py
end2end: True # whether to use end-to-end mode
| YOLO26以前的模式 | YOLO26 |
![]() | ![]() |
YOLOv26 通过 one2one 头实现一对一监督训练,让模型学会"每个位置只预测一个目标",从而推理时可以用 Top-K 直接输出,无需 NMS,实现真正的端到端目标检测。Top-K 就是从所有预测框中,选择置信度最高的 K 个框。


由于训练时一对一监督,模型自然地倾向于: 每个目标位置只有一个高置信度预测 不会有大量重复框 , 因此直接 Top-K 选择即可,无需 NMS 去重
改进了损失函数并使用了MuSGD优化器
class E2EDetectLoss:
def __init__(self, model):
# 🔑 核心:创建两个损失函数,使用不同的 tal_topk
self.one2many = v8DetectionLoss(model, tal_topk=10) # 多对一
self.one2one = v8DetectionLoss(model, tal_topk=1) # 一对一def __call__(self, preds, batch):
one2many = preds["one2many"]
loss_one2many = self.one2many(one2many, batch)one2one = preds["one2one"]
loss_one2one = self.one2one(one2one, batch)# 🔑 双损失相加
return loss_one2many[0] + loss_one2one[0], loss_one2many[1] + loss_one2one[1]


自动优化器切换
# trainer.py 中的逻辑
name, lr, momentum = ("MuSGD", 0.01, 0.9) if iterations > 10000 else ("AdamW", lr_fit, 0.9)
# 迭代次数 < 10000: 使用 AdamW (快速收敛)
# 迭代次数 > 10000: 切换到 MuSGD (稳定训练)
YOLO不同任务的头部网络
yolo26-cls.yaml
head:
- [-1, 1, Classify, [nc]] # Classify
-1表示输入来自上一层,即backbone的输出,可以使用YOLO的骨干网络,也可使用ResNet等
1表示重复次数为1次
Classify为网络模块
[nc]是Classify的参数,表示类别数量
class Classify(nn.Module):
"""YOLO classification head, i.e. x(b,c1,20,20) to x(b,c2).
This class implements a classification head that transforms feature maps into class predictions.
Attributes:
export (bool): Export mode flag.
conv (Conv): Convolutional layer for feature transformation.
pool (nn.AdaptiveAvgPool2d): Global average pooling layer.
drop (nn.Dropout): Dropout layer for regularization.
linear (nn.Linear): Linear layer for final classification.
Methods:
forward: Perform forward pass on input feature maps.
Examples:
Create a classification head
>>> classify = Classify(c1=1024, c2=1000)
>>> x = torch.randn(1, 1024, 20, 20)
>>> output = classify(x)
"""
export = False # export mode
def __init__(self, c1: int, c2: int, k: int = 1, s: int = 1, p: int | None = None, g: int = 1):
"""Initialize YOLO classification head to transform input tensor from (b,c1,20,20) to (b,c2) shape.
Args:
c1 (int): Number of input channels.
c2 (int): Number of output classes.
k (int): Kernel size.
s (int): Stride.
p (int, optional): Padding.
g (int): Groups.
"""
super().__init__()
c_ = 1280 # efficientnet_b0 size
self.conv = Conv(c1, c_, k, s, p, g)
self.pool = nn.AdaptiveAvgPool2d(1) # to x(b,c_,1,1)
self.drop = nn.Dropout(p=0.0, inplace=True)
self.linear = nn.Linear(c_, c2) # to x(b,c2)
def forward(self, x: list[torch.Tensor] | torch.Tensor) -> torch.Tensor | tuple:
"""Perform forward pass on input feature maps."""
if isinstance(x, list):
x = torch.cat(x, 1)
x = self.linear(self.drop(self.pool(self.conv(x)).flatten(1)))
if self.training:
return x
y = x.softmax(1) # get final output
return y if self.export else (y, x)
backbone 输出: (B, 1024, H, W)
↓
conv: (B, 1024, H, W) → (B, 1280, H, W)
↓
pool: (B, 1280, H, W) → (B, 1280, 1, 1)
↓
flatten: (B, 1280)
↓
linear: (B, 1280) → (B, nc)
↓
softmax: (B, nc) # 推理时返回概率
注:yolo11-cls-resnet18.yaml就是使用resnet18作为骨干网络训练分类网络
backbone: TorchVision [resnet18, DEFAULT, True, 2]
会加载torchvision.models.resnet18 并去掉最后的池化和全连接层
Input (3, H, W)
↓
conv1 + bn + relu + maxpool → 64 channels
↓
layer1 (x2) [64]
↓
layer2 (x2) [128]
↓
layer3 (x2) [256]
↓
layer4 (x2) [512]
↓
输出 (512, H', W') → 传给 Classify Head
yolo26.yaml
- [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)
与yolov11的一致,输入层索引:P3/8 (小目标)、P4/16 (中目标)、P5/32 (大目标)
区别是yolo26引入了end2end 和 reg_max
def forward(self, x):
# 1. one2many 分支
preds = self.forward_head(x, **self.one2many)# 2. one2one 分支 (end2end=True 时)
if self.end2end:
x_detach = [xi.detach() for xi in x]
one2one = self.forward_head(x_detach, **self.one2one)
preds = {"one2many": preds, "one2one": one2one}# 3. 训练返回 preds,推理进行后处理
if self.training:
return preds
y = self._inference(preds["one2one"] if self.end2end else preds)
if self.end2end:
y = self.postprocess(y.permute(0, 2, 1)) # top-k筛选
总结:两个版本都有one2many,实际上是对于一个目标,会产生多个检测框,计算这部分的损失函数的时候,包括分类分支的BCEloss和检测框部分的IOUloss,所有正负anchor计算BCE,只对正anchor计算IoU loss和DFL loss;而yolo26多了一个one2one的分支,训练过程两个分支的loss权重也不断调整,推理时也用的是one2one分支进行推理。
❯ 对于top10来说,相当于每个GT只有10个anchor是正anchor,其他都是负anchor,对所有正负anchor都会计算BCE损失,而只对10个正anchor计算IOU损失和DFL损失

yolo26-seg.yaml
- [[16, 19, 22], 1, Segment26, [nc, 32, 256]] # Segment26(P3, P4, P5)
区别于yolo26检测模型仅在于 Segment26
而详细来看,区别于yolo11版本的分割模型,最关键区别在于:
Proto模块不同 - 最关键差异
Segment (yolo11-seg) - head.py:289
self.proto = Proto(ch[0], self.npr, self.nm) # 只用P3特征
Segment26 (yolo26-seg) - head.py:391
self.proto = Proto26(ch, self.npr, self.nm, nc) # 用所有特征 + 类别数
- Proto: 只使用最小尺度特征图 ch[0] (P3/8)
- Proto26: 使用所有多尺度特征 ch (P3+P4+P5),并传入类别数 nc
因此Segment26使用更先进的多尺度原型生成网络,能生成更精细的掩码,同时支持端到端推理。

yolo26-obb.yaml
- [[16, 19, 22], 1, OBB, [nc, 1]] # OBB(P3, P4, P5)
class OBB(Detect): # 继承自Detect
"""YOLO OBB detection head for detection with rotation models."""
参数:
- nc: 类别数
- ne: 额外参数数量(角度通道数,默认=1)
- reg_max: DFL通道数(默认16)
- end2end: 是否端到端模式
- ch: 输入通道数元组


旋转框有5个自由度: [x, y, w, h, θ]
- ProbIoU损失:测量整体重叠度
- 角度损失:专门监督旋转角度
1. ProbIoU是综合指标,优化整体但可能在某些情况(如正方形)对角度不敏感
2. Angle Loss是专门监督,直接约束角度预测
3. 两者结合形成主从关系:ProbIoU主导整体优化,Angle Loss弥补角度梯度不足
补充



更多推荐




所有评论(0)