YOLO相关原理 :
https://blog.csdn.net/leviopku/article/details/
https://www.jianshu.com/p/d13ae
https://blog.csdn.net/_/article/details/
https://blog.csdn.net/chandanyan8568/article/details/
https://blog.csdn.net/leviopku/article/details/
分析代码:
https://github.com/eriklindernoren/PyTorch-YOLOv3
注:这个是方便个人学习pytorch和yolov3所做的记录,有任何错误欢迎指出。
从detect.py开始分析代码的流程。
1.1.1 YOLOv3模型解析
model = Darknet(opt.model_def, img_size=opt.img_size).to(device),这条语句加载darkent模型,即YOLOv3模型。Darknet模型在model.py中进行定义。其完整定义如下:
首先从__init__()函数开始,大致流程是从.cfg中解析文件,然后根据文件内容生成相关的网络结构。
解析后会生成一个列表,存储网络结构的各种属性,通过遍历这个列表便可以得到网络结构,解析后的列表如下图所示(部分):

self.hyperparams, self.module_list = create_modules(self.module_defs),这条语句会根据生成的列表构建网络结构,create_modules()函数如下:
根据列表会生成相应的convolutional、maxpool、upsample、route、shortcut、yolo层。
convolutional层构建方法很常规:设置filter尺寸、数量,添加batch normalize层(在.cfg文件中batch_normalize=1),以及pad层,使用leaky激活函数。
maxpool层,不过在YOLOv3中没有使用最大池化来进行下采样,是使用的3*3的卷积核,步长=2的卷积操作进行下采样,(细心的同学会发现yolov3.cfg没有maxpool层),一共5次,下采样2^5=32倍数。

upsample层,上采样层,由于nn.Upsample被弃用了,所以新建了一个类完成这个操作。
接下来是route层,这层十分重要。这层的作用相当于把前面的特征图进行相融合。
下图来自darknet-master(windos下的yolov3实现,纯C语言,下图是方便理解,本文不对该代码分析)

layer=-4,表示当前层的序号减4,如第83层route,-4之后是79层,把79层的特征层融合(layer值只有一个,相当于只有链接过来),route层的输出可以看作是下一层的输入,即13*13*512和79层的特征图是完全吻合的。同理,layer=-1,61,表示融合85(86-1)层和61层的特征图。即26*26*512+26*26*256=26*26*768。至于为什么选这几个层进行融合,我表示并不清楚,希望有了解的朋友指点一下。 这几层刚好下采样块的输出层。
shortcut层,直连层,借鉴于ResNet网络。
https://cloud.tencent.com/developer/article/,更多细节可查看:https://blog.csdn.net/u0/article/details/
ResNet 的动机依然是解决深度模型中的退化问题:层数越深,梯度越容易发散,误差越大,难以训练。理论上,模型层数越深,误差应该越小才对,因为我们总可以根据浅层模型的解构造出深层模型的解(将深层模型与浅层模型对应的层赋值为浅层模型的权重,将后面的层取为恒等映射),使得这个深层模型的误差不大于浅层模型的误差。但是实际上,深度模型的误差要比浅层模型的误差要大,在CIFAR-10上面的训练和测试误差如下图所示。产生这种现象的原因是深度模型难以优化,难以收敛到较优的解,并假设相比于直接优化最初的plain networks的模型F(x)=y,残差F(x)=y-x更容易优化。 需要注意的是,变换F可以是很多层,也就是说shortcut不一定只跨越1层。并且实际中,由于shortcut只跨越单层没有优势,ResNet中是跨越了2层或3层
YOLOv3完整的结构有100+层,所以采用直连的方式来优化网络结构,能使网络更好的训练、更快的收敛。值得注意的是,YOLOv3的shortcut层是把网络的值进行叠加,没有改变特征图的大小,所以仔细的人会发现在shortcut层的前后,输入输出大小没变。
在本代码中,route层和shortcut层使用EmptyLayer()来进行占位。
重点:yolo层。
仔细看上图的五次采样,会发现有三个Scale,分别是Scale1(下采样2^3=8倍),Scale2(下采样2^4=16倍),Scale3(下采样2^5=32倍),此时网络默认的尺寸是416*416,对应的feature map为52*52,26*26,13*13。这里借用一幅图:
https://blog.csdn.net/leviopku/article/details/

之所以使用3种尺度,是为了加强对小目标的检测,这个应该是借鉴SSD的思想。比较大的特征图来检测相对较小的目标,而小的特征图负责检测大目标。
在有多尺度的概念下,作者使用k-means得到9个先验框的尺寸(416*416的尺寸下)。作者原话为:
We still use k-means clustering to determine our bounding box priors. We just sort of chose 9 clusters and 3 scales arbitrarily and then divide up the clusters evenly across scales. On the COCO dataset the 9 clusters were: (10×13),(16×30),(33×23),(30×61),(62×45),(59× 119),(116×90),(156×198),(373×326).
解析yolo层代码:
可以看到输出:

可以看到yolo层搭建了三次,可以看图4,第一个yolo层是下采样2^5=32倍,特征图尺寸是13*13(默认输入416*416,下同)。这层选择mask的ID是6,7,8,对应的anchor box尺寸是(116, 90)、(156, 198)、(373, 326)。这对应了上面所说的,小的特征图检测大目标,所以使用的anchor box最大。
至此,Darknet(YOLOv3)模型基本加载完毕,接下来就是,加载权重.weights文件,进行预测。
1.2.1 获取检测框
model.load_darknet_weights(opt.weights_path)通过这个语句加载yolov3.weights。加载的完整代码如下:
这一段的代码是解析.weights文件,这里我了解不够到位,欢迎有知道的人指点。主要是不知.weights的结构是怎样的,所以有点疑惑。加载完.weights文件之后,便开始加载测试图片数据。
ImageFolder是遍历文件夹下的测试图片,完整定义如下。ImageFolder中的__getitem__()函数会把图像归一化处理成img_size(默认416)大小的图片。
detections = model(input_imgs),把图像放进模型中,得到检测结果。这里是通过Darknet的forward()函数得到检测结果。其完整代码如下:
通过遍历self.module_defs,与self.module_list,来完成网络的前向传播。
如果是"convolutional", "upsample", "maxpool"层,则直接使用前向传播即可。
如果是route层,则使用torch.cat()完成特征图的融合(拼接)。这里测试一张图:
这张图的尺寸为3*768*576,我们看看放进模型进行测试的时候,其shape是如何变化的。图像会根据cfg归一化成416*416.
接下来查看一下route层对应的ID以及shape:

该模型的每一层的输出通过layer_outputs.append(x),保存在layer_outputs列表中,本次结构完全符合本文前面所论述的部分。如果layer只有一个值,那么该route层的输出就是该层。如果layer有两个值,则route层输出是对应两个层的特征图的融合。
shortcut层则特别清晰,直接对应两层相叠加即可:
yolo层有三个,分别对应的特征图大小为13*13,26*26,52*52。每一个特征图的每一个cell会预测3个bounding boxes。每一个bounding box会预测预测三类值:(1)每个框的位置(4个值,中心坐标tx和ty,,框的高度bh和宽度bw),(2)一个objectness prediction ,一个目标性评分(objectness score),即这块位置是目标的可能性有多大。这一步是在predict之前进行的,可以去掉不必要anchor,可以减少计算量(3)N个类别,COCO有80类,VOC有20类。
所以不难理解,在这里是COCO数据集,在13*13的特征图中,一共有13*13*3=507个bounding boxes,每一个bounding box预测(4+1+80=85)个值,用张量的形式表示为[1, 507, 85],那个1表示的是batch size。同理,其余张量的shape不难理解。

至于如何得到这个张量的,主要需要了解yolo层的forward()和compute_grid_offsets(),其完整代码如下:
num_samples是每一批有多少张图片,grid_size是特征图的大小。

使用torch.view,改变输入yolo层的张量结构(shape),以prediction命名的张量进行预测处理。

接下来是便是对边框进行预测,具体细节可以参考:https://blog.csdn.net/_/article/details/。x,y坐标都是使用了sigmoid函数进行处理,置信度和类别概率使用同样的方法处理。
论文中的边界框预测:

Bounding boxes with dimension priors and location prediction. We predict the width and height of the box as offsets from cluster centroids. We predict the center coordinates of the box relative to the location of filter application using a sigmoid function. This figure blatantly self-plagiarized from.
在3个尺度下,分别进行预测坐标、置信度、类别概率,这和在搭建yolo层一直,可对比图5。

从图中我们发现grid_size和self.grid_size是不相等的,所以需要进行计算偏移,即compute_grid_offsets。完整代码在YOLOLayer中。
以gird=13为例。此时特征图是13*13,但原图shape尺寸是416*416,所以要把416*416评价切成13*13个方格,需要得到间隔(步距self.stride=416/13=32)。相应的并把anchor的尺寸进行缩放,即116/32=3.6250,90/32=2.8125。

根据论文和图10可知,每一个小方格(cell),都会预测3个边界框,同样以gird=13为列。第一个小方格(cell),会预测3个边界框,每个边界框都有坐标+置信度+类别概率。所以以下代码中的x.shape=[1, 3, 13, 13],并且与y,w,h的shape一致。
同时由于在最后进行拼接,得到输出output 。其507=13*13*3,2028=26*26*3,8112=52*52*3不难理解。

由于target=None(推演的时候设置为None),所以输出的total_loss=0。
1.2.2 非极大值抑制
在获取检测框之后,需要使用非极大值抑制来筛选框。即 detections = non_max_suppression(detections, opt.conf_thres, opt.nms_thres)
完整代码如下:
非极大值抑制算法可参考:
https://www.cnblogs.com/makefile/p/nms.html
https://www.jianshu.com/p/d452b
在经过非极大值抑制处理之后,在这里唯一有一点不同的是,这里采取了边界框“融合”的策略:
显示非极大值抑制过后的目标检测效果。

至此第一部分检测分析完毕,剩下关于训练的部分还在更新中。
Pytorch | yolov3代码详解(二)(更新中)
已更完
Pytorch | yolov3代码详解(二)
https://blog.csdn.net/_/article/details/
到此这篇yolov3作者(yolov3简介)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!
版权声明:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权、违法违规、事实不符,请将相关资料发送至xkadmin@xkablog.com进行投诉反馈,一经查实,立即处理!
转载请注明出处,原文链接:https://www.xkablog.com/bcyy/55725.html