024、数据增强改进(三):针对小目标与遮挡目标的增强技巧
从产线检测的漏报说起上周在工厂现场调试产线主管指着监控屏问我“为什么远处传送带上的小零件经常漏检明明在近处都能识别。”我拉出验证集的统计一看小目标像素面积小于32×32的召回率比常规目标低了近三十个百分点。这不仅是模型能力问题更是数据层面的“先天不足”——训练集中小目标样本不足且遮挡情况模拟不够真实。在目标检测任务中小目标和遮挡目标一直是性能提升的瓶颈。YOLO系列虽然速度快但在处理这类目标时容易丢失细节或误判。今天我们就深入数据增强层聊聊如何通过“数据手术”提升模型对这两类难例的感知能力。小目标增强不只是简单缩放很多人一提到小目标增强第一反应就是“把图片缩小”让原本的小目标在缩小后的图中显得更大。这个思路没错但太粗暴。直接全局缩小图像会导致背景信息压缩过度且目标尺寸分布失真。我们需要的是一种更精细的调控。复制-粘贴增强Copy-Paste Augmentation这是目前业界验证有效的策略之一思路很简单从训练集中随机抽取一些小目标实例粘贴到其他训练图片的合理位置上。注意这里的关键是“合理位置”——要避免粘贴到不合理区域比如把螺丝钉贴到天空上同时要同步更新bbox和分割掩码如果做分割。defcopy_paste_small_objects(img,targets,small_obj_thresh32): 对小目标进行复制粘贴增强 img: 原始图像 targets: 标注信息格式为[N, (cls, x1, y1, x2, y2)] small_obj_thresh: 小目标阈值这里指最大边长 h,wimg.shape[:2]small_masks[]new_targets[]# 找出小目标fortintargets:_,x1,y1,x2,y2tifmax(x2-x1,y2-y1)small_obj_thresh:# 这里踩过坑一定要深拷贝否则原图会被破坏obj_patchimg[int(y1):int(y2),int(x1):int(x2)].copy()masknp.ones(obj_patch.shape[:2],dtypenp.uint8)small_masks.append((obj_patch,mask,(x1,y1)))# 随机选择几个小目标粘贴到新位置forobj_patch,mask,_inrandom.sample(small_masks,kmin(3,len(small_masks))):# 随机生成粘贴位置这里简单实现实际要考虑遮挡关系和场景合理性new_xrandom.randint(0,w-obj_patch.shape[1])new_yrandom.randint(0,h-obj_patch.shape[0])# 混合粘贴简单alpha混合roiimg[new_y:new_yobj_patch.shape[0],new_x:new_xobj_patch.shape[1]]blendedcv2.addWeighted(roi,0.7,obj_patch,0.3,0)img[new_y:new_yobj_patch.shape[0],new_x:new_xobj_patch.shape[1]]blended# 更新标注new_targets.append([0,new_x,new_y,new_xobj_patch.shape[1],new_yobj_patch.shape[0]])returnimg,np.vstack([targets,new_targets])ifnew_targetselsetargets注意实际部署时建议用更成熟的库如albumentations的实现自己写容易漏掉边缘情况。粘贴时考虑光照一致性、阴影匹配会更逼真。多尺度训练Multi-Scale Training的变体YOLO本身支持多尺度训练但我们可以针对小目标做调整。不是简单随机缩放整图而是对小目标密集的图片进行差异化缩放。比如检测到某张图小目标数量超过阈值就以较高概率触发放大操作比如缩放到原图的1.5倍然后裁剪出原始尺寸的区域进行训练。这样相当于给小目标做了“特写镜头”。# 在dataloader中的简化示例ifrandom.random()0.3andcount_small_objects(targets)5:# 小目标密集进行放大裁剪scalerandom.uniform(1.2,1.8)new_h,new_wint(h*scale),int(w*scale)imgcv2.resize(img,(new_w,new_h))# 随机裁剪回原尺寸start_xrandom.randint(0,new_w-w)start_yrandom.randint(0,new_h-h)imgimg[start_y:start_yh,start_x:start_xw]# 同步调整bbox坐标代码略遮挡目标增强模拟真实世界的残缺遮挡是目标检测的老大难问题。现场环境中目标被部分遮挡是常态。训练集如果都是“完整亮相”的目标模型遇到遮挡就懵了。随机擦除Random Erasing与网格遮挡Grid Mask随机擦除最早在分类任务中提出后来在检测中也很好用。思路是在图像中随机选择一个矩形区域用随机值或均值填充模拟遮挡。但纯随机矩形可能不太自然更推荐使用Grid Mask或其变种——生成网格状的遮挡模式更接近树枝、栅栏等现实遮挡物。defgrid_mask_augment(img,prob0.5,grid_size_range(10,30),ratio_range(0.3,0.7)): 网格遮挡增强 grid_size_range: 网格单元大小范围 ratio_range: 遮挡比例范围每个单元内遮挡部分的占比 ifrandom.random()prob:returnimg h,wimg.shape[:2]grid_sizerandom.randint(*grid_size_range)ratiorandom.uniform(*ratio_range)masknp.ones((h,w),dtypenp.uint8)foriinrange(0,h,grid_size):forjinrange(0,w,grid_size):# 每个网格单元内随机生成一个小矩形遮挡cell_hmin(grid_size,h-i)cell_wmin(grid_size,w-j)ifcell_h0orcell_w0:continue# 计算遮挡区域大小erase_hint(cell_h*ratio)erase_wint(cell_w*ratio)iferase_h0orerase_w0:continue# 随机位置erase_xjrandom.randint(0,cell_w-erase_w)erase_yirandom.randint(0,cell_h-erase_h)# 填充灰色或随机噪声这里用灰色img[erase_y:erase_yerase_h,erase_x:erase_xerase_w]np.random.randint(100,180,(erase_h,erase_w,3))returnimg目标间遮挡合成更高级的做法是模拟目标之间的相互遮挡。可以从同一批数据中随机选取两个实例计算其空间位置将一个实例部分覆盖到另一个上同时调整被遮挡目标的bbox。这需要更精细的像素级操作但效果最接近真实场景。# 概念性代码实际实现需要考虑遮挡顺序、光照融合等definter_object_occlusion(img,targets):iflen(targets)2:returnimg,targets# 随机选择两个不同目标idx1,idx2random.sample(range(len(targets)),2)t1,t2targets[idx1],targets[idx2]# 计算重叠区域这里简化实际应计算实例分割掩码x_overlapmax(0,min(t1[3],t2[3])-max(t1[1],t2[1]))y_overlapmax(0,min(t1[4],t2[4])-max(t1[2],t2[2]))ifx_overlap0andy_overlap0:# 模拟t2遮挡t1的部分区域overlap_areaimg[int(max(t1[2],t2[2])):int(min(t1[4],t2[4])),int(max(t1[1],t2[1])):int(min(t1[3],t2[3]))]# 用t2区域的颜色或纹理填充这里简单用噪声overlap_area[:]np.random.randint(0,255,overlap_area.shape)# 注意这里bbox没有调整实际应该根据遮挡程度调整t1的bbox或添加难度权重returnimg,targets融合策略与调参经验单独使用某一种增强可能效果有限甚至带来副作用。我的经验是小目标增强容易导致假阳性增多因为背景区域被误认为小目标的概率上升。解决办法是同时加强负样本挖掘或者在损失函数中调整小目标的权重。YOLOv11的anchor设计本身对小目标不太友好建议在数据增强后重新聚类anchor特别是增加小尺度anchor的数量。遮挡增强过度会破坏目标可识别性模型可能学不到完整特征。建议控制遮挡比例在20%~40%之间并且优先遮挡目标边缘区域而非中心关键特征区域比如人脸的眼睛、车辆的轮胎。可以结合关键点检测先验知识避免遮挡重要语义部位。增强不是越多越好。我通常采用渐进式策略先在小规模数据上测试各种增强组合观察验证集mAP的变化曲线。如果小目标AP提升但大目标AP下降说明增强强度需要调整。最终线上部署时我会保留23种最有效的增强并控制其触发概率在0.30.5之间。部署前的最后检查数据增强在训练时是“虚拟”的但部署环境是真实的。有个坑我踩过增强时模拟的遮挡模式如整齐网格和真实场景不规则树枝差异太大导致模型过拟合到增强模式。建议去现场拍一些真实遮挡、小目标的负样本混入训练集。增强参数如遮挡形状、大小尽量随机化避免模式固定。在验证集上单独统计小目标和遮挡目标的精度而不是只看整体mAP。最后说个反直觉的点有时候数据增强解决不了的问题恰恰是模型架构的瓶颈。如果尝试了各种增强技巧后小目标AP仍然上不去可能需要回头审视backbone的下采样率是否过高或者检测头是否缺乏细粒度特征融合。数据增强是“喂好数据”但模型也得“消化得了”才行。写在后面调试数据增强像老中医开方子得根据模型的具体“体质”调整。我的习惯是每轮训练后可视化增强后的样本和模型预测结果特别关注那些难例——看看是数据没喂到位还是模型学偏了。记住增强的目标不是让训练集变得“花里胡哨”而是让模型提前见识它在战场上会遇到的所有挑战。