
💡💡💡PolyMLP结合C2PSA能有效提升涨点效果:

💡💡💡本文改进:PolyMLP块结合YOLO26的C2PSA模块,全网首发创新。

博主简介

AI小怪兽 | 计算机视觉布道者 | 视觉检测领域创新者
深耕计算机视觉与深度学习领域,专注于目标检测前沿技术的探索与突破。长期致力于YOLO系列算法的结构性创新、性能极限优化与工业级落地实践,旨在打通从学术研究到产业应用的最后一公里。
🚀 核心专长与技术创新
🏆 行业影响力与商业实践
💡 未来方向与使命
秉持 “让每一行代码都有温度” 的技术理念,未来将持续聚焦于实时检测、语义分割及工业缺陷检测的商业化闭环等核心方向。愿与业界同仁协同创新,共同推动技术边界,以坚实的技术能力赋能实体经济与行业变革。

论文:https://arxiv.org/pdf/2509.25164
摘要:本研究对Ultralytics YOLO26进行了全面分析,重点阐述了其关键架构改进及其在实时边缘目标检测中的性能基准测试。YOLO26于2025年9月发布,是YOLO系列最新、最先进的成员,专为在边缘及低功耗设备上实现高效、精确且易于部署的目标而构建。本文依次详述了YOLO26的架构创新,包括:移除了分布焦点损失(DFL);采用端到端的无NMS推理;集成了渐进损失(ProgLoss)与小目标感知标签分配(STAL);以及引入了用于稳定收敛的MuSGD优化器。除架构外,本研究将YOLO26定位为多任务框架,支持目标检测、实例分割、姿态/关键点估计、定向检测及分类。我们在NVIDIA Jetson Nano与Orin等边缘设备上呈现了YOLO26的性能基准测试,并将其结果与YOLOv8、YOLOv11、YOLOv12、YOLOv13及基于Transformer的检测器进行比较。本文进一步探讨了其实时部署路径、灵活的导出选项(ONNX、TensorRT、CoreML、TFLite)以及INT8/FP16量化技术。文章重点展示了YOLO26在机器人、制造业及物联网等领域的实际应用案例,以证明其跨行业适应性。最后,讨论了关于部署效率及更广泛影响的见解,并展望了YOLO26及YOLO系列的未来发展方向。
关键词:YOLO26;边缘人工智能;多任务目标检测;无NMS推理;小目标识别;YOLO(You Only Look Once);目标检测;MuSGD优化器

结构框图如下:


1)池化次数灵活性:YOLO11 的 3 次池化是硬编码的,要修改必须改源码;YOLO26 通过n参数可灵活调整(比如设为 2 次或 4 次),无需改核心逻辑。
2)Shortcut 设计:YOLO26 新增的残差连接能缓解深层网络的梯度消失问题,提升特征复用能力,而 YOLO11 无此设计。
3)激活函数控制:YOLO26 禁用 Conv1 的激活函数,让特征在池化前保持更 “原始” 的状态,是工程上对特征提取的优化。

源码位置:ultralytics/nn/modules/block.py
1)注意力机制的新增:YOLO26 的 C3k2 首次引入PSABlock(金字塔注意力模块),通过attn参数控制是否启用,这是两者最核心的功能差异 —— 启用后模块会先通过 Bottleneck 提取基础特征,再通过 PSABlock 增强关键区域的特征权重,提升小目标 / 复杂场景的检测效果。
2)分支逻辑的扩展:YOLO11 的分支仅受c3k控制,而 YOLO26 的分支逻辑优先级为attn > c3k,即只要attn=True,会优先启用注意力模块,忽略c3k的配置。

重复模块m (n次迭代):
┌─────────────────────────────────────────────────────────┐
│ │
│ 如果 attn=True: │
│ Sequential( │
│ Bottleneck(self.c, self.c), │ ←─ 先特征提取
│ PSABlock(self.c, attn_ratio=0.5, num_heads=...) │ ←─ 后注意力增强
│ ) │
│ │
│ 否则如果 c3k=True: │
│ C3k(self.c, self.c, 2) │ ←─ 同YOLOv11
│ │
│ 否则: │
│ Bottleneck(self.c, self.c) │ ←─ 同YOLOv11
│ │
└─────────────────────────────────────────────────────────┘YOLO26 C3k2代码:
源码位置:ultralytics/nn/modules/block.py
YOLO26引入了多项关键架构创新,使其区别于前几代YOLO模型。这些增强不仅提高了训练稳定性和推理效率,还从根本上重塑了实时边缘设备的部署流程。本节将详细描述YOLO26的四项主要贡献:(i)移除分布焦点损失(DFL),(ii)引入端到端无NMS推理,(iii)新颖的损失函数策略,包括渐进损失平衡(ProgLoss)和小目标感知标签分配(STAL),以及(iv)开发用于稳定高效收敛的MuSGD优化器。我们将详细讨论每一项架构增强,并通过对比分析突显其相对于YOLOv8、YOLOv11、YOLOv12和YOLOv13等早期YOLO版本的优势。

YOLO26最重要的架构简化之一是移除了分布焦点损失(DFL)模块(图3a),该模块曾存在于YOLOv8和YOLOv11等早期YOLO版本中。DFL最初旨在通过预测边界框坐标的概率分布来改进边界框回归,从而实现更精确的目标定位。虽然该策略在早期模型中展示了精度提升,但也带来了不小的计算开销和导出困难。在实践中,DFL在推理和模型导出期间需要专门处理,这使针对ONNX、CoreML、TensorRT或TFLite等硬件加速器的部署流程变得复杂。
源码位置:ultralytics/utils/loss.py
通过reg_max 设置为1,移除了分布焦点损失(DFL)
class BboxLoss(nn.Module):
"""Criterion class for computing training losses for bounding boxes."""
def __init__(self, reg_max: int = 16):
"""Initialize the BboxLoss module with regularization maximum and DFL settings."""
super().__init__()
self.dfl_loss = DFLoss(reg_max) if reg_max > 1 else NoneYOLO26从根本上重新设计了预测头,以直接产生非冗余的边界框预测,无需NMS。这种端到端设计不仅降低了推理复杂度,还消除了对手动调优阈值的依赖,从而简化了集成到生产系统的过程。对比基准测试表明,YOLO26实现了比YOLOv11和YOLOv12更快的推理速度,其中nano模型在CPU上的推理时间减少了高达43%。这使得YOLO26对于移动设备、无人机和嵌入式机器人平台特别有利,在这些平台上,毫秒级的延迟可能产生重大的操作影响。
源码位置:ultralytics/utils/nms.py
训练稳定性和小目标识别仍然是目标检测中持续存在的挑战。YOLO26通过整合两种新颖策略来解决这些问题:渐进损失平衡(ProgLoss)和小目标感知标签分配(STAL),如图(图3c)所示。
ProgLoss在训练期间动态调整不同损失分量的权重,确保模型不会过拟合于主导物体类别,同时防止在稀有或小类别上表现不佳。这种渐进式再平衡改善了泛化能力,并防止了训练后期的不稳定。另一方面,STAL明确优先为小目标分配标签,由于像素表示有限且易被遮挡,小目标尤其难以检测。ProgLoss和STAL共同为YOLO26在包含小目标或被遮挡目标的数据集(如COCO和无人机图像基准)上带来了显著的精度提升。
YOLO26的最后一项创新是引入了MuSGD优化器(图3d),它结合了随机梯度下降(SGD)的优势与最近提出的Muon优化器(一种受大型语言模型训练中使用的优化策略启发而发展的技术)。MuSGD利用SGD的鲁棒性和泛化能力,同时融入了来自Muon的自适应特性,能够在不同数据集上实现更快的收敛和更稳定的优化。
源码位置:ultralytics/optim/muon.py

论文:https://arxiv.org/pdf/2605.20839
摘要:现代视觉骨干网络通常将逐点激活(如ReLU、GELU)和指数softmax视为非线性的必要来源,但我们证明,在类MetaFormer的视觉骨干网络中,它们并非必需。我们为三种核心原语(MLP、卷积和注意力)设计了无需激活的多项式替代方案,其中哈达玛积取代了标准非线性,从而产生输入的多项式函数。这些模块可无缝集成到现有架构中:在MetaFormer(一个用于视觉骨干网络的模块化框架)内部实例化后,我们的PolyNeXt模型在ImageNet分类、ADE20K语义分割和分布外鲁棒性方面,在多种模型尺度上均达到或超越了基于激活的对应模型。同时,我们以更低的计算成本大幅超越了先前的多项式网络,表明标准模块的多项式变体优于复杂的定制架构。
3. 多项式模块
我们提出了用于通道混合、卷积空间混合和基于注意力的空间混合的无激活替代方案。图2总结了这三种原语;逐层比较见附录图S4。

3.1. PolyMLP:无激活通道混合
标准前馈网络在线性投影之间应用逐点激活,而门控变体添加了乘法交互,但仍在一个分支上保留激活。PolyMLP完全移除了激活: PolyMLP(x) = Wₒ((Wₐx) * (W_bx)), (2) 其中 Wₐ, W_b ∈ R^{d'×d} 投影到中间维度 d',Wₒ ∈ R^{d×d'} 投影回,并在哈达玛积后应用层归一化。这产生了输入的二阶多项式。
我们设置 d' = d,对应于 2× 扩展,通过增加深度来平衡。对于分类头,我们添加了一个内部跳跃:PolyHead(x) = Wₒ( Wₐx + (Wₐx) * (W_bx) ),防止信息在乘法交互中形成瓶颈。
3.2. PolyConv:通过卷积实现多项式空间混合
MetaFormer 的 ConvFormer 使用可分离卷积,并在投影之间带有激活。为了用哈达玛积替换此激活,我们引入了具有不同感受野的并行卷积分支。
该设计旨在乘法之前最大化特征多样性。一个粗分支使用膨胀深度卷积用于广上下文,而一个细分支使用标准 3×3 深度卷积用于局部细节。我们在融合前对一个分支应用通道翻转以进一步解相关它们: h = W_in x, (3) m = K_c(h) * flip(K_f(h)), (4) y = W_out(K(m)), (5) 其中 W_in 和 W_out 是逐点卷积,K_c 是膨胀的粗分支,K_f 是细分支,flip(·) 翻转通道,K 是 3×3 合并卷积。块后接层归一化。在第一阶段,我们使用 3×3 膨胀卷积以提高效率。
与之前在两个分支中使用相似结构的先前多项式网络不同,PolyConv 显式构建异构感受野以产生跨尺度交互项。第 4.3 节的消融实验验证了此设计。
3.3. PolyAttn:通过注意力实现多项式空间混合
自注意力计算输出为值向量 V 的加权组合,其中权重取决于查询-键相似度。给定输入 X,标准注意力投影到查询 Q = XW_Q,键 K = XW_K 和值 V = XW_V,然后计算 softmax(QK^T / √d_k) V,其中 d_k 是键维度。softmax 中的指数提供了非线性。
PolyAttn 用多项式核替换了指数。我们计算未归一化的注意力权重为: A = (s · QK^T + 1)^p, (6) 其中 s = σ(λ) 是一个可学习的每头标量,p 是多项式次数。然后我们应用 ℓ1 归一化:Â_ij = A_ij / Σ_k A_ik,并计算 ÂV。我们使用 p = 4,它平衡了表达能力和数值稳定性。
遵循 PolyConv 的结构,我们对 Q、K、V 进行深度卷积以提供局部空间上下文,类似于 CvT 和 CoAtNet。我们共享查询和键的投影以节省参数。由于 PolyAttn 仅修改注意力核,它仍然与窗口化或稀疏模式等变体兼容。
3.4. 深度多项式网络的稳定化
哈达玛积会放大较大值,并且这在层间会累积。我们引入了两种技术来实现数百层网络的稳定训练。
Sigmoid 缩放。每个子层输出由一个可学习的 sigmoid 边界标量缩放: y = x + σ(λ) · f(x), (6) 其中 f(x) 是子层输出,σ(λ) ∈ (0,1) 限制了残差贡献。我们初始化 λ 使得 σ(λ) 随深度减小。在推理时,σ(λ) 可以吸收到前一层的权重中,不引入激活。
多输入跳跃连接。我们将网络组织成细胞,每个细胞包含多个 mixer-PolyMLP 对。遵循 NASNet,每个细胞接收来自前一个细胞和再前一个细胞的输入: x̃ = s₀ * x_{t-2} + s₁ * x_{t-1}, (7) 其中 s₀, s₁ ∈ R^C 是可学习的每通道标量。组合输入在处理前通过层归一化。
深度优于宽度。深度为 L 的网络可以表示次数为 2^L 的多项式,其参数随 L 线性增长,远少于显式参数化所有 2^L 次项。堆叠细胞使得网络能够比先前的多项式架构更深。
3.5. 架构实例化:PolyNeXt
我们在一个具有四个层次阶段的 MetaFormer 风格架构中实例化我们的多项式模块。空间分辨率在每个阶段过渡时减半,而通道维度增加。概述图见附录图 S5。

变体。CPolyNeXt 在所有阶段使用 PolyConv 进行空间混合,提供了一个纯卷积多项式骨干。
主干与下采样。 主干是一个步长为4的7×7卷积。对于第1阶段,我们使用主干输出的特征来初始化两个跳跃输入。对于后续阶段,我们使用前一阶段最后两个单元的输出作为两个跳跃输入。阶段之间的下采样在进行求和之前,分别对两个跳跃路径应用步长为2的卷积。
核心源码如下:
######################################## ICML2026 PolyMLP by AI Little monster start ########################################
### http://cv2023.blog.csdn.net
def poly_init(x: torch.Tensor, batch: bool = False):
if batch:
return
nn.init.kaiming_normal_(x, nonlinearity="relu")
class LayerNorm2d(nn.LayerNorm):
def __init__(self, num_channels, affine: bool = True, bias: bool = True):
super().__init__(num_channels, elementwise_affine=affine, bias=bias)
self.bias_tf = bias
def forward(self, x):
u = x.mean(dim=1, keepdim=True)
s = ((x * x).mean(dim=1, keepdim=True) - (u * u)).clamp(0)
x = (x - u) * torch.rsqrt(s + self.eps)
if self.bias_tf:
x = x * self.weight.view(1, -1, 1, 1) + self.bias.view(1, -1, 1, 1)
else:
x = x * self.weight.view(1, -1, 1, 1)
return x
class ChannelBatchNorm(nn.Module):
def __init__(
self,
num_channels: int,
eps: float = 1e-5,
momentum: float = 0.1,
affine_hw: bool = True,
affine_c: bool = True,
bias: bool = True,
track_running_stats: bool = True,
):
super().__init__()
self.num_channels = int(num_channels)
self.eps = float(eps)
self.momentum = float(momentum)
self.affine_hw = bool(affine_hw)
self.affine_c = bool(affine_c)
self.bias_tf = bool(bias)
self.track_running_stats = bool(track_running_stats)
self.gamma_hw = None
self.beta_hw = None
if self.affine_c:
self.gamma_c = nn.Parameter(torch.ones(self.num_channels))
self.beta_c = None
else:
self.register_parameter("gamma_c", None)
self.register_parameter("beta_c", None)
if self.track_running_stats:
self.register_buffer("running_mean", torch.empty(0))
self.register_buffer("running_var", torch.empty(0))
self.register_buffer("num_batches_tracked", torch.tensor(0, dtype=torch.long))
else:
self.register_buffer("running_mean", None)
self.register_buffer("running_var", None)
self.register_buffer("num_batches_tracked", None)
def _check_input(self, x):
if x.dim() != 4:
raise ValueError(f"Expected (N,C,H,W), got {tuple(x.shape)}")
if x.size(1) != self.num_channels:
raise ValueError(f"Expected C={self.num_channels}, got C={x.size(1)}")
def _maybe_init(self, H, W, device, dtype):
if self.track_running_stats:
if (self.running_mean.numel() == 0) or (self.running_mean.shape[-2:] != (H, W)):
self.running_mean = torch.zeros((1, 1, H, W), device=device, dtype=dtype)
self.running_var = torch.ones((1, 1, H, W), device=device, dtype=dtype)
self.num_batches_tracked = torch.tensor(0, device=device, dtype=torch.long)
if self.affine_hw:
need = (self.gamma_hw is None) or (tuple(self.gamma_hw.shape[-2:]) != (H, W))
if need:
self.gamma_hw = nn.Parameter(torch.ones((1, 1, H, W), device=device, dtype=dtype))
self.beta_hw = (
nn.Parameter(torch.zeros((1, 1, H, W), device=device, dtype=dtype))
if self.bias_tf
else None
)
def forward(self, x):
self._check_input(x)
_, C, H, W = x.shape
self._maybe_init(H, W, x.device, x.dtype)
if self.training:
mean = x.mean(dim=(0, 1), keepdim=True)
ex2 = (x * x).mean(dim=(0, 1), keepdim=True)
var = (ex2 - mean * mean).clamp(min=0.0)
if self.track_running_stats:
with torch.no_grad():
self.num_batches_tracked += 1
m = self.momentum
self.running_mean.mul_(1 - m).add_(m * mean)
self.running_var.mul_(1 - m).add_(m * var)
else:
if self.track_running_stats and self.running_mean is not None and self.running_mean.numel() != 0:
mean, var = self.running_mean, self.running_var
else:
mean = x.mean(dim=(0, 1), keepdim=True)
ex2 = (x * x).mean(dim=(0, 1), keepdim=True)
var = (ex2 - mean * mean).clamp(min=0.0)
y = (x - mean) * torch.rsqrt(var + self.eps)
if self.affine_hw:
y = y * self.gamma_hw
if self.bias_tf and (self.beta_hw is not None):
y = y + self.beta_hw
if self.affine_c:
y = y * self.gamma_c.view(1, C, 1, 1)
if self.bias_tf and (self.beta_c is not None):
y = y + self.beta_c.view(1, C, 1, 1)
return y
def make_norm(norm_type, num_channels, bias=False):
if norm_type == "bn":
return ChannelBatchNorm(num_channels, bias=bias)
if norm_type == "ln":
return LayerNorm2d(num_channels, bias=bias)
raise ValueError(f"Unsupported norm_type: {norm_type}")
class DropPath(nn.Module):
def __init__(self, drop_prob: float = 0.0):
super().__init__()
self.drop_prob = float(drop_prob)
def forward(self, x):
if self.drop_prob == 0.0 or not self.training:
return x
keep_prob = 1 - self.drop_prob
shape = (x.shape[0],) + (1,) * (x.ndim - 1)
random_tensor = torch.rand(shape, dtype=x.dtype, device=x.device)
random_tensor = torch.floor(random_tensor + keep_prob)
return x / keep_prob * random_tensor
class PolyMLP(nn.Module):
def __init__(
self,
in_features, hidden_features=None, out_features=None,
norm: str = "ln",
drop_path: float = 0.0,
bias: bool = False,
):
super().__init__()
out_features = out_features or in_features
hidden_features = hidden_features or in_features
self.C = out_features
self.C_inner = hidden_features
if self.C_inner % 2 != 0:
raise ValueError(f"C * expansion must be even, got {self.C_inner}")
self.proj_in = nn.Conv2d(in_features, self.C_inner, kernel_size=1, padding=0, bias=bias)
self.proj_out = nn.Conv2d(self.C_inner // 2, self.C, kernel_size=1, padding=0, bias=bias)
self.out = nn.Sequential(
make_norm(norm, self.C_inner // 2, bias=False),
self.proj_out,
DropPath(drop_path),
)
poly_init(self.proj_in.weight)
poly_init(self.proj_out.weight)
def forward(self, x):
high, low = self.proj_in(x).split(self.C_inner // 2, 1)
return self.out(high * low)
######################################## ICML2026 PolyMLP by AI Little monster end ########################################
1)第一处修改 C2PSA_PolyMLP进行注册
from ultralytics.nn.block.PolyMLP import C2PSA_PolyMLP2)第二处修改
修改def parse_model(d, ch, verbose=True): # model_dict, input_channels(3)
只需要在你源码基础上加入C2PSA_PolyMLP,其他模块为博主其他文章的优化点
base_modules = frozenset(
{
Classify,
Conv,
ConvTranspose,
GhostConv,
Bottleneck,
GhostBottleneck,
SPP,
SPPF,
C2fPSA,
C2PSA,
DWConv,
Focus,
BottleneckCSP,
C1,
C2,
C2f,
C3k2,
RepNCSPELAN4,
ELAN1,
ADown,
AConv,
SPPELAN,
C2fAttn,
C3,
C3TR,
C3Ghost,
torch.nn.ConvTranspose2d,
DWConvTranspose2d,
C3x,
RepC3,
PSA,
SCDown,
C2fCIB,
A2C2f,
C2PSA_PolyMLP,
}
)
repeat_modules = frozenset( # modules with 'repeat' arguments
{
BottleneckCSP,
C1,
C2,
C2f,
C3k2,
C2fAttn,
C3,
C3TR,
C3Ghost,
C3x,
RepC3,
C2fPSA,
C2fCIB,
C2PSA,
A2C2f,
C2PSA_PolyMLP
}
)

# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
# Ultralytics YOLO26 object detection model with P3/8 - P5/32 outputs
# Model docs: https://docs.ultralytics.com/models/yolo26
# Task docs: https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80 # number of classes
end2end: True # whether to use end-to-end mode
reg_max: 1 # DFL bins
scales: # model compound scaling constants, i.e. 'model=yolo26n.yaml' will call yolo26.yaml with scale 'n'
# [depth, width, max_channels]
n: [0.50, 0.25, 1024] # summary: 260 layers, 2,572,280 parameters, 2,572,280 gradients, 6.1 GFLOPs
s: [0.50, 0.50, 1024] # summary: 260 layers, 10,009,784 parameters, 10,009,784 gradients, 22.8 GFLOPs
m: [0.50, 1.00, 512] # summary: 280 layers, 21,896,248 parameters, 21,896,248 gradients, 75.4 GFLOPs
l: [1.00, 1.00, 512] # summary: 392 layers, 26,299,704 parameters, 26,299,704 gradients, 93.8 GFLOPs
x: [1.00, 1.50, 512] # summary: 392 layers, 58,993,368 parameters, 58,993,368 gradients, 209.5 GFLOPs
# YOLO26n 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, 2, C3k2, [256, False, 0.25]]
- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8
- [-1, 2, C3k2, [512, False, 0.25]]
- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16
- [-1, 2, C3k2, [512, True]]
- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32
- [-1, 2, C3k2, [1024, True]]
- [-1, 1, SPPF, [1024, 5, 3, True]] # 9
- [-1, 2, C2PSA_PolyMLP, [1024]] # 10
# YOLO26n head
head:
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 6], 1, Concat, [1]] # cat backbone P4
- [-1, 2, C3k2, [512, True]] # 13
- [-1, 1, nn.Upsample, [None, 2, "nearest"]]
- [[-1, 4], 1, Concat, [1]] # cat backbone P3
- [-1, 2, C3k2, [256, True]] # 16 (P3/8-small)
- [-1, 1, Conv, [256, 3, 2]]
- [[-1, 13], 1, Concat, [1]] # cat head P4
- [-1, 2, C3k2, [512, True]] # 19 (P4/16-medium)
- [-1, 1, Conv, [512, 3, 2]]
- [[-1, 10], 1, Concat, [1]] # cat head P5
- [-1, 1, C3k2, [1024, True, 0.5, True]] # 22 (P5/32-large)
- [[16, 19, 22], 1, Detect, [nc]] # Detect(P3, P4, P5)原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。