yolov3之pytorch源码解析_PyTorch实现yolov3代码详细解密(一)

yolo系列是目标识别的重头戏,为了更好的理解掌握它,我们必须从源码出发深刻理解代码。下面我们来讲解pytorch实现的yolov3源码。在讲解之前,大家应该具备相应的原理知识yolov1,yolov2,yolov3。

大部分同学在看论文时并不能把所有的知识全部掌握。我们必须结合代码(代码将理论变成实践),它是百分百还原理论的,也只有在掌握代码以及理论后,我们才能推陈出新有所收获,所以大家平时一定多接触代码,这里我们会结合yolov3的理论知识让大家真正在代码中理解思想。

yolov3的inference部分我们主要分为四个部分去分析:

PyTorch实现yolov3代码详细解密(一)

yolov3的train部分我们主要分为两个部分去分析:

创建YOLO网络

首先我们知道yolov3将resnet改造变成了具有更好性能的Darknet作为它的backbone,称为darknet。

配置文件

官方代码(authored in C)使用一个配置文件来构建网络,即 cfg 文件一块块地描述了网络架构。我们开始要做的就是用pytorch来读取网络结构形成自己的module进行前向与反向传播。

我们看到图中有两个卷积层与一个用于残差相加的跳转连接,下面我们逐一讲解darknet中所有的层级。

1.卷积层

这个很简单,就是普通的卷积操作,filters其实就是输出的通道数,激活函数用的是leaky,这里batch_normalize=1 相当于一个flag并不是bn的参数。

详细解释一下pytorch中的bn,如图BN这个类需要一些参数,num_features是feature map的通道数,因为bn最终优化的参数是γ和β,这两个参数是相对于每个维度来讲的,即一个维度对应一组参数。其他的参数不是很重要,想了解的话可以去官网查看。

2.跳转连接

它于残差网络中使用的结构类似,参数 from 为-3 表明该层的输出为前一层的输出加上前三层的输出(要求维度相同,两组输出直接相加)。

3.上采样

通过参数 stride 在前面层级中双线性上采样特征图。

4.路由层(Route)

它的参数 layers 有一个或两个值。当只有一个值时,它输出这一层通过该值索引的特征图。在我们的实验中设置为了-4,所以层级将输出路由层之前第四个层的特征图。

当层级有两个值时,它将返回由这两个值索引的拼接特征图。在我们的实验中为-1 和 61,因此该层级将输出从前一层级(-1)到第 61 层的特征图,并将它们按深度拼接。

5.YOLO

这里的9个anchor是论文中提出用k-means求出来的,yolov3有三条预测之路(多尺度由此而来)mask=0,1,2代表第一条支路,分别对应其anchors。

还有一个net,其描述的不是层的信息,而是训练相关的参数。

解析配置文件

我们定义一个函数 parse_cfg,该函数使用配置文件的路径作为输入。这个函数是将cfg中的网络结构读取出来并将属性和键值存储到词典block中,最后将这些block添加到列表blocks中由函数返还。lines以list形式将cfg按照‘/n’分隔开并存储,然后删除空行,注释,以及每行的空格。

for line in lines:遍历list中每个元素,按照要求将key和value组成block字典存入blocks中。

构建块

现在我们将使用上面 parse_cfg 返回的列表来构建 PyTorch 模块。列表中有 5 种类型的层。PyTorch 为 convolutional 和 upsample 提供预置层。我们将通过扩展 nn.Module 类为其余层写自己的模块。

我们知道blocks[0]是net中的信息,我们将它于层分离, prev_filters = 3因为RGB图输入通道数为3,output_filters = []我们初始化输出通道数。

nn.ModuleList

nn.Sequential它是Module的子类,在构建数个网络层之后会自动调用forward()方法,从而有网络模型生成。而nn.ModuleList仅仅类似于pytho中的list类型,只是将一系列层装入列表,并没有实现forward()方法,因此也不会有网络模型产生的副作用。

路由层(route layer)从前面层得到特征图(可能是拼接的)。如果在路由层之后有一个卷积层,那么卷积核将被应用到前面层的特征图上,精确来说是路由层得到的特征图。因此,我们不仅需要追踪前一层的卷积核数量,还需要追踪之前每个层。随着不断地迭代,我们将每个模块的输出卷积核数量添加到 output_filters 列表上。

现在,我们的思路是迭代模块的列表,并为每个模块创建一个 PyTorch 模块。nn.Sequential 类被用于按顺序地执行 nn.Module 对象,一个 convolutional 类型的模块有一个批量归一化层、一个 leaky ReLU 激活层以及一个卷积层。我们使用 nn.Sequential 将这些层串联起来,得到 add_module 函数。

接下来,我们看看路由层于跳转连接层

因为路由层layers会出现两个数,即两层concat,所以开始用‘,’将数据隔开,一个用start表示,一个用end表示,并将输出的通道数记录以便后面使用。

这里出现了EmptyLayer()

顾名思义,就是空的层。因为Route layer,shortcut layer执行的操作很简单一个是相加,一个是拼接(在特征图上调用 torch.cat)像上述过程那样设计一个层将导致不必要的抽象,增加样板代码。取而代之,我们可以将一个假的层置于之前提出的路由层的位置上,然后直接在代表 darknet 的 nn.Module 对象的 forward 函数中执行拼接运算。

YOLO 层

最后,我们将编写创建 YOLO 层的代码:

x[“mask”]这个键值里面存放的是mask的值,我们用DetectionLayer保存用于检测边界框的锚点。

最后,我们看到由106个sequential组成了ModuleList这个列表。这一部分我们仅仅完成了ModuleList列表的构建,即我们把darknet的框架搭建完成了,但是还缺少网络的前向传播,下面一章我们来完成前向传播。

你可能感兴趣的:(yolov3之pytorch源码解析_PyTorch实现yolov3代码详细解密(一))