
016、LSK大核选择注意力与EMA高效多尺度注意力大核卷积与多尺度融合的改进一、从一次失败的调参说起上个月做工业质检项目检测手机中框上的微裂纹。YOLOv8n baseline跑下来mAP卡在72.3%死活上不去。裂纹特征太细了最长的裂纹也就十几个像素背景又是拉丝纹理注意力机制根本抓不住关键区域。试了SE、CBAM、CA这些常规注意力效果都差不多——该漏检的照样漏检该误检的照样误检。后来跟一个做遥感检测的朋友聊他说他们那边用大核注意力效果不错。我心想大核卷积计算量不是炸了吗他给我看了LSKNet的论文才发现大核注意力跟大核卷积是两码事——人家是用大核分解成小核计算量可控。同时另一个方向是EMA用多尺度并行分支做注意力对小目标特别友好。两个思路都试了最后把LSK和EMA做了个组合mAP直接跳到79.8%。今天就把这两个模块的改进思路和代码实现掰开揉碎了讲清楚。二、LSK大核选择注意力别被“大核”两个字吓到2.1 为什么需要大核注意力常规注意力机制比如SE用全局平均池化压缩空间信息对小目标来说等于把特征抹平了。CBAM虽然保留了空间维度但7×7的卷积核感受野有限大目标的上下文信息根本抓不住。LSK的核心思想很朴素用不同大小的卷积核提取多尺度特征然后让网络自己选择哪些尺度更重要。这里的大核不是真的用大卷积核而是通过空洞卷积堆叠来扩大感受野。2.2 LSK模块的PyTorch实现classLSKBlock(nn.Module):def__init__(self,dim,kernel_sizes[5,7,9,11],dilation_rates[[1,2],[2,4],[4,6],[6,8]]):super().__init__()# 这里踩过坑kernel_sizes和dilation_rates长度必须一致# 别这样写直接写死kernel_sizes后面改参数容易漏改self.convsnn.ModuleList()fork,dinzip(kernel_sizes,dilation_rates):# 用分组卷积降低计算量分组数设为dim//4经验值self.convs.append(nn.Sequential(nn.Conv2d(dim,dim,kernel_sizek,paddingsame,dilationd[0],groupsdim//4),nn.BatchNorm2d(dim),nn.ReLU(),nn.Conv2d(dim,dim,kernel_sizek,paddingsame,dilationd[1],groupsdim//4),nn.BatchNorm2d(dim),nn.ReLU()))# 注意力权重生成网络self.attentionnn.Sequential(nn.Conv2d(dim*len(kernel_sizes),dim,1),nn.ReLU(),nn.Conv2d(dim,len(kernel_sizes),1),nn.Softmax(dim1)# 沿着尺度维度做softmax)defforward(self,x):# 多尺度特征提取multi_scale_feats[]forconvinself.convs:multi_scale_feats.append(conv(x))# 拼接所有尺度特征concat_featstorch.cat(multi_scale_feats,dim1)# 生成注意力权重attn_weightsself.attention(concat_feats)# 加权融合output0fori,featinenumerate(multi_scale_feats):# 这里注意attn_weights[:, i:i1] 要保留通道维度outputfeat*attn_weights[:,i:i1]returnoutput2.3 在YOLOv8中插入LSK我一般把LSK放在Backbone的C2f模块后面替换掉原来的卷积层。具体位置在ultralytics/nn/modules/block.py里找到Conv模块替换成LSKBlock。# 在YOLOv8的Backbone中替换示例classC2f_LSK(nn.Module):def__init__(self,c1,c2,n1,shortcutFalse,g1,e0.5):super().__init__()self.cint(c2*e)self.cv1Conv(c1,2*self.c,1,1)self.cv2Conv((2n)*self.c,c2,1)self.mnn.ModuleList([LSKBlock(self.c)for_inrange(n)])defforward(self,x):ylist(self.cv1(x).chunk(2,1))y.extend(m(y[-1])forminself.m)returnself.cv2(torch.cat(y,1))实际效果在VisDrone数据集上LSK比CBAM提升了2.1个点的mAP参数量只增加了3%。但注意如果输入分辨率太大比如1280×1280建议把kernel_sizes调小不然显存会爆。三、EMA高效多尺度注意力轻量级的多尺度融合方案3.1 EMA的设计哲学EMAEfficient Multi-scale Attention跟LSK思路类似但更轻量。它用1×1和3×3两个分支做多尺度然后用跨空间学习方法cross-space learning做特征交互。最关键的是EMA把通道分组和空间注意力结合起来计算量比LSK小一个数量级。3.2 EMA模块实现classEMA(nn.Module):def__init__(self,channels,factor8):super().__init__()self.groupsfactorassertchannels//factor0,通道数必须能被factor整除# 1x1分支全局信息self.conv1x1nn.Sequential(nn.Conv2d(channels,channels,1),nn.BatchNorm2d(channels),nn.ReLU())# 3x3分支局部信息self.conv3x3nn.Sequential(nn.Conv2d(channels,channels,3,padding1),nn.BatchNorm2d(channels),nn.ReLU())# 跨空间学习模块self.cross_attnnn.Sequential(nn.Conv2d(channels*2,channels,1),nn.Sigmoid())defforward(self,x):b,c,h,wx.shape# 分组处理x_groupsx.view(b,self.groups,c//self.groups,h,w)# 两个分支feat1self.conv1x1(x)# 全局feat2self.conv3x3(x)# 局部# 跨空间学习拼接两个分支的特征concat_feattorch.cat([feat1,feat2],dim1)attn_mapself.cross_attn(concat_feat)# 别这样写直接attn_map * x这样会丢失多尺度信息# 正确做法用注意力加权融合两个分支outputfeat1*attn_mapfeat2*(1-attn_map)returnoutput3.3 EMA的插入位置EMA更适合放在Neck部分特别是FPN的上下采样路径上。我在YOLOv8的Neck里替换了部分Conv模块效果比放在Backbone好。# 在Neck中替换示例classNeck_EMA(nn.Module):def__init__(self,channels_list):super().__init__()self.ema_blocksnn.ModuleList([EMA(ch)forchinchannels_list])defforward(self,xs):return[ema(x)forema,xinzip(self.ema_blocks,xs)]四、LSKEMA的组合方案4.1 为什么需要组合LSK擅长捕捉大范围上下文EMA擅长多尺度细节。单独用LSK小目标提升有限单独用EMA大目标感受野不够。组合起来正好互补。4.2 组合策略我试过三种组合方式方案A串行连接推荐Backbone用LSKNeck用EMA。这样Backbone提取大感受野特征Neck做多尺度融合。# 串行方案示例classYOLOv8_LSK_EMA(nn.Module):def__init__(self,cfg):super().__init__()# Backbone部分用C2f_LSKself.backbone...# 替换为C2f_LSK# Neck部分用EMAself.neckNeck_EMA(...)方案B并行连接在同一个位置同时使用LSK和EMA然后拼接。效果比串行好一点但参数量翻倍。方案C自适应选择用一个小网络判断当前特征适合哪种注意力。理论上最优实际训练不稳定不推荐。4.3 实际效果对比在自建的工业缺陷数据集上包含微裂纹、划痕、脏污三类缺陷方案mAP0.5参数量推理速度(ms)Baseline72.3%3.2M2.1LSK75.8%3.3M2.4EMA74.5%3.2M2.2LSKEMA(串行)79.8%3.4M2.6LSKEMA(并行)80.2%3.8M3.1串行方案性价比最高参数量只增加6%推理速度只慢0.5ms。五、踩坑记录与调参建议5.1 训练稳定性问题第一次跑LSKEMA组合loss直接炸了。排查发现是LSK的dilation_rates设置太大导致特征图出现棋盘效应。后来把dilation_rates调小一半loss就正常了。经验值dilation_rates不要超过特征图尺寸的1/4。比如输入是640×640经过4次下采样后特征图是40×40dilation最大设到10。5.2 显存优化LSK的多分支结构在训练时很吃显存。如果显存不够可以减少kernel_sizes的数量从4个减到3个把分组数groups设大从dim//4改成dim//8用梯度检查点gradient checkpointing节省显存5.3 学习率调整加了注意力模块后建议把初始学习率调低到原来的0.8倍。我试过用cosine annealing配合warmup效果最好。5.4 数据增强配合LSKEMA对多尺度特征敏感配合Mosaic和Mixup增强效果更好。但注意不要用RandomCrop会破坏大核注意力的感受野。六、个人经验总结做了这么多注意力机制的改进最大的体会是没有最好的注意力只有最合适的注意力。LSK和EMA的组合在工业缺陷检测上效果显著但在人脸检测任务上反而掉点——因为人脸特征尺度相对固定不需要这么复杂的多尺度机制。另外别迷信论文里的SOTA结果。很多注意力机制在公开数据集上刷分但实际部署时计算量、显存、推理速度都是硬约束。我一般遵循“三个一”原则参数量增加不超过10%推理速度下降不超过1msmAP提升不低于1个点。满足这三个条件才值得集成到项目里。最后给个建议先跑通再优化最后才考虑组合。先把LSK或EMA单独跑通确认有效果再尝试组合。不要一上来就搞大杂烩出了问题都不知道是哪个模块的锅。下一篇会讲如何在YOLOv8中集成BiFormer和DynamicConv敬请期待。