YOLO26改进 – 采样 ICCV 顶会技术:WaveletPool 小波池化强化采样,保留小目标细节
# 前言 本文介绍了基于小波变换的池化方法——Wavelet Pooling,作为传统最大池化与平均池化的有效替代方案。该方法通过两级小波分解丢弃高频子带,保留更具代表性的低频特征,从而在减少信息丢失的同时提升模型的正则化能力。我们将 Wavelet Pool 和 UnPool 成功集成进 YOLO26,替代原有的下采样与上采样模块,实现更高效的特征提取与恢复。实验证明,YOLO26-WaveletPool 在多个分类与检测任务中均取得优异表现,展现了小波池化在深度学习中的广泛应用前景。
文章目录: YOLO26改进大全:卷积层、轻量化、注意力机制、损失函数、Backbone、SPPF、Neck、检测头全方位优化汇总
专栏链接: YOLO26改进专栏
介绍

摘要
卷积神经网络(Convolutional Neural Networks, CNNs)持续推动着二维和三维图像分类及目标识别技术的发展。然而,为了维持这一快速进展,有必要对神经网络中的基础构件进行持续的评估与改进。当前主流的网络正则化方法大多侧重于卷积操作本身,而对池化层的设计选择关注不足。
为此,我们提出了一种新的池化策略——小波池化(Wavelet Pooling),作为传统邻域池化方法(如最大池化和平均池化)的有效替代方案。该方法通过将特征分解为多层小波子带,并舍弃第一层级的高频子带来实现下采样,从而有效降低特征维度。与最大池化中常见的过拟合问题不同,小波池化在降维过程中保留了更多结构信息,具备更强的泛化能力。此外,相比于基于固定邻域的池化方式,小波池化在结构上实现了更紧凑、高效的特征压缩。
我们在四个标准图像分类数据集上进行了系统实验,结果表明:所提出的小波池化方法在性能上显著优于或与最大池化、平均池化、混合池化以及随机池化等主流方法相当,验证了其作为通用池化策略的潜力。
文章链接
论文地址:论文地址
代码地址:代码地址
论文地址:论文地址
基本原理:
首先,池化是一种通过舍弃信息实现正则化效果的操作。然而,传统的池化方法存在一些不足:
- Max pooling:当重要特征的幅度值低于不重要特征时,重要特征会被忽略。
- Average pooling:同时接纳幅值大和幅值小的特征,容易稀释关键特征。
为了解决这些问题,该论文提出基于小波变换的池化操作,具体思路如下:
小波变换的基本原理
小波变换可将输入特征图划分为低频子带(LL)和高频子带(LH、HL、HH)。其数学公式为:
-
一级小波变换:
$$ LL1, LH1, HL1, HH1 = DWT(I) $$ 逆变换:
$$ I = IDWT(LL1, LH1, HL1, HH1) $$ -
二级小波变换:
$$ LL2, LH2, HL2, HH2 = DWT(LL1) $$ 逆变换:
$$ LL1 = IDWT(LL2, LH2, HL2, HH2) $$
小波变换通过下采样将特征图尺寸缩小一半,逆变换可完美重建原始图像。
论文的方法
该论文方法流程如下:
- 对输入图像 $I$ 进行两次小波变换,得到:
$$ LL2, (LH2, HL2, HH2), (LH1, HL1, HH1) = DWT(DWT(I)) $$ - 舍弃最高频子带 $(LH1, HL1, HH1)$,保留低频子带 $(LL2, LH2, HL2, HH2)$。
- 对保留的二级小波系数进行逆变换,重建池化后的图像:
$$ I' = IDWT(LL2, LH2, HL2, HH2) $$
核心代码
class WaveletPool(nn.Module):
def __init__(self):
super(WaveletPool, self).__init__()
ll = np.array([[0.5, 0.5], [0.5, 0.5]])
lh = np.array([[-0.5, -0.5], [0.5, 0.5]])
hl = np.array([[-0.5, 0.5], [-0.5, 0.5]])
hh = np.array([[0.5, -0.5], [-0.5, 0.5]])
filts = np.stack([ll[None,::-1,::-1], lh[None,::-1,::-1],
hl[None,::-1,::-1], hh[None,::-1,::-1]],
axis=0)
self.weight = nn.Parameter(
torch.tensor(filts).to(torch.get_default_dtype()),
requires_grad=False)
def forward(self, x):
C = x.shape[1]
filters = torch.cat([self.weight,] * C, dim=0)
y = F.conv2d(x, filters, groups=C, stride=2)
return y
class WaveletUnPool(nn.Module):
def __init__(self):
super(WaveletUnPool, self).__init__()
ll = np.array([[0.5, 0.5], [0.5, 0.5]])
lh = np.array([[-0.5, -0.5], [0.5, 0.5]])
hl = np.array([[-0.5, 0.5], [-0.5, 0.5]])
hh = np.array([[0.5, -0.5], [-0.5, 0.5]])
filts = np.stack([ll[None, ::-1, ::-1], lh[None, ::-1, ::-1],
hl[None, ::-1, ::-1], hh[None, ::-1, ::-1]],
axis=0)
self.weight = nn.Parameter(
torch.tensor(filts).to(torch.get_default_dtype()),
requires_grad=False)
def forward(self, x):
C = torch.floor_divide(x.shape[1], 4)
filters = torch.cat([self.weight, ] * C, dim=0)
y = F.conv_transpose2d(x, filters, groups=C, stride=2)
return y
实验
脚本
import warnings
warnings.filterwarnings('ignore')
from ultralytics import YOLO
if __name__ == '__main__':
# 修改为自己的配置文件地址
model = YOLO('./ultralytics/cfg/models/26/yolo26-WaveletPool.yaml')
# 修改为自己的数据集地址
model.train(data='./ultralytics/cfg/datasets/coco8.yaml',
cache=False,
imgsz=640,
epochs=10,
single_cls=False, # 是否是单类别检测
batch=8,
close_mosaic=10,
workers=0,
optimizer='MuSGD',
# optimizer='SGD',
amp=False,
project='runs/train',
name='yolo26-WaveletPool',
)
结果
