在上一篇博文中,我们讲解了YOLOv8
实例分割的训练过程,已将前向传播过程分析完毕,那么,接下来便是损失计算过程了。
文章目录
- 训练整体流程
- 分割整体损失函数
- 生成网格
- 前处理过程(真值转换)
- 预测框解码
- TaskAlignedAssigner方法
- Box损失
- bbox2dist方法
- DFL损失
- 掩码损失(分割损失)
- single_mask_loss方法
- 模型参数更新
- 总结
训练整体流程
获得预测结果与真值后,即可计算损失。
整体流程如下:
这里的batch我们使用的是单张图像,所以在计算损失时,真值我们可以直接选出。
详细结构图如下:
这里我们主要是对TaskAlignedAssigner样本匹配策略进行详细解释
预测结果preds
如下:
真值batch
如下:
分割整体损失函数
v8SegmentationLoss
的计算过程如下,从最终的结果来看,其计算了四个损失,分别是目标预测框损失、mask
损失、类别损失以及DEL
损失,博主已将每段代码的结果标注在对应的代码位置。同时,在损失计算过程中不可避免的需要使用其他方法,博主将一些较为重要的方法也罗列出来了。
生成网格
make_anchor
方法,其作用是利用特征图生成网格的形式来创建预测框,这种产方式自YOLOv3
起便一直沿用。
这里生成网格使用的方法为:x,y=torch.meshgrid(a,b)
torch.meshgrid(a,b)的功能是生成网格,可以用于生成坐标。函数输入两个数据类型相同的一维张量,两个输出张量的行数为第一个输入张量的元素个数,列数为第二个输入张量的元素个数,当两个输入张量数据类型不同或维度不是一维时会报错。
其中第一个输出张量填充第一个输入张量中的元素,各行元素相同;第二个输出张量填充第二个输入张量中的元素,各列元素相同。a
tensor([1, 2, 3, 4, 5, 6])b
tensor([ 7, 8, 9, 10])x
tensor([[1, 1, 1, 1],
[2, 2, 2, 2],
[3, 3, 3, 3],
[4, 4, 4, 4],
[5, 5, 5, 5],
[6, 6, 6, 6]])y
tensor([[4, 5, 6],
[4, 5, 6],
[4, 5, 6],
[4, 5, 6]])
我们以第一层特征图生成的网格为例,得到的sy
,sx
如下:
生成的anchor-point
即为两者合并的结果,维度为(6400,2)
最终,将三个尺度的特征图产生的anchor
合并,得到:
前处理过程(真值转换)
这个方法是将target
进行处理,初始的target
为(22,6)
转换为(batch,最大目标数量,5)
5即类别id+xywh
,当然最后还会将xywh
转换为坐标形式
随后拆分得到cls
与box
:
预测框解码
bbox_decode
函数的目的是从预测的物体边界框坐标分布(pred_dist)和参考点(anchor_points)解码出实际的边界框坐标xyxy。(此时pred_dist
为4,8400,64
)进行解码,得到(4,8400,4)
matmul方法为矩阵乘法,pred_dist在matmul之前, shape为[b, a, 4, 16],
self.proj的shape为[16], 最终的pred_dist的shape为[b, a, 4] 如果不理解可以直接使用 a =
torch.ones((1, 3, 4, 16)), 与b=torch.rand(16)进行matmul
TaskAlignedAssigner方法
该方法用于分配正负样本,即哪些锚点负责预测那个目标,没有分配到目标的锚点则认为预测的是背景。
TaskAlignedAssigner 的匹配策略简单总结为:根据分类与回归的分数加权的分数选择正样本。
- 计算真实框和预测框的匹配程度(分类与回归的分数加权的分数)。
其中,s
是预测类别分值,u
是预测框和真实框的ciou
值,α
和 β
为权重超参数,两者相乘就可以衡量匹配程度,当分类的分值越高且ciou越高时,align_metric
的值就越接近于1,此时预测框就与真实框越匹配,就越符合正样本的标准。
- 对于每个真实框,直接对
align_metric
匹配程度排序,选取topK
个预测框作为正样本。 - 对一个预测框与多个真实框匹配测情况进行处理,保留
ciou
值最大的真实框。
得到的结果如下:
- target_labels, 这里用_代替了:形状为 [batch_size, num_anchors],包含每个锚点的目标标签
- target_bboxes:形状为 [batch_size, num_anchors, 4],包含每个锚点的目标边界框
- target_scores:形状为 [batch_size, num_anchors, num_classes],包含每个锚点的目标得分。
- fg_mask:形状为 [batch_size, num_anchors],标识哪些锚点是前景(即有效的目标, 正样本)。
fg_mask作用:标识哪些锚点是前景(正样本),哪些是背景(负样本)。
正样本:锚点被分配给一个真实的目标,表示这个锚点负责检测这个目标。
负样本:锚点未被分配给任何目标,表示这个锚点不负责检测任何目标 - target_gt_idx, 这里用_代替了:形状为 [batch_size, num_anchors],包含每个锚点对应的真实目标索引。
Box损失
Box
损失中包含IOU
损失和DEL
损失
传入的参数如下:
这里的pred_bboxes
是预测的检测框,pred_dist
则是用于DEL
损失计算,
bbox2dist方法
在求dfl
损失时,有个bbox2dist
方法,传入的参数如下:
其将中心点坐标anchor_points
与左上、右下坐标做差并重写拼接,同时将值限制在0到15(准确的说是14.99)以内
作用:限幅。将input
的值限制在[min, max]
之间,并返回结果。
举例如下:
那么,为何要这样做呢,我们知道将中心点坐标与左上右下的坐标(真值坐标)做差后得到的是宽高,将其限制在15以内就是说明其只负责30x30
以内的目标,而我们的特征图此时最大的为80x80
,这其实已将不小了,即认为图像中的目标应当都在这个范围内(大多数)毕竟其作用在DEL
损失中。
经过该方法处理后得到target_ltrb
,其维度仍为(4,8400,4)这个是真值的数据
随后便是计算DEL
损失了,其定义如下:
DFL损失
DFL
,全称Distribution Focal Loss
(分布焦点损失),很多人一听到Focal Loss
就立马想到分类,这没错,但DFL
却是用在边框回归中。这个损失用于求中心点坐标到上下左右四条边的距离。
其中,y为真值坐标,yi与yi+1是预测出的距离值,S是其对应的概率
由于DEL模块输出的数据为(4,8400,64)这个64=4x16,4即4条边,16则可认为是距离,这是一个分布,通过softmax函数求出的是概率,即距离为1到16的概率。
具体过程如下:
1、 模型先生成一个reg_max(默认为16)的概率统计分布,其对应得是{ 0 , 1 , . . . , 7 , 8 , . . . , 14 , 15 } ,该值就是模型预测出来的anchor points到bbox边的距离。假设模型预测出的结果pred_dist
{ 0.01 , 0.05 , . . . , 0.12 , 0.23 , . . . , 0.01 , 0.34 } 也就是anchor points
到bbox
边的距离为0的概率是0.01,距离为15的概率为0.34。
2、 然后使用上面介绍的检测头代码中的self.dfl
求出anchor points
到bbox
的距离的期望y
就是模型预测的最终的anchor points
到bbox
边的距离,这个期望最大是15
,也就是说模型预测出的anchor points
到bbox
边的距离最大是15
该距离的求法如下:
那么在预测过程中,这里是直接预测了,不需要再计算多个分布值了
而在训练时,可以按照如下理解:
如下图所示:
调用DELoss
方法,传入的参数如下:
fg_mask
代表在TaskAlignedAssigne
r方法中匹配上的正样本,其维度为(4,8400),其值为False
或True
因此其会提取pred_dit(4,8400,64)
与target_ltrb(4,8400,4)
对应坐标内的数据,即在(4,8400)
这个维度提取,由于fg_mask
共有209
个,因此取出的值有209
个,同时通过view
对pred_dist
进行维度转换,即209x4=836
随后开始DFLoss
计算:
掩码损失(分割损失)
将masks从batch中取出,并
匹配上的锚点(正样本)
掩膜(真值)这个值是经过处理下采样后
每个batch
中真值对应的类别
目标所属batch
真值预测框,这个是处理后的,为方便计算(转为了4,8400维)
预测的mask
single_mask_loss方法
该法用于求单张图片的分割损失。传入的参数如下:
这里的40
指的是匹配上的锚点数量(四个batch的锚点数量为40,69,100,40
)。可以看到,此时的pred_mask
(预测的mask)与真值mask都已经转换为相同的维度,即(40,160,160)
这里求两者损失使用的是binary_cross_entropy_with_logits
方法,即交叉熵损失函数,这里带着_with_logits
的原因是不需要将数据传入前使用sigmoid/softmax
映射到(0,1)
之间了。
至此,完成了实例分割的损失计算
crop_mask
方法,这个方法是为了让mask
不要超界(不要超出box
)
最终的总损失如下:
模型参数更新
至此完成了损失计算过程。
随后,便是开始反向传播(这是个黑盒)
完成后,边进行模型参数的更新即可:
总结
本章梳理了预测结果与真值的损失计算过程,可以加深我们对模型训练的理解。