一、前言
loss
的计算是一个AI
工程代码的核心之一,nanodet
的损失函数与yolo v3/5
系列有很大不同,具体见Generalized Focal Loss,说实话一开始看这个损失函数博客,没看明白,后来看完代码才看懂,作者虽然简单讲了一下,但是讲的很到位,结合代码来看,一目了然。损失函数源代码较为复杂,各种调用、各种变换,看的头疼。为此整理了一份流程图,简单参考一下。
二、正文
loss
部分代码较多,全部贴出来反而会妨碍阅读,欲详细了解的,去看代码注释吧——loss函数主体代码、gfocal_loss代码。下文大致讲讲loss
的组成及相关细节。
nanodet
的loss
分为两种——GIOU loss
和gfocal_loss
,后者可细分为quality focal loss
与distribution focal loss
。其中GIOU loss
与distribution focal loss
对应模型的目标bbox
输出,quality focal loss
对应模型的目标类别输出。
1.GIOU loss
GIOU loss很常见了,预测的bbox
值与相匹配的gt bbox
标签做损失函数输入。先说说预测的bbox
值怎么来的。
class Integral(nn.Module):
def __init__(self, reg_max=16):
super(Integral, self).__init__()
self.reg_max = reg_max # 7
self.register_buffer(
"project", torch.linspace(0, self.reg_max, self.reg_max + 1) # 返回一维 tensor = [0, 1, 2, 3, ... reg_max]
)
# input x.shape = (N, 4 * (reg_max + 1))
def forward(self, x):
x = F.softmax(x.reshape(-1, self.reg_max + 1), dim=1) # softmax 之后,数据就是 (0, 1)之间了
x = F.linear(x, self.project.type_as(x)).reshape(-1, 4) # 与 self.project 做矩阵相乘,返回 (0, reg_max) 之间的数
return x
模型输出的原始bbox
值,先经softmax
变换为一组概率值。为什么用概率来表示bbox
位置呢,按照李翔大佬的说法:在复杂场景中,边界框的表示具有很强的不确定性,而用概率来衡量一个事物的不确定性,是很合适的。如下图所示,概率值与reg_max
数组做点积输出的值就是anchor
中心与bbox
某一条边的预测距离。在拿anchor
中心坐标加减前面的预测距离就得到了熟悉的bbox
预测坐标。
上图的结果为2 x 0.1 + 3 x 0.9 = 2.9
,该数值就是anchor
中心到bbox
某条边的距离。
然后就可以拿该预测bbox
值与gt bbox
值做损失函数计算了。
loss_bbox = self.loss_bbox(
pos_decode_bbox_pred, # 预测的 bbox 左上右下角点坐标, (pos_num, 4)
pos_decode_bbo