# YOLOX改进之损失函数修改（上）

（1）置信度预测损失更换：二元交叉熵损失替换为 FocalLoss或者 VariFocalLoss

（2）定位损失更换：IOU损失替换为GIOU、CIOU、EIOU以及 a-IOU系列

1、置信度预测损失更换之 FocalLoss（不需要创建新的py文件）

（1）首先找到置信度预测损失计算位置loss_obj，并进行替换（位置在386-405行左右）


loss_iou = (
).sum() / num_fg

loss_obj = (
self.focal_loss(obj_preds.sigmoid().view(-1, 1), obj_targets)
).sum() / num_fg
loss_cls = (
self.bcewithlog_loss(
)
).sum() / num_fg


（2）创建focal_loss方法，放到def get_l1_target（…）之前即可，代码如下：

def focal_loss(self, pred, gt):
pos_inds = gt.eq(1).float()
neg_inds = gt.eq(0).float()
pos_loss = torch.log(pred+1e-5) * torch.pow(1 - pred, 2) * pos_inds * 0.75
neg_loss = torch.log(1 - pred+1e-5) * torch.pow(pred, 2) * neg_inds * 0.25
loss = -(pos_loss + neg_loss)
return loss


2、置信度预测损失更换之 VariFocalLoss（代码较多，所以额外创建新的py文件）

import torch.nn as nn
import torch.nn.functional as F

def reduce_loss(loss, reduction):
"""Reduce loss as specified.

Args:
loss (Tensor): Elementwise loss tensor.

reduction (str): Options are "none", "mean" and "sum".

Return:
Tensor: Reduced loss tensor.

"""
reduction_enum = F._Reduction.get_enum(reduction)

if reduction_enum == 0:
return loss
elif reduction_enum == 1:
return loss.mean()
elif reduction_enum == 2:
return loss.sum()

def weight_reduce_loss(loss, weight=None, reduction='mean', avg_factor=None):
"""Apply element-wise weight and reduce loss.

Args:
loss (Tensor): Element-wise loss.

weight (Tensor): Element-wise weights.

reduction (str): Same as built-in losses of PyTorch.

avg_factor (float): Avarage factor when computing the mean of losses.

Returns:
Tensor: Processed loss values.

"""

if weight is not None:
loss = loss * weight

if avg_factor is None:
loss = reduce_loss(loss, reduction)
else:

if reduction == 'mean':
loss = loss.sum() / avg_factor

elif reduction != 'none':
raise ValueError('avg_factor can not be used with reduction="sum"')
return loss

def varifocal_loss(pred,
target,
weight=None,
alpha=0.75,
gamma=2.0,
iou_weighted=True,
reduction='mean',
avg_factor=None):
"""Varifocal Loss _
Args:
pred (torch.Tensor): The prediction with shape (N, C), C is the
number of classes
target (torch.Tensor): The learning target of the iou-aware
classification score with shape (N, C), C is the number of classes.

weight (torch.Tensor, optional): The weight of loss for each
prediction. Defaults to None.

alpha (float, optional): A balance factor for the negative part of
Varifocal Loss, which is different from the alpha of Focal Loss.

Defaults to 0.75.

gamma (float, optional): The gamma for calculating the modulating
factor. Defaults to 2.0.

iou_weighted (bool, optional): Whether to weight the loss of the
positive example with the iou target. Defaults to True.

reduction (str, optional): The method used to reduce the loss into
a scalar. Defaults to 'mean'. Options are "none", "mean" and
"sum".

avg_factor (int, optional): Average factor that is used to average
the loss. Defaults to None.

"""

assert pred.size() == target.size()
pred_sigmoid = pred.sigmoid()
target = target.type_as(pred)
if iou_weighted:
focal_weight = target * (target > 0.0).float() + \
alpha * (pred_sigmoid - target).abs().pow(gamma) * \
(target  0.0).float()
else:
focal_weight = (target > 0.0).float() + \
alpha * (pred_sigmoid - target).abs().pow(gamma) * \
(target  0.0).float()
loss = F.binary_cross_entropy_with_logits(
pred, target, reduction='none') * focal_weight
loss = weight_reduce_loss(loss, weight, reduction, avg_factor)
return loss

class VarifocalLoss(nn.Module):

def __init__(self,
use_sigmoid=True,
alpha=0.75,
gamma=2.0,
iou_weighted=True,
reduction='mean',
loss_weight=1.0):
"""Varifocal Loss _
Args:
use_sigmoid (bool, optional): Whether the prediction is
used for sigmoid or softmax. Defaults to True.

alpha (float, optional): A balance factor for the negative part of
Varifocal Loss, which is different from the alpha of Focal
Loss. Defaults to 0.75.

gamma (float, optional): The gamma for calculating the modulating
factor. Defaults to 2.0.

iou_weighted (bool, optional): Whether to weight the loss of the
positive examples with the iou target. Defaults to True.

reduction (str, optional): The method used to reduce the loss into
a scalar. Defaults to 'mean'. Options are "none", "mean" and
"sum".

loss_weight (float, optional): Weight of loss. Defaults to 1.0.

"""
super(VarifocalLoss, self).__init__()
assert use_sigmoid is True, \
'Only sigmoid varifocal loss supported now.'
assert alpha >= 0.0
self.use_sigmoid = use_sigmoid
self.alpha = alpha
self.gamma = gamma
self.iou_weighted = iou_weighted
self.reduction = reduction
self.loss_weight = loss_weight

def forward(self,
pred,
target,
weight=None,
avg_factor=None,
reduction_override=None):
"""Forward function.

Args:
pred (torch.Tensor): The prediction.

target (torch.Tensor): The learning target of the prediction.

weight (torch.Tensor, optional): The weight of loss for each
prediction. Defaults to None.

avg_factor (int, optional): Average factor that is used to average
the loss. Defaults to None.

reduction_override (str, optional): The reduction method used to
override the original reduction method of the loss.

Options are "none", "mean" and "sum".

Returns:
torch.Tensor: The calculated loss
"""
assert reduction_override in (None, 'none', 'mean', 'sum')
reduction = (
reduction_override if reduction_override else self.reduction)
if self.use_sigmoid:
loss_cls = self.loss_weight * varifocal_loss(
pred,
target,
weight,
alpha=self.alpha,
gamma=self.gamma,
iou_weighted=self.iou_weighted,
reduction=reduction,
avg_factor=avg_factor)
else:
raise NotImplementedError
return loss_cls


（1）导入

from .varifocalloss import VarifocalLoss


（2）在init中实例化

self.varifocal = VarifocalLoss(reduction='none')


（3）替换原有的置信度预测损失loss_obj


loss_iou = (
).sum() / num_fg

loss_obj = (self.varifocal(obj_preds.view(-1, 1), obj_targets)
).sum() / num_fg
loss_cls = (
self.bcewithlog_loss(
).sum() / num_fg


Original: https://blog.csdn.net/weixin_45679938/article/details/122343945
Author: 你的陈某某
Title: YOLOX改进之损失函数修改（上）

(0)