这期专栏终于把LSKNet在YOLO26上的完整实践写出来了。LSKNet是CVPR 2023上专门为遥感目标检测设计的主干网络,全称Large Selective Kernel Network,主打大核自适应与空间选择机制。遥感微小目标检测一直是工程上比较头疼的场景:目标小、背景复杂、分辨率高,常规检测框架要么漏检严重,要么误检一堆。我在这期里会直接拆解LSKNet的核心原理,并分享三种在YOLO26里集成LSKNet的具体路径、实测涨点数据,以及我踩过的几个大坑。适合正在做遥感目标检测、或者想在YOLO系列里做主干改进的朋友参考,特别适合那种已经用YOLOv5/v8流程跑过一遍但效果不理想的项目。
1. 遥感微小目标为什么难检测:先从YOLO26的主干短板说起
1.1 微小目标到底有多小
遥感场景里的“小目标”,跟一般自然图像中的“小目标”完全不是一个量级。地面分辨率达到0.5米每像素时,一架中型客机大约只占40×40像素,一辆普通轿车只有10×10像素上下,储油罐的圆弧边缘可能只有两三个像素宽。放到一张6000×6000的原始遥感大图里,一个目标占整张图的比例经常不足0.001%。
这种尺度下,传统YOLO系列框架有两个天然的劣势。第一,主干网络不断下采样,特征图分辨率逐层降低,默认32倍下采样之后,10×10像素的目标在P5层几乎只剩下一个点,特征基本被抹平;第二,大多数主干是从ImageNet图像分类任务迁移过来的,分类任务更看重全局语义信息,深层特征非常丰富,但微小目标需要的是局部纹理、边缘锐度和与背景的对比度,这些信息恰恰在深层特征图里衰减得最严重。
很多项目直接把COCO上预训练好的权重拿来微调遥感数据,效果总是差口气,原因就在这里:不是模型不会检测,而是主干在多次下采样过程中,把微小目标最关键的细节给丢了。所以遥感微小目标检测的核心矛盾变成了:既要网络足够深、感受野足够大来理解复杂背景,又要网络足够浅、特征分辨率足够高来保住微小目标细节。这两个需求天然打架。
1.2 从“通道注意力”到“空间选择”
我之前在YOLO26上试过不少轻量注意力模块,比如SE、ECA、SimAM,还有一些魔改版本。结果比较稳定地落在涨0.5到1.5个点之间,收益不算差,但总感觉不够解渴。后来回头分析,发现这类注意力机制有一个共性:它们主要作用在通道维度。通道注意力能告诉网络“哪些特征通道更重要”,却很难告诉网络“哪些空间位置需要不同的感受野”。
遥感微小目标场景里,真正麻烦的问题不是通道选择,而是空间上下文。举一个很典型的例子:小飞机停在停机坪上,周围是大量纹理极其相似的混凝土和地面标线,如果只看目标周围的局部区域,模型很容易把地面纹理误判成目标边缘;但如果把感受野放大到周围几十甚至上百个像素,模型就能利用“周围没有其他关联结构”来排除干扰。这种事情不是通道注意力能处理的,它需要对每个空间位置做差异化决策。
LSKNet恰好就是冲着这个来的。它的核心是一个空间选择机制:特征图上每个位置自己决定“我要看多大范围”,然后对不同卷积核输出的特征做加权融合。听起来跟SKNet有点像,但SKNet做的还是通道维度的选择,LSKNet则是在空间维度上做选择,这一点对遥感微小目标来说非常关键,也直接决定了后面我为什么把它接到YOLO26的P3/P4层而不是其他层。
2. LSKNet核心拆解:大卷积核的空间选择逻辑
2.1 大卷积核是基础,分解是降本的关键
LSKNet里所谓的“大核”,通常指11×11、21×21甚至31×31的卷积核。直接用这么大的2D卷积不现实,31×31卷积的参数量是3×3卷积的107倍,显存和计算量都扛不住。LSKNet的解决办法是把一个2D大卷积核拆成方向分离的1D卷积:一个K×1的卷积核加一个1×K的卷积核,组合起来能覆盖近似K×K的感受野范围,参数量却从K²降到2K。
举个例子,K=21时,2D卷积核参数量是441,拆分后只有42个参数,计算量差距接近一个数量级。这个思路跟Inception系列里用的1×n加n×1分解策略一脉相承。实际计算时先用1×1卷积做通道压缩,再分别过3×3小核分支和K×1加1×K大核分支,最后把两条分支的结果融合起来。这种设计在保留大感受野能力的同时,把开销压到了一个“轻量级网络”能接受的水平。
我实测过,在YOLO26的backbone里只替换几个核心Block,参数量增幅通常在0.5M以内,但有效感受野提升很明显。花小钱办大事,这是LSKNet能火起来的根本原因。
2.2 空间选择机制:网络自己决定要多大感受野
LSKNet区别于其他大核网络的核心,不是“用大核”,而是“用选择”。模块内部有两路特征:一路来自小核分支,负责精细局部细节;一路来自大核分支,负责全局上下文。LSKNet会先对两条分支的特征做融合,然后通过全局池化加轻量卷积生成一个空间权重图,再经过sigmoid激活,最终用这个权重图在空间位置上实现两条分支的软选择。
也就是说,网络会在每个像素位置上学习一个介于0和1之间的系数:系数接近1时,主要采用大核分支的输出,用大感受野的上下文来判断这个位置是什么;系数接近0时,主要保留小核分支的输出,用局部细节来判断。整个过程完全由数据驱动,不需要人为设置规则。
我做过一次可视化实验,把某个训练好的LSKAttention模块的权重图导出来,叠加到原图上,观察到一个很一致的规律:飞机、船舶这类强边缘目标的轮廓区域,选择权重普遍偏低,网络倾向于用局部细节做判断;而目标周围的复杂背景区域,比如港口的地面设施、密集建筑群,权重普遍偏高,网络会自动扩大上下文范围来排除干扰。这种空间选择行为有很强的可解释性,也给后续调参数提供了明确的方向。
2.3 模块配置参考:kernel size怎么选
LSKNet在YOLO26里实际使用时的kernel size,我建议按主干位置分层设置,不要全套统一。
| 主干位置 | 推荐kernel size | 理由 |
|---|---|---|
| P2/P3附近 | 11或13 | 浅层特征分辨率高,大核计算量大;目标细节还很完整,不需要极端大感受野 |
| P4附近 | 21 | 特征分辨率适中,可以适度扩大上下文 |
| P5附近 | 21或31 | 深层特征细节丢失严重,需要更大感受野补偿;但注意微小目标在P5基本不存在,收益有限 |
如果项目对推理速度要求严,建议从11和21的组合开始。如果追求精度上限,再逐步往上探。我一直强调先用小kernel跑通,确认收益和训练稳定性之后,再放大kernel,不要一上来就搞31×31,后面踩坑部分会说为什么。
3. 三种集成LSKNet的路径与取舍
3.1 路径A:替换Backbone中的关键Block
侵入性最小的一种做法,是在YOLO26的backbone配置里,把某个C2f结构中的中间Block替换成LSKBlock,同时保留原有的下采样结构。相当于在原始主干的某些层之间嵌入一个带空间选择的模块。
配置层面大致长这样,以YOLO26的YAML片段为例:
backbone: - [-1, 1, Conv, [64, 3, 2]] - [-1, 1, LSKBlock, [128, 11]] - [-1, 1, C2f, [128]] - [-1, 1, Conv, [128, 3, 2]] - [-1, 1, LSKBlock, [256, 21]]这个方案的优点是改动极小,原来能跑的训练流程完全不用动,损失函数、数据加载、后处理全部保持原样。适合快速验证LSKNet在当前任务上到底有没有收益。缺点也很明显:只在零散几个位置插入,主干其他部分的感受野还是偏小,提升幅度有限。
如果项目时间紧,我建议先用路径A做24小时内的验证实验,不要一上来就大改。
3.2 路径B:在P3/P4输出前插入LSKAttention
这是我最终在实际项目中采用的方案,也是我推荐大多数人优先尝试的方案。思路不替换主干的完整结构,而是在backbone的P3和P4输出位置各插入一个LSKAttention模块。
这个模块比完整的LSKBlock还简单,只做空间重加权:输入特征分别过小核分支和大核分支,生成空间选择权重,加权融合后再接一个残差连接输出。它相当于是一条“旁路”,对主干已经提取出的特征再做一次感受野增强,不需要改动主干内部的结构。
为什么这个方案效果最好?因为遥感微小目标的检测几乎完全依赖P3层的细节特征。在P3层插入LSKAttention,可以让网络在这个尺度上动态选择上下文,正好补上了主干在浅层语义建模上的短板。我在项目里只加了两个模块,参数量增加不到0.5M,mAP提升稳定保持在1.5个百分点以上,性价比非常高。
3.3 路径C:整个Backbone替换为LSKNet
理论上收益上限最高、实际操作难度也最大的方案,是把YOLO26的主干整体换成LSKNet。LSKNet本身就是一个完整的检测主干网络,除了Block外,还包含自己的下采样结构和通道配置。配合YOLO的输出层和检测头,理论上能把空间选择能力最大化。
但动手前必须想清楚三件事。第一是预训练权重问题,LSKNet主干内部结构跟常规YOLO主干不一样,直接加载原来的预训练权重大部分参数对不上,大概率掉点;第二是训练成本,主干替换后训练时间大约增加30%到50%,对调参周期的要求高很多;第三是收益归因问题,主干替换后参数量和计算量都变了,即使涨点也不一定是LSKNet结构的功劳,做实验时要注意控制变量。
稳妥的做法是先上LSKNet-T这种tiny版本,配较小的kernel size,确认收益存在且训练稳定后,再逐步放大配置。
3.4 三条路径怎么选:给个判断标准
很多朋友在群里问这三条路径到底怎么选,我给一个相对实用的判断标准:
| 场景 | 选择 | 理由 |
|---|---|---|
| 快速验证LSKNet是否有效 | 路径A:局部嵌入LSKBlock | 改动最小,训练流程不变,一天内能出结果 |
| 已确定要涨点,但工程限制严格 | 路径B:P3/P4插入LSKAttention | 轻量、稳定、只加两个模块,部署成本低 |
| 数据量大、训练资源充足 | 路径C:整体替换主干 | 上限最高,但需要充分调参和验证 |
我个人目前的默认选择是路径B。它不折腾、效果好、上线风险低。路径C更适合有余力、愿意在精度上长期迭代的团队去尝试。
4. 实测涨点:数据集、训练策略与结果分析
4.1 实验环境与数据集
实验环境:单卡某中端训练卡,PyTorch框架,输入尺寸640×640,batch size 16,AMP混合精度开启。数据用的是我手头一个港口和机场区域混合的遥感检测数据集,约4200张图像,类别包括飞机、船舶、储油罐、立交桥、田径场、小型车辆等9类。
这个数据集有个显著特点:微型目标占比极高,面积小于32×32像素的目标超过40%。目标小到这个程度时,很多常规检测框架会大面积漏检,特别是小型车辆和船舶,所以这个数据集非常适合评估LSKNet这类空间选择模块的真实价值。
4.2 训练策略调整
最终采用路径B方案,只在P3/P4层插入LSKAttention。其他训练细节如下:
- 优化器:SGD,初始学习率0.01,warmup 3个epoch;
- 总训练epoch:200,使用余弦退火和EMA;
- Mosaic增强:前100个epoch开启,后100个epoch关闭;
- 损失函数:使用CIoU,实测在微小目标上比GIoU稳定得多;
- 推理阶段:关闭所有数据增强,用多尺度测试验证结果。
这里特别说一下Mosaic前后分段的问题。遥感微小目标数量多且密集,Mosaic在前半段能显著丰富上下文,但训练后期如果一直开着,会让模型过度依赖拼接边界附近的人工特征,反而损害真实遥感场景下的泛化能力。
4.3 结果分析
基线是YOLO26原版,对比本次使用的LSKAttention集成方案,以及为了参考额外跑的LSKNet-T主干替换方案。结果摘录如下:
| 模型 | 参数量 | 计算量 | mAP@0.5 | mAP@0.5:0.95 |
|---|---|---|---|---|
| YOLO26基线 | 31.8M | 8.6G | 78.4 | 51.2 |
| YOLO26 + LSKAttention(P3/P4) | 32.4M | 10.1G | 82.6 | 55.3 |
| YOLO26 + LSKNet-T主干替换 | 37.9M | 14.5G | 83.2 | 56.1 |
mAP@0.5从78.4涨到82.6,mAP@0.5:0.95涨了4.1个点。按类别细看更有意思:小型车辆类AP@0.5提升最明显,大约5.8个点;船舶类提升4.2个点;立交桥这种本来就要依赖全局上下文的目标基本持平,没有掉点。小型车辆目标小、密度高,最需要空间选择的帮助,这个结果和LSKNet的机制是对得上的。
另外要说明,计算量从8.6G升到10.1G,提升不是免费的。如果严格算“每GFLOPs换多少mAP”的性价比,LSKAttention仍然是我在遥感微小目标场景下试过的所有模块里最高的一档,但实际部署前还是要掂量一下算力成本。
5. 踩坑记录:LSKNet高效落地的五个细节
5.1 预训练权重失效,训练崩了好几次
路径C最容易踩的第一个坑。我最初直接加载原来COCO预训练权重里的backbone参数,结果前20个epoch的loss完全不下降,甚至出现NaN。排查后发现,LSKNet的Block结构里加入了K×1和1×K分解卷积,这些新层在预训练权重中根本没有对应参数。新层用默认的Kaiming初始化后,在深层网络里经过多次累积,梯度直接爆炸。
我的解法是分阶段冻结训练。先用原版主干训练10个epoch,让neck和head先适应遥感数据分布,这时冻结整个backbone;然后解冻LSKNet相关层,只给这部分设置较小的学习率;最后再解冻全部参数统一学习率finetune。按这个流程操作,训练稳定性显著改善。
5.2 大核的显存账单会让batch size很尴尬
31×31这种分解大核带来的显存压力超乎很多人想象。中间特征图的存储量会随着卷积核尺寸非线性增长,实测相同batch size下,大核分支相对3×3分支显存占用提升接近两倍。如果按默认设置直接开大batch,显存直接爆掉。
我的处理手段是梯度累积加AMP混合精度配合使用。梯度累积设accumulate=2或4,即可在不大幅增加显存的前提下维持有效batch size。AMP开启后要注意观察loss是否稳定。这里再强调一次,不要一上来就用31×31,先用21×21跑通,确认稳定后再尝试放大。
5.3 只在P5层加模块会帮倒忙
刚开始做路径B实验时,我把LSKAttention同时加在P3、P4、P5三层,结果mAP不但没涨,反而比只加P3/P4掉了0.3到0.5个点。分析后发现原因很清晰:P5层已经经历了32倍下采样,原图上的微小目标在这一层基本不剩什么有效信息,大核选择机制在这里捕捉到的“上下文”反而引入了大量背景噪声,导致误检上升。
这个实验给我留下的教训是:在微小目标场景下,特征增强的优先级要严格遵循P3 > P4 > P5。越靠近输入层越关键,深层不是没用,而是它的收益需要谨慎评估。
5.4 AMP下某些大核卷积梯度溢出
开启AMP后,kernel size=31的LSKBlock偶尔会出现梯度溢出现象,表现形式是loss在某个step突然变成Inf,然后整轮训练报废。查了几次都指向同一个原因:FP16下大核分支的数值动态范围超出了半精度能表达的区间,导致上溢。
解法有两个。一是给LSKBlock单独设置float32计算域,不参与AMP,这是最直接的方案;二是调整AMP的loss_scale,开启动态缩放,让它自动适应大梯度场景。我最终选择了前一种,训练速度下降不到5%,但稳定性大幅提高。如果你们的batch size较大、梯度本身就不算极端,可以先试第二种。
5.5 遥感大图切片时的训练分布陷阱
遥感原始图像经常是6000×6000甚至更大,直接喂进模型一定会爆显存。我习惯切成1024×1024的patch训练,但均匀切图引入了一个新问题:有的patch里挤着几十个目标,有的patch连一个目标都没有。LSKNet在这种极端不均衡的数据分布下,容易在少数密集patch上过拟合,训练曲线看着很好,验证集却一塌糊涂。
解决办法是按目标密度做重采样。先统计每个patch里的目标数量,目标密度低的patch提高采样权重,目标过密的patch适当降权,让每个batch里的目标分布更均衡。这个方法对一般YOLO系列同样有效,但加了LSKNet之后会感觉更明显,因为模块本身就是为了捕捉上下文而设计的,样本均衡性直接影响权重学习质量。
6. 在LSKNet基础上还能怎么做
6.1 与检测头协同改造
主干加了LSKNet之后,特征分布跟原来不一样了,如果检测头还是简单的分类回归结构,提取到的空间选择信息容易在最后一层被压缩掉。我在项目中试过在检测头里加一个轻量注意力模块,先把分类分支和回归分支的特征做一次融合,再输出预测。这个“主干做空间选择、检测头做特征融合”的组合,在LSKNet收益的基础上又稳定提升了0.6到0.8个点,代价是推理速度下降大概5%。
6.2 多尺度特征融合的配合
LSKNet改变了主干每层特征图的感受野结构,默认的特征融合结构在对不同层特征做相加时,没有考虑各层特征的重要性差异。可换成带可学习权重的融合方式,简单说就是在融合时对P3、P4、P5的特征分别乘一个可学习系数,让网络自己决定每层贡献多少。
不需要完全替换整个特征融合结构,只把上采样和下采样的加和路径里插入一个可学习的scale因子,就能在遥感微小目标场景下看到边缘细节保持得更好,小目标漏检率降低。
6.3 数据增强与推理阶段技巧
遥感微小目标还有两个便宜好用的“涨点外挂”,与LSKNet完全不冲突:
- 训练阶段使用Cutout和随机旋转增强。遥感目标姿态多样,旋转增强尤其管用,几乎零成本;
- 推理阶段使用多尺度测试和翻转测试,能够稳定提升mAP 0.5到1个点。如果原始图像非常大,配合切片推理,也就是把大图切成小patch分别检测再合并结果,效果会更明显。
我个人建议先做路径B稳一手,把LSKNet带来的收益吃透,再考虑叠加其他trick。对大多数项目来说,少踩坑、能稳定上线,比一味堆涨点方法重要得多。最后分享一个小技巧:训练结束后,把LSKAttention模块的空间选择权重导出来,转成热力图叠加到原图上。你会直观地看到网络到底在“看”目标边缘还是背景全局上下文,这对向同事解释方案价值、写技术报告都特别有帮助。