通过MXnet详细了解YOLOv2的算法以及其运算过程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaomifanhxx/article/details/81509733

YOLO算法在object_detection领域中算是比较有意思的分支,2017年CVPR上的YOLOv2对原来的YOLO算法进行了提升,本次通过对Mxnet来对YOLOv2的算法进行详细的了解。

1.模型加载

模型加载中,主要是使用pre-training,将整个模型的除最后两层的参数加载到模型中,然后通过将要训练的类别以及对应的初始anchor输入,得到predict_layer,将predict_layer加载进去即可得到整个的网络结构。

2.训练代码

    首先初始化一个训练器,然后训练的循环,在训练初始先将几个Loss值重置为,然后for i, batch in enumerate(train_data)每次循环都读取一个batch的数据。x = net(x)是数据从输入到输出预测结果,比如当anchor数量为2、输入图像大小为256*256,batch size为32时,该行代码的输入是32*3*256*256,输出是32*14*16*16,其中14是2*(2+1+4),括号中的三个值分别表示类别数、score和坐标信息。output, cls_pred, score, xywh = yolo2_forward(x, 2, scales)一行调用yolo2_forward函数将net输出结果进行处理,如(2.1所示)。tid, tscore, tbox, sample_weight = yolo2_target(score, xywh, y, scales, thresh=0.5)一行是调用yolo2_target函数得到模型训练目标相关的信息,后面会详细介绍该函数。如(2.2所示)可以看出在YOLO算法中每个object都是由这个object的中心所在的grid cell中的一个box来预测的。score_weight是计算正负样本在回传损失时候的权重,这里nd.where函数的第一个输入矩阵的一些位置的数值满足不等式,那么对应位置的数值就用第二个输入来替代,相反就用第三个输入来替代,最后得到的这个score_weight,除了和真实框的IOU最大的box的权重是positive_weight以外,其他都是negative_weight。 loss2 = l1_loss(score, tscore, score_weight)一行是计算score的损失,这个score也就是我们常见的显示在框上的置信度,范围是0到1的小数。loss3 = l1_loss(xywh, tbox, sample_weight * box_weight)一行是计算box回归的损失,这里sample_weight*box_weight也是只回传和真实框的IOU最大的box的损失,而乘以box_weight是为了增加loss3在中loss中的权重。最后回传的loss是这3个loss的和。trainer.step(batch_size)是更新网络参数,之所以要输入batch_size,是因为梯度要归一化成1/batch_size。cls_loss.update(loss1)是更新loss数值,obj_loss和box_loss同理,这里的3个值都只是为了打印在显示界面上,和回传的损失没关系

2.1

yolo2_forward函数用来将网络输出进行整理和转换。以这份代码的网络结构以及输入尺寸是3*256*256,batch_size=32为例,yolo2_forward函数的输入x是32*14*16*16。stride = num_class + 5这里的5是一个score加上四个坐标相关的值。x = x.transpose((0, 2, 3, 1))是将输出channel移到最后一个维度,然后通过x = x.reshape((0, 0, 0, -1, stride))得到5维的输出,前面3维不变,分别是batch size,weight,height,第4维是anchor的数量,第5维就是每个anchor对应的参数(2个类别数+1个score+4个坐标值),所以得到的x是32*16*16*2*7。cls_pred = x.slice_axis(begin=0, end=num_class, axis=-1)是取x的最后一维的前num_class个矩阵(这里是2)作为类别预测结果。 score_pred = x.slice_axis(begin=num_class, end=num_class + 1, axis=-1)是取x的最后一维的接下来1个矩阵作为score的预测结果。 xy_pred = x.slice_axis(begin=num_class + 1, end=num_class + 3, axis=-1)是取x的最后一维的再接下来的2个矩阵作为box的中心点坐标预测结果。 wh = x.slice_axis(begin=num_class + 3, end=num_class + 5, axis=-1)是取x的最后一维的再接下来的2个矩阵作为box的宽高预测结果。这样长度为7的最后一维就分清楚了。这里score = nd.sigmoid(score_pred)和 xy = nd.sigmoid(xy_pred)都是做归一化,前者是因为score的范围在0到1之间,后者是因为要用到grid cell的相对坐标,所以需要0到1范围(可以看原文Figure3的bx和by计算,这里模型预测得到的xy对应Figure3中的tx和ty)。transform_center是用来将每个grid cell里面的相对坐标转换成图片上的相对坐标。transform_size函数是将模型输出的宽高处理成实际的宽高。cid是预测的每个box的类别。left、top、right、bottom是预测的box的边界。

transform_center函数是用来将每个grid cell里面的相对坐标转换成图片上的相对坐标。首先输入xy是32*16*16*2*2大小,那么xy[0,1,1,0,:]就表示第一个输入的16*16的feature map上的(1,1)位置的第0个anchor的weight和height,feature map上的每个点代表一个grid cell,这个weight和height就是这个grid cell中某个点相对于grid cell的左上角的距离,如果weight=height=1,那么这个点就是grid cell的右下角点。offset_y是32*16*16*2*1大小,其中16*16是第一行为0,第二行为1…最后一行为15的二维矩阵,其他维度都是直接broadcast过去的,offset_x同理。这样在执行x + offset_x操作时,对于x[b,h,2,n,0]就是加上2,x[b,h,4,n,0]就是加上4。最后除以w或者除以h也是归一化的操作,使得最后得到的x和y范围在0到1之间。因此这个函数的作用就是实现论文中Figure3的加号这一步。

transform_size函数和transform_center函数类似。实现的是论文中Figure3的这一步(如下图公式)。输入wh对应tw和th。aw和ah就是box的宽高信息。

2.2

yolo2_target函数构造模型训练目标。这里输入labels就是ground truth,尺寸是32*1*5,1表示只有1个object,5包含1个class标签和4个坐标信息。for b in range(output.shape[0])是遍历batch中的每个输入,label是k*5大小的numpy array,k就是object数量,一般正常的object标签都是大于0的,所以这里valid_label是为了过滤掉那些错误的标签。输入scores的尺寸中n表示anchor的数量,h和w针对输入图像是256*256的情况分别是16和16。for l in valid_label就遍历一张图中所有有效的object标注信息,因为标注数据的坐标是采取框的左上角和右下角坐标(还是相对坐标,也就是值在0到1),所以通过简单的加减可得到gx、gy、gw和gh;ind_x和ind_y则是对应于输入的坐标,比如你的输入feature map是16*16,换句话说ind_x和ind_y就是16*16的feature map上的某个grid cell的坐标。因此重点来了:tx = gx * w - ind_x和ty = gy * h - ind_y,tx和ty是模型回归的目标值。intersect是计算每个anchor和ground truth的交集面积,因此是一个1*n的numpy array,n是anchor的数量;ovps是计算交集面积占并集面积的比例,也就是IOU,也是1*n大小。best_match是选择IOU最大的那个anchor的index。接下来的几行赋值语句中都用到了ind_x和ind_y,这就是为什么说在YOLO算法中是以object的ground truth框的中心所在的box来预测该object,实际上所谓的box都是隐式的,从这里的介绍也可以看出,先是按照box的尺寸去匹配目前的这个object,找IOU最大的box,然后再一个ndarray中将ground truth的信息赋给该box,包括socre、坐标、类别标签、哪个box以及中心点坐标。target_id[b, ind_y, ind_x, best_match, :] = l[0]是将IOU最大的anchor的标签赋值为ground truth的标签,只要没进行这个赋值的点的anchor的标签都是-1,表示背景。target_score[b, ind_y, ind_x, best_match, :] = 1.0是将best_match的box的score赋值为1,也就是置信度为1,其他都为0。tw和th的计算是论文中Figure3的公式的相反过程。因此最后的target_box放的就是模型训练的目标,符号和论文中的公式符号都是一一对应的。sample_weight表示权重。最后得到的tx和ty对应论文中sigmoid函数处理过的结果。

扫描二维码关注公众号,回复: 3158572 查看本文章

参考博客:https://blog.csdn.net/u014380165/article/details/79367541

猜你喜欢

转载自blog.csdn.net/xiaomifanhxx/article/details/81509733