get_target(获得y_true)
获得网络预测结果
#-----------------------------------------------# # 获得网络应该有的预测结果 #-----------------------------------------------# y_true, noobj_mask, box_loss_scale = self.get_target(l, targets, scaled_anchors, in_h, in_w)
这里用到了get_target这个函数。先附上该函数的代码
def get_target(self, l, targets, anchors, in_h, in_w): # 特征图索引:0,1,2, # targets:列表形式,长度batchsize,每个元素又是5列,边界框信息和类别 # scaled_anchors:缩放到特征层的anchor # in_h, in_w:特征层大小 #-----------------------------------------------------# # 计算一共有多少张图片 #-----------------------------------------------------# bs = len(targets) #-----------------------------------------------------# # 用于选取哪些先验框不包含物体 #-----------------------------------------------------# # 创建一个(batshzie,3,13,13)全1矩阵 noobj_mask = torch.ones(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False) #-----------------------------------------------------# # 让网络更加去关注小目标 # 创建一个(batshzie,3,13,13)全0矩阵 #-----------------------------------------------------# box_loss_scale = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False) #-----------------------------------------------------# # batch_size, 3, 13, 13, 5 + num_classes # y_true用来存放ground truth 信息 #-----------------------------------------------------# y_true = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, self.bbox_attrs, requires_grad = False) for b in range(bs): if len(targets[b])==0: continue batch_target = torch.zeros_like(targets[b]) #-------------------------------------------------------# # 计算出正样本在特征层上的中心点? #-------------------------------------------------------# batch_target[:, [0,2]] = targets[b][:, [0,2]] * in_w batch_target[:, [1,3]] = targets[b][:, [1,3]] * in_h batch_target[:, 4] = targets[b][:, 4] batch_target = batch_target.cpu() #-------------------------------------------------------# # 将真实框转换一个形式 # num_true_box, 4 #-------------------------------------------------------# gt_box = torch.FloatTensor(torch.cat((torch.zeros((batch_target.size(0), 2)), batch_target[:, 2:4]), 1)) #-------------------------------------------------------# # 将先验框转换一个形式 # 9, 4 #-------------------------------------------------------# anchor_shapes = torch.FloatTensor(torch.cat((torch.zeros((len(anchors), 2)), torch.FloatTensor(anchors)), 1)) #-------------------------------------------------------# # 计算交并比 # self.calculate_iou(gt_box, anchor_shapes) = [num_true_box, 9]每一个真实框和9个先验框的重合情况 # best_ns: # [每个真实框最大的重合度max_iou, 每一个真实框最重合的先验框的序号] #-------------------------------------------------------# best_ns = torch.argmax(self.calculate_iou(gt_box, anchor_shapes), dim=-1) for t, best_n in enumerate(best_ns): if best_n not in self.anchors_mask[l]: continue #----------------------------------------# # 判断这个先验框是当前特征点的哪一个先验框 # [[6, 7, 8], [3, 4, 5], [0, 1, 2]] #----------------------------------------# k = self.anchors_mask[l].index(best_n) #----------------------------------------# # 获得真实框属于哪个网格点 # floor 返回一个新张量,包含输入input张量每个元素的floor,即取不大于元素的最大整数。 #----------------------------------------# i = torch.floor(batch_target[t, 0]).long() j = torch.floor(batch_target[t, 1]).long() #----------------------------------------# # 取出真实框的种类 #----------------------------------------# c = batch_target[t, 4].long() #----------------------------------------# # noobj_mask代表无目标的特征点 #----------------------------------------# noobj_mask[b, k, j, i] = 0 #----------------------------------------# # tx、ty代表中心调整参数的真实值 # y_true[b, k, j, i, 0] 意思是取第几各个batch的第k个锚框(每个层有三个),在特征层上第j行第i列网格点 #----------------------------------------# y_true[b, k, j, i, 0] = batch_target[t, 0] y_true[b, k, j, i, 1] = batch_target[t, 1] y_true[b, k, j, i, 2] = batch_target[t, 2] y_true[b, k, j, i, 3] = batch_target[t, 3] # 前几行这是box的信息 y_true[b, k, j, i, 4] = 1 # bbox由5个信息组成,前四个是坐标信息,第5个是Pc,是否由目标 y_true[b, k, j, i, c + 5] = 1 # c + 5指定获得的第几个类,5是前5个维度(x,y,w,h,Pc),从下个维度开始是类 #----------------------------------------# # 用于获得xywh的比例 # 大目标loss权重小,小目标loss权重大 #----------------------------------------# box_loss_scale[b, k, j, i] = batch_target[t, 2] * batch_target[t, 3] / in_w / in_h return y_true, noobj_mask, box_loss_scale
get_target函数需要传入5个参数,l【特征层索引】,targets【真实值】,anchors【已经缩放后的anchors】,in_h=in_w=19.
我们先创建一个全1的mask tensor【后面用来记录不含目标的地方】,此时的noobj_mask shape为【batch_size,3,19,19】
#-----------------------------------------------------# # 计算一共有多少张图片 #-----------------------------------------------------# bs = len(targets) #-----------------------------------------------------# # 用于选取哪些先验框不包含物体 #-----------------------------------------------------# # 创建一个(batshzie,3,13,13)全1矩阵 noobj_mask = torch.ones(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False)
在创建一个全0的tensor box_loss_scale。shape 【batch_size,3,19,19】
#-----------------------------------------------------# # 让网络更加去关注小目标 # 创建一个(batshzie,3,13,13)全0矩阵 #-----------------------------------------------------# box_loss_scale = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, requires_grad = False)
再创建一个全0的tensor,用来记录ground truth信息。shape【batch_size,3,19,19,5+num_classes】=【4,3,19,19,6】
这里再多嘴一句,上面这种shape的张量可以这样理解:有3种anchor,每种anchor都会负责一个19 * 19的cell网格,而每个cell内也都均有5+num_classes个参数需要预测。
# y_true用来存放ground truth 信息 #-----------------------------------------------------# y_true = torch.zeros(bs, len(self.anchors_mask[l]), in_h, in_w, self.bbox_attrs, requires_grad = False)
batch_target[真实值映射到特征层]
下面这段代码的是遍历每个batch,然后判断一下target[b]中是否有目标,如果有目标就创建一个与target[b]shape一样的全0张量。由于我这里target长度是4【表示4个batch】,第一个target[0]的长度为5【表示这张图像有5个标注的目标】。 target[b]的shape为【该图有几个目标,5(这个5坐标信息和类)】
for b in range(bs): if len(targets[b]) == 0: continue batch_target = torch.zeros_like(targets[b])
此刻的target[0]为:【center_x,center_y,w,h,class】。
tensor([
[0.2352, 0.1637, 0.2928, 0.3273, 0.0000],
[0.2097, 0.5896, 0.1135, 0.4490, 0.0000],
[0.8569, 0.5296, 0.1382, 0.2500, 0.0000],
[0.6867, 0.5395, 0.1201, 0.2763, 0.0000],
[0.6061, 0.1637, 0.2911, 0.3273, 0.0000]], device='cuda:0')
计算出正样本在特征层上的中心点。可以计算出我们的target对应到我们19 *19 的特征层上坐标是多少。
#-------------------------------------------------------# # 计算出正样本在特征层上的中心点? #-------------------------------------------------------# batch_target[:, [0,2]] = targets[b][:, [0,2]] * in_w batch_target[:, [1,3]] = targets[b][:, [1,3]] * in_h batch_target[:, 4] = targets[b][:, 4] batch_target = batch_target.cpu()
则现在我们得到batch_target就记录下了我们的第一个batch中ground truth坐标映射到特征层的坐标信息。
tensor([[ 4.4688, 3.1094, 5.5625, 6.2188, 0.0000],
[ 3.9844, 11.2031, 2.1562, 8.5312, 0.0000],
[16.2812, 10.0625, 2.6250, 4.7500, 0.0000],
[13.0469, 10.2500, 2.2813, 5.2500, 0.0000],
[11.5156, 3.1094, 5.5312, 6.2188, 0.0000]], device='cuda:0')
我们对上面在特征层上得到gt坐标形式稍加改变。batch_target shape为【5,5】。size(0)=5,先创建一个【5,2】的全0tensor,然后与batch_target的第2列(w)至第3列(h)进行拼接。
#-------------------------------------------------------# # 将真实框转换一个形式 # num_true_box, 4 #-------------------------------------------------------# gt_box = torch.FloatTensor(torch.cat((torch.zeros((batch_target.size(0), 2)), batch_target[:, 2:4]), 1))
此刻的gt_box:
tensor([
[0.0000, 0.0000, 5.5625, 6.2188],
[0.0000, 0.0000, 2.1562, 8.5312],
[0.0000, 0.0000, 2.6250, 4.7500],
[0.0000, 0.0000, 2.2813, 5.2500],
[0.0000, 0.0000, 5.5312, 6.2188]])
同时将先验框也转变一个形式:将一个【3,2】的全零tensor和anchors进行拼接。
#-------------------------------------------------------# # 将先验框转换一个形式 # 9, 4 #-------------------------------------------------------# anchor_shapes = torch.FloatTensor(torch.cat((torch.zeros((len(anchors), 2)), torch.FloatTensor(anchors)), 1))
此刻anchor_shape:【实际就是给anchor前面多加了两列0】
tensor([
[ 0.0000, 0.0000, 0.3750, 0.5000],
[ 0.0000, 0.0000, 0.5938, 1.1250],
[ 0.0000, 0.0000, 1.2500, 0.8750],
[ 0.0000, 0.0000, 1.1250, 2.3438],
[ 0.0000, 0.0000, 2.3750, 1.7188],
[ 0.0000, 0.0000, 2.2500, 4.5625],
[ 0.0000, 0.0000, 4.4375, 3.4375],
[ 0.0000, 0.0000, 6.0000, 7.5938],
[ 0.0000, 0.0000, 14.3438, 12.5312]])
IOU计算并获得anchor索引:
通过定义的函数计算IOU,主要传入两个参数,gt_box和anchor_shapes【前者是target映射到特征层上的gt,后者是anchor映射到特征层上缩放后的anchor】
注:这里的iou并不是预测的box和gt的。 iou的计算我这里不再写,另一篇文章有。
best_ns返回的是每个最大iou索引序号【表示哪些anchor有目标的以上】
#-------------------------------------------------------# # 计算交并比 # self.calculate_iou(gt_box, anchor_shapes) = [num_true_box, 9]每一个真实框和9个先验框的重合情况 # best_ns: # [每个真实框最大的重合度max_iou, 每一个真实框最重合的先验框的序号] #-------------------------------------------------------# best_ns = torch.argmax(self.calculate_iou(gt_box, anchor_shapes), dim=-1)
此刻得到的best_ns为,即这几个anchor预测到了目标。可以这样理解,这张图里出现了5个目标,第一个目标由7号anchor预测到了,第二个目标到第四个由5号anchor预测到了,最后一个目标由7号anchor预测到了。
tensor([7, 5, 5, 5, 7])
判断目标落在哪个特征层的哪个先验框
最初前面我们定义了一个self.anchors_mask=[[6,7,8],[3,4,5],[0,1,2]]-->对应于19,38,56特征层的anchor。通过上面的索引也就知道了,出现的这5个目标分别落在了19 * 19特征层,38*38,38*38,38*38,38*38,19 * 19上。
f
or t, best_n in enumerate(best_ns): if best_n not in self.anchors_mask[l]: continue #----------------------------------------# # 判断这个先验框是当前特征点的哪一个先验框 # [[6, 7, 8], [3, 4, 5], [0, 1, 2]] #----------------------------------------# k = self.anchors_mask[l].index(best_n)
通过for循环对上面的bast_ns进行遍历,先确定一下我们一个best_n到底在不在anchors_mask上。
因为此时l为0,对应的19 * 19的特征层,所以anchors_mask[0] = [6,7,8],则k=1.
通过上面的操作,我们知道了当前目标应该由几号anchor进行预测,但还不知道当前的gt属于19 * 19中的哪个cell,所以我们现在需要再确定一下gt落在了哪个cell。
方法也很简单,前面我们通过batch_target获得了gt在当前特征层上的box坐标,那么我们仅需要获得这些box的(center_x,center_y)即可,并对这些中心点坐标取整就可以知道目标落在哪个cell。
#----------------------------------------# # 获得真实框属于哪个网格点 # floor 返回一个新张量,包含输入input张量每个元素的floor,即取不大于元素的最大整数。 #----------------------------------------# i = torch.floor(batch_target[t, 0]).long() j = torch.floor(batch_target[t, 1]).long()
得到i和j如下,说明我这个目标的中心点落在了19 * 19 的第4行第3列的cell处。
i=4;
j=3;
然后我们还可以获得落在该cell处的类是什么。
#----------------------------------------# # 取出真实框的种类 #----------------------------------------# c = batch_target[t, 4].long()
是否还记得我们前面定义了一个全1的tensor noobj_mask。此刻我们可以获得所有无目标的地方,或者说有目标的地方【1表示无目标,0表示有目标,当然你反过来也可以】。此时由于是第一次的遍历,b=0,k=1,j=3.i=4,表示第一个batch,中第k anchor,3,4有目标。
#----------------------------------------# # noobj_mask代表无目标的特征点 #----------------------------------------# noobj_mask[b, k, j, i] = 0
前面我们还定义了一个y_true的全0 tensor用来记录gt,shape是【batch_size,3,19,19,5+num_classes】。然后我们记录下当前目标box坐标信息。前4列是box信息,然后又看到y_true[...,4]=1表示有目标,后面c+5表示当前为获得第几个类。
#----------------------------------------# # tx、ty代表中心调整参数的真实值 # y_true[b, k, j, i, 0] 意思是取第几各个batch的第k个锚框(每个层有三个),在特征层上第j行第i列网格点 #----------------------------------------# y_true[b, k, j, i, 0] = batch_target[t, 0] y_true[b, k, j, i, 1] = batch_target[t, 1] y_true[b, k, j, i, 2] = batch_target[t, 2] y_true[b, k, j, i, 3] = batch_target[t, 3] # 前几行这是box的信息 y_true[b, k, j, i, 4] = 1 # bbox由5个信息组成,前四个是坐标信息,第5个是Pc,是否由目标 y_true[b, k, j, i, c + 5] = 1 # c + 5指定获得的第几个类,5是前5个维度(x,y,w,h,Pc),从下个维度开始是类
此时y_true[0,1,3,4,:]:这就表示第一张图(batch 0),设置的第二个anchor(k=1),在19 *19 的特征层的3行4列处有目标设置为1,该类对应为第1个类。
tensor([4.4688, 3.1094, 5.5625, 6.2188, 1.0000, 1.0000])
batch_target存放着当前batch[当前图像]所有目标的box信息和类信息,那肯定每个目标的box有大有小,小目标的box面积就小,大目标的box面积就大,那么我们就可以得到每个目标的box面积,与当前特征层(19 * 19)面积做个比值,这个值是什么意思呢?我们就可以得到大小目标占当前特征层的比例值。
#----------------------------------------# # 用于获得xywh的比例 # 大目标loss权重小,小目标loss权重大 #----------------------------------------# box_loss_scale[b, k, j, i] = batch_target[t, 2] * batch_target[t, 3] / in_w / in_h