💡💡💡问题点:注意到红外小目标图像中目标与背景之间存在极大的不平衡,这使得模型更加关注背景特征而不是目标特征
💡💡💡解决对策:提出了一种新的自适应阈值焦点损失函数,该函数将目标和背景解耦,并利用自适应机制来调整损失权重,迫使模型将更多的注意力分配给目标特征。
💡💡💡在红外小目标数据集上暴力涨点,涨点近三个点。

收录
YOLOv8原创自研
💡💡💡全网独家首发创新(原创),适合paper !!!
💡💡💡 2024年计算机视觉顶会创新点适用于Yolov5、Yolov7、Yolov8等各个Yolo系列,专栏文章提供每一步步骤和源码,轻松带你上手魔改网络 !!!
💡💡💡重点:通过本专栏的阅读,后续你也可以设计魔改网络,在网络不同位置(Backbone、head、detect、loss等)进行魔改,实现创新!!!
1.原理介绍

论文: https://arxiv.org/pdf/2307.14723.pdf
摘要:单帧红外小目标检测被认为是一项具有挑战性的任务,由于目标与背景的极度不平衡,边界盒回归对红外小目标极其敏感,目标信息容易在高层语义层丢失。在本文中,我们提出了一个增强特征学习网络(EFLNet)来解决这些问题。首先,我们注意到红外图像中目标与背景之间存在极大的不平衡,这使得模型更加关注背景特征而不是目标特征。为了解决这一问题,我们提出了一种新的自适应阈值焦点损失(ATFL)函数,该函数将目标和背景解耦,并利用自适应机制来调整损失权重,迫使模型将更多的注意力分配给目标特征。其次,我们引入归一化高斯Wasserstein距离(NWD)来缓解收敛带来的困难利用边界盒回归对红外小目标的极高灵敏度。最后,我们将动态头部机制整合到网络中,以实现对每个语义层的相对重要性的自适应学习。实验结果表明,与目前最先进的基于深度学习的方法相比,我们的方法在红外小目标的检测性能上取得了更好的性能。

红外图像主要由背景组成,只有一小部分被目标占据,如图2所示。因此,在训练过程中学习背景的特征比学习目标的特征更容易。背景可以看作是简单样本,目标可以看作是难样本。然而,即使是良好的学习背景,在训练过程中仍然会产生损失。实际上,占据红外图像主体部分的背景样本主导了梯度更新方向,淹没了目标信息。为了解决这个问题,我们提出了一个新的ATFL函数。首先,利用阈值设置将易识别的背景与难识别的目标解耦;其次,通过强化与目标相关的损失,减轻与背景相关的损失,我们迫使模型将更多的注意力分配到目标特征上,从而缓解目标与背景之间的不平衡。最后,将自适应设计应用于超参数,以减少调整超参数所带来的时间消耗。

我们提出了一种基于设定阈值解耦目标和背景的ATFL。根据预测概率值自适应调整损失值,旨在提高红外小目标的检测性能。


实验结果

2. 如何将入到YOLOv8
2.1 修改utils/loss.py
1)加入以下代码
class AdaptiveThresholdFocalLoss(nn.Module):
# Wraps focal loss around existing loss_fcn(), i.e. criteria = FocalLoss(nn.BCEWithLogitsLoss(), gamma=1.5)
def __init__(self, loss_fcn, gamma=1.5, alpha=0.25):
super(AdaptiveThresholdFocalLoss, self).__init__()
self.loss_fcn = loss_fcn # must be nn.BCEWithLogitsLoss()
self.gamma = gamma
self.alpha = alpha
self.reduction = loss_fcn.reduction
self.loss_fcn.reduction = 'none' # required to apply FL to each element
def forward(self, pred, true):
loss = self.loss_fcn(pred, true)
pred_prob = torch.sigmoid(pred)
p_t = true * pred_prob + (1 - true) * (1 - pred_prob) #得出预测概率
p_t = torch.cuda.FloatTensor(p_t) #将张量转化为pytorch张量,使其在pytorch中可以进行张量运算
mean_pt = p_t.mean()
p_t_list = []
p_t_list.append(mean_pt)
p_t_old = sum(p_t_list) / len(p_t_list)
p_t_new = 0.05 * p_t_old + 0.95 * mean_pt
# gamma =2
gamma = -torch.log(p_t_new)
# 处理大于0.5的元素
p_t_high = torch.where(p_t > 0.5, (1.000001 - p_t)**gamma, torch.zeros_like(p_t))
# 处理小于0.5的元素
p_t_low = torch.where(p_t <= 0.5, (1.5- p_t)**(-torch.log(p_t)), torch.zeros_like(p_t)) # # 将两部分结果相加
modulating_factor = p_t_high + p_t_low
loss *= modulating_factor
if self.reduction == 'mean':
return loss.mean()
elif self.reduction == 'sum':
return loss.sum()
else: # 'none'
return loss2)修改class v8DetectionLoss:
原始为:
self.bce = nn.BCEWithLogitsLoss(reduction="none")
修改为
#self.bce = nn.BCEWithLogitsLoss(reduction="none")
self.bce = AdaptiveThresholdFocalLoss(nn.BCEWithLogitsLoss(reduction="none"))
3)修改def __call__(self, preds, batch):
原始为:
loss[1] = self.bce(pred_scores, target_scores.to(dtype)).sum() / target_scores_sum # BCE
修改为
#loss[1] = self.bce(pred_scores, target_scores.to(dtype)).sum() / target_scores_sum # BCE
loss[1] = self.bce(pred_scores, target_scores).sum() / target_scores_sum # BCE