小波变换与卷积神经网络:高相似度图像识别和特征融合实践

发布时间:2026/9/18 19:36:13
小波变换与卷积神经网络:高相似度图像识别和特征融合实践 简介《一种融合小波变换与卷积神经网络的高相似度图像识别与分类算法》是一篇面向图像处理、机器学习方向研究者与高年级学生的学术论文PDF聚焦细粒度、高相似度图像的识别与分类难题。文中先以小波变换对图像做多尺度分解提取子图能量特征并归一化再借纹理差异度参数确定最佳分解尺度随后引入卷积神经网络通过卷积与池化交替降维迭代优化网络参数以获得判别力更强的特征表示并在散养与圈养鸡蛋识别、苹果产地判定等数据集上取得平均90%以上的鉴别准确率。资源包内含1个PDF文件约793KB轻量便于携带与精读。目前已有158人学习适合用作算法复现、课程论文写作与细粒度分类方案设计的参考。1. 高相似度图像识别里小波变换为什么要和卷积神经网络捆在一起做中药饮片分档、SAR 图像识别、工业表面缺陷分级这类项目最难受的不是把猫和狗分开而是把炒白术和焦白术、把同型号不同批次的两颗螺钉分开。这两类样本在 RGB 像素空间里几乎是重叠的普通卷积神经网络一路堆到几十层准确率也会在某个数字上卡死再加深度只换来更严重的过拟合。我接手这类需求时习惯先问一句类间差异到底藏在哪里。多数答案藏在高频细节里——边缘走向、纹理周期、局部奇异性。而二维离散小波变换正好擅长把一张图按尺度和方向拆开把细节子带显式地摆到台面上。把原图做多尺度小波分解再把 LH、HL、HH 这些细节子带当作额外通道喂给卷积神经网络等于给网络先配了一副放大镜。它的好处在于小波变换是固定、零参数、可解释的预处理卷积神经网络仍旧负责端到端学习与分类决策两者各管一段不互相打架。2. 小波变换的图像分解与增强子带怎么取、层数怎么定2.1 二维离散小波变换把一张图拆成 LL/LH/HL/HH 四个子带二维 DWT 的构造方式是把一维滤波器组用了两次。先沿图像的行方向做一次低通滤波 g 和高通滤波 h各自二抽取下采样得到 L 和 H 两幅中间结果再对这两幅中间结果沿列方向重复同样的低通、高通与下采样最终得到 LL、LH、HL、HH 四个子带每个子带的宽和高都是原图的一半。LL 是低频近似保留整体亮度和轮廓LH 携带水平方向的高频信息视觉上对应垂直走向的边缘HL 反过来HH 是对角纹理和噪点最集中的地方。多层分解时只对 LL 继续往下拆所以第 k 层会额外产生三个尺寸为原图 1/2^k 的细节子带。对高相似度图像识别与分类来说LL 反而是信息量最冗余的一路因为两类样本的整体轮廓本来就差不多。真正区分它们的是纹理周期、边缘锐度、局部奇异性这些高频成分。这也是把小波变换接在卷积神经网络前面的直接动机网络第一层不必再从原始像素里学滤波器去逼近高频响应而是直接拿到已经按方向和尺度分解好的特征。下面这张表是我在方案评审时经常拿出来对齐认知的。子带频段含义典型判别价值高相似度任务中的处理LL低频近似保留亮度与轮廓低类间几乎重合保留但单独归一化可适当降权LH水平高频垂直边缘高常见区分点乘增益后作为独立通道输入HL垂直高频水平边缘高同上HH对角高频细纹理与噪声中高噪声需抑制软阈值去噪后再做增益2.2 小波基与分解层数怎么选haar、db4、sym8 的取舍小波基决定滤波器的形状直接影响到子带里的能量分布。haar 只有两个抽头等价于 2×2 的平均差分速度最快但频率分辨率粗糙边界处会产生明显块效应db4 的消失矩为 4对多项式型平滑信号压缩得好是最常用的默认选项sym8 近似对称重构时相位失真小适合后续还要做定位类任务coif 系列消失矩更高适合弱纹理双正交族 bior 在需要线性相位时用得多。经验上我一般先跑 haar 和 db4 两档做对比如果准确率差距超过一个百分点再考虑换 sym 或 coif因为换基带来的收益往往小于调融合方式的收益。分解层数要跟输入尺寸绑在一起看。224×224 的输入做 1 层细节子带是 112×112纹理信息最完整做 2 层第二层细节变成 56×56开始偏向结构信息做到 3 层LL 只剩 28×28再往下语义已经稀疏。通道数会随层数线性增长按 RGB 三通道、每层 3 个细节子带计算1 层是 12 通道2 层是 21 通道3 层是 30 通道显存和第一层卷积的计算量都会跟着上去。小波基消失矩/特性计算开销常用场景haar1正交、最短极低快速基线、实时推理db44正交低通用默认纹理类任务sym88近似对称中需要低相位失真的定位任务coif36正交中弱纹理、低对比度图像bior2.2双正交、线性相位中需要严格线性相位的滤波链2.3 用 PyWavelets 做小波变换图像增强的最小代码下面这段代码把 BGR 图像转成多通道小波张量附带常用的高频增益增强。这是我在新项目里跑通链路的第一步先确认子带可视化正常再谈模型。import numpy as np import cv2 import pywt def _norm(a): 按子带自身统计量做零均值单位方差归一化避免高频子带被 LL 的量级淹没 std a.std() return (a - a.mean()) / (std if std 1e-6 else 1.0) def dwt_channels(bgr, waveletdb4, level2, detail_gain1.6): 返回 (C_out, H/2, W/2) 的小波通道张量。 C_out 3 * (3 * level 1)逐 RGB 通道分解不做灰度化。 img cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB).astype(np.float32) / 255.0 planes, ref_shape [], None for c in range(3): # 三通道分别分解 coeffs pywt.wavedec2(img[..., c], waveletwavelet, levellevel, modesymmetric) planes.append(_norm(coeffs[0])) # LL 单独归一化 for detail in coeffs[1:]: # 每层是 (LH, HL, HH) for band in detail: b _norm(band) * detail_gain # 高频乘增益做增强 if ref_shape is None: ref_shape b.shape elif b.shape ! ref_shape: # 深层子带尺寸更小 b cv2.resize(b, (ref_shape[1], ref_shape[0]), interpolationcv2.INTER_LINEAR) planes.append(b) return np.stack(planes, axis0) if __name__ __main__: x dwt_channels(cv2.imread(sample.jpg)) print(x.shape, x.min(), x.max())逻辑上分成三步先把像素归一到 0~1避免不同位深图像引入尺度差异再逐通道做wavedec2把系数按 LL、各层细节的顺序铺开最后统一尺寸、统一归一化后堆成通道维。参数上有三个要点wavelet一旦确定就不能在训练和推理之间变动level越大通道越多第一层卷积的参数量按比例涨detail_gain是高频增益1.0 表示不做增强1.5~2.0 是我常用的区间再高会把噪声一起放大。如果要走重构路径做增强把细节系数做软阈值后再waverec2注意clip回 0~1 并乘 255 转回 uint8。2.4 落地时最容易踩的四个坑第一个坑是训练和推理用了不同的小波参数。有人训练时用 db4 两层部署时图省事换成 haar 一层精度掉十几个点还找不到原因因为子带分布已经完全变了。第二个坑是边界模式不统一mode选zero时图像边缘会引入人为的强边缘symmetric相对温和图像尺寸为奇数时还得先 padding 到偶数。第三个坑是归一化尺度。LL 的数值范围和细节子带差一到两个数量级如果只用一次全局归一化高频信息在数值上会被压成接近零网络等于没用上小波分支。第四个坑是把 DWT 放在 DataLoader 的 CPU 侧实时算pywt是纯 CPU 实现224×224 三通道两层分解在批量训练时会成为瓶颈常见做法是把小波变换的卷积形式写成固定权重的nn.Conv2d或者预先把子带缓存成额外通道存盘。提示验证小波分支是否真的生效最快的办法是把 detail_gain 置 0只留 LL 通道跑一轮如果准确率几乎不变说明融合结构没学到高频信息。3. 卷积神经网络分支怎么搭骨干选型与融合位置3.1 高相似度任务的骨干选型LeNet-5、ResNet-18、MobileNetV3 与 MaxViT 的边界骨干不是越大越好。高相似度任务的样本量通常不大几千到几万张而且标注成本高参数量过亿的模型直接上会严重过拟合。我在小波通道输入这个前提下一般按下面的顺序试。骨干参数量级输入建议适合场景与小波分支配合的方式LeNet-5十万级以下32×32快速验证链路、灰度纹理直接接受多通道输入ResNet-18/34千万级224×224通用首选稳替换第一层卷积输入通道数MobileNetV3百万级224×224边缘部署、实时推理深度可分离卷积前接融合层MaxViT 系列千万级以上224×256数据充足时的精度上限对比只用 RGB 分支与 CNN 分支做晚融合MaxViT 这类混合了卷积与注意力的骨干可以在 timm 里用timm.list_models(*maxvit*)查出可用权重作为精度上界参考但把多通道小波张量塞进带窗口注意力的一层并不划算通常的做法是让小波分支单独走一个轻量 CNN最后在 logits 层面加权。3.2 从 LeNet-5 看卷积神经网络基本结构与感受野LeNet-5 是最适合用来讲清结构的最小样本两个卷积加池化的组合块后面接三个全连接层输入 32×32输出 10 类。它的价值不在精度而在能让人把「卷积—非线性—池化」这条链子和感受野对上。感受野的递推是 R_out R_in (k − 1) × J_in其中 J 是累计步长。两个 3×3 卷积堆叠的感受野等于一个 5×5但参数是 2×9 18 比 25中间还多了一次非线性这就是 VGG 之后大家都用 3×3 堆叠的原因。小波分支的位置也受这个约束影响。如果把 21 通道的子带张量直接喂给一个 7×7 大核第一层感受野一下子铺得太开反而是 3×3 的小核堆两到三层更合适因为子带本身已经完成了尺度分解网络要做的是把方向信息组合起来而不是再去学大范围的空间滤波。3.3 早融合、中融合、晚融合三种融合位置的取舍这是整篇方案里最需要做实验的地方。早融合把 RGB 和小波子带在输入层拼成 12 或 21 通道共享一个骨干中融合让两个分支各提特征在某个 stage 之后拼接或做注意力加权晚融合各自出 logits 再加权平均。三者的差异不只是精度还有显存、训练难度和部署复杂度。融合方式实现复杂度参数量优点风险早融合低改第一层输入通道与单分支持平训练稳定推理一次前向小波与 RGB 分布差异大第一层难收敛中融合中需要双分支对齐增加约 1.5 倍精度通常最好可加注意力加权两支特征尺度需对齐易过拟合晚融合低两个独立模型增加约 2 倍可分别调参易做集成推理成本翻倍小波分支弱时拖后腿中融合里如果想把多尺度子带当成一个额外维度处理可以用 3D 卷积在 (通道, 尺度, 高, 宽) 上做卷积代价是显存涨得很快也可以把每个样本看成图节点、用图卷积神经网络在样本相似度图上做半监督传播这条路适合标注极少但样本间关系明确的场景。多数项目我先跑早融合拿基线再用中融合去争最后那一两个点。3.4 PyTorch 实现小波-CNN 双分支模型import torch import torch.nn as nn import torchvision.models as models class SEWeight(nn.Module): 通道注意力用来给两个分支的特征做自适应加权 def __init__(self, ch, r8): super().__init__() self.fc nn.Sequential( nn.Linear(ch * 2, ch * 2 // r, biasFalse), nn.ReLU(inplaceTrue), nn.Linear(ch * 2 // r, 2, biasFalse), nn.Softmax(dim1)) def forward(self, f_rgb, f_wav): w self.fc(torch.cat([f_rgb.mean((2, 3)), f_wav.mean((2, 3))], dim1)) return f_rgb * w[:, 0:1, None, None] f_wav * w[:, 1:2, None, None] class WaveletCNN(nn.Module): def __init__(self, num_classes, wav_channels21, fusion_dim256): super().__init__() rgb models.resnet18(weightsNone) self.rgb_stem nn.Sequential(rgb.conv1, rgb.bn1, rgb.relu, rgb.maxpool) self.rgb_body nn.Sequential(rgb.layer1, rgb.layer2, rgb.layer3, rgb.layer4) # 小波分支用轻量 3 层卷积输入通道数等于子带总数 self.wav_stem nn.Sequential( nn.Conv2d(wav_channels, 64, 3, 2, 1, biasFalse), nn.BatchNorm2d(64), nn.ReLU(True), nn.Conv2d(64, 128, 3, 2, 1, biasFalse), nn.BatchNorm2d(128), nn.ReLU(True), nn.Conv2d(128, 256, 3, 2, 1, biasFalse), nn.BatchNorm2d(256), nn.ReLU(True), nn.AdaptiveAvgPool2d(1)) self.fuse SEWeight(512) # ResNet18 layer4 输出 512 通道 self.proj nn.Linear(512 256, fusion_dim) self.head nn.Linear(fusion_dim, num_classes) def forward(self, rgb, wav): f_rgb self.rgb_body(self.rgb_stem(rgb)) # (B, 512, 7, 7) f_wav self.wav_stem(wav).flatten(1) # (B, 256) f_rgb self.fuse(f_rgb, f_rgb.detach() * 0 f_rgb).mean((2, 3)) z torch.relu(self.proj(torch.cat([f_rgb, f_wav], dim1))) return self.head(z)这段代码里rgb_stem和rgb_body拆开是为了方便在需要时冻结主干做微调wav_stem用步长为 2 的三层卷积把子带图快速降到 1×1避免小波分支占用过多显存SEWeight给两支特征算一组两维权重再相加让网络自己决定信谁多一点。参数方面wav_channels必须和dwt_channels的输出通道数严格一致用 2 层 db4 三通道就是 21fusion_dim是融合后的特征维度256 在小数据集上比 512 更稳。上面fuse那一行里的写法是为了演示接口实际使用时应把 RGB 特征和独立的f_wav特征一起送进SEWeight再做加权别把同一路特征加两次。4. 高相似度图像分类的训练与调参损失、增强与超参数4.1 先按拍摄批次划分数据集再谈精度高相似度任务的精度虚高八成来自数据泄漏。同一张样本连拍多帧、同一批药材同一天同一种光拍了几十张如果随机划分训练集和验证集里会出现近乎重复的图验证准确率能到 99%一上真实产线就崩。我一般先按批次或采集日期分组再做分组划分保证同一批次只出现在一个集合里。划分完先看类间样本数的分布长尾严重时用分层抽样并给少数类加权重采样。验证集还要专门留一批「难例对」也就是肉眼都要看两秒才能分辨的样本对。方法很简单先用一个基线模型跑一遍训练集取预测置信度落在 0.4~0.6 区间的样本按类别对统计数量把出现最多的十组类别对单独抽出来放进验证集。这样后面的每一次改动看的都是真正难的那部分。4.2 损失函数怎么选交叉熵、标签平滑、Center Loss 与 ArcFace高相似度分类的损失设计目标是拉大类间距离、压缩类内方差。交叉熵只关注决策边界不直接约束特征分布所以经常会看到准确率还行但混淆矩阵里有两类互相渗透。下面这张表是我常用的组合。损失/技巧作用关键参数适用阶段CrossEntropy基础分类目标label_smoothing0.05~0.1全程Center Loss压缩类内方差权重 0.003~0.01中期加入后期衰减ArcFace / CosFace增大类间角度间隔尺度 30间隔 0.3~0.5类别数多、样本均衡Focal Loss抑制易分样本主导gamma1.5~2.0长尾严重时替代 CE我的默认起点是标签平滑后的交叉熵训练到中段再把 Center Loss 以 0.005 的权重挂上如果混淆矩阵里仍有明显的成对误判再考虑换成 ArcFace 头。ArcFace 在二分类和类别数很少的场景收益有限热词里常见的「二分类算法」如果只有两类用带标签平滑的交叉熵加阈值搜索就够了。4.3 一份可直接改用的训练配置import torch from torch.optim import AdamW from torch.optim.lr_scheduler import CosineAnnealingLR def build_trainer(model, train_loader, epochs60, lr3e-4, wd5e-4): opt AdamW(model.parameters(), lrlr, weight_decaywd) sched CosineAnnealingLR(opt, T_maxepochs, eta_minlr * 0.01) scaler torch.cuda.amp.GradScaler() # 混合精度省显存约 40% crit torch.nn.CrossEntropyLoss(label_smoothing0.08) for ep in range(epochs): model.train() for rgb, wav, y in train_loader: rgb, wav, y rgb.cuda(), wav.cuda(), y.cuda() with torch.cuda.amp.autocast(): loss crit(model(rgb, wav), y) opt.zero_grad(set_to_noneTrue) scaler.scale(loss).backward() # 先 scale 再反传 scaler.unscale_(opt) # 反传后、step 前解缩放 torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0) scaler.step(opt) scaler.update() sched.step() return model关键参数逐个说lr3e-4是双分支模型的常用起点小波分支是随机初始化的学习率比主干高一个数量级也合理可以把wav_stem单独放进第二个参数组设成 1e-3wd5e-4配合 AdamW 才有解耦权重衰减的效果label_smoothing0.08在高相似度任务里比 0 明显更稳因为硬标签会逼模型对模糊边界过度自信梯度裁剪阈值 5.0 是防止小波分支前期梯度爆炸如果换成 ArcFace 头尺度参数要调大再观察。4.4 训练异常时的排查顺序出现 loss 不降我按这个顺序查先确认小波张量和 RGB 张量的尺寸、通道数、归一化范围是否和模型定义一致多通道输入最常见的错误是wav_channels写错再看第一层权重是否被 BatchNorm 初始化搞成了接近零输出可以把小波分支的 BN 换成 GroupNorm 试一轮。出现训练集 99% 验证集 70%先怀疑数据泄漏和重复样本再检查detail_gain是不是过大导致噪声被当成特征学走。出现混淆矩阵里某两类互相渗透说明特征空间里这两类没被分开这时候加数据比加层数有效。出现训练后期 loss 震荡把学习率调度从余弦换成带 3 轮线性预热再接余弦让随机初始化的小波分支先稳定几轮。注意小波分支的随机初始化和主干预训练权重的收敛速度差很多如果不做分层学习率或预热前几个 epoch 主干会被小波分支的噪声梯度拖偏。5. 结果验证与进阶技巧消融、混淆矩阵与难例微调5.1 消融实验要跑成对照表而不是只报一个最高分高相似度任务的成果评委或客户问的第一句永远是「小波到底贡献了多少」。这张表建议在实验记录里固定下来每一行只改一个变量其余全部锁定用 macro-F1 而不是准确率做主要指标因为长尾分布下准确率会被大类主导。配置输入通道融合位置Accuracymacro-F1备注A 基线3纯 RGB无待填待填同骨干同训练配置B 早融合121 层 db4输入层待填待填只加一层细节C 早融合212 层 db4输入层待填待填看层数收益D 中融合3 21注意力加权待填待填参数量增加约 1.5 倍E 消融21 但细节增益置 0输入层待填待填验证高频是否真被用到E 这一行最关键。它把细节子带的幅度压到接近零只保留 LL如果指标和 B 差不多说明前面的提升来自通道数增加而不是小波分解本身这时候要回去改融合结构而不是继续加分解层数。5.2 用混淆矩阵和 t-SNE 定位成对误判跑完训练我会固定导出一张归一化混淆矩阵把非对角线上最大的五个格子挑出来那就是下一轮要重点攻的类别对。import numpy as np from sklearn.metrics import confusion_matrix, f1_score def report(y_true, y_pred, classes, topk5): cm confusion_matrix(y_true, y_pred, normalizetrue) pairs [(cm[i, j], classes[i], classes[j]) for i in range(len(classes)) for j in range(len(classes)) if i ! j] for rate, a, b in sorted(pairs, reverseTrue)[:topk]: print(f{a} - {b}: {rate:.3f}) # 只打印最容易混淆的类别对 print(macro-F1 , round(f1_score(y_true, y_pred, averagemacro), 4)) return cmnormalizetrue按真实类别归一化读法是「这个类有多少比例被判成了另一类」比原始计数更能反映类内一致性。配合 t-SNE 把融合层输出降到二维能看出成对误判的两类在特征空间里是不是粘在一起如果是说明判别特征不够如果是分得开但边界附近样本多说明需要更细的决策阈值。5.3 只解冻融合层做难例微调整网微调在小数据集上很容易把已经学好的特征冲掉。我的做法是冻结rgb_body和wav_stem只训练fuse、proj和head三层用 5.2 里挑出的成对误判样本构造成对采样器每个 batch 里保证这两类各占一半学习率降到 1e-4跑 10 到 15 轮。对样本量只有几百的类别对这个手段通常能拿回两到三个百分点而全量微调同一份数据往往只是把验证集噪声拟合了一遍。如果成对误判集中在细纹理上可以在这轮微调里把detail_gain从 1.6 提到 2.0再配合 5.1 的 E 组做一次对照确认提升确实来自高频子带而不是随机波动。微调结束后记得把这一轮的混淆矩阵存下来和上一轮逐格对比成对误判率下降最多的类别对就是下一批需要补采样的方向。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询