原文为英文,进行了翻译和部分修改,原文地址
代码地址:github仓库、ACgit仓库
相关内容:
YOLOv3论文翻译
YOLOv3原理及流程简述
从头实现YOLOv3:第1部分
从头实现YOLOv3:第3部分
从头实现YOLOv3:第4部分
从头实现YOLOv3:第5部分
第2部分:创建网络结构的层
前置知识
- PyTorch 的基本工作知识,包括如何使用
nn.Module
、nn.Sequential
和torch.nn.parameter
类创建自定义架构。
现在开始
首先创建一个目录,将检测器的代码存放在里面。
然后,创建一个文件darknet.py
。darknet
是YOLO
底层架构的名称。该文件将包含创建 YOLO
网络的代码。我们将用一个名为 util.py
的文件对其进行补充,该文件将包含各种辅助函数的代码。将这两个文件保存在检测器文件夹中。可以使用 git
来跟踪修改。
配置文件
官方代码(用 C 编写)使用配置文件来构建网络。 cfg
文件逐块描述网络的布局。如果您用过 caffe
,则相当于用于描述网络的 .protxt
文件。
我们将使用作者发布的官方 cfg
文件来构建我们的网络。从这里下载它并将其放在检测器目录中名为 cfg
的文件夹中。如果您使用的是 Linux,请 cd 进入网络目录并键入:
mkdir cfg
cd cfg
wget https://raw.githubusercontent.com/pjreddie/darknet/master/cfg/yolov3.cfg
如果你打开配置文件,会看到类似这样的东西。
[convolutional]
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=32
size=1
stride=1
pad=1
activation=leaky
[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky
[shortcut]
from=-3
activation=linear
我们看到上面有 4 个块。其中,3 个描述了卷积层,然后是一个shortcut layer
。shortcut layer
是一种 skip connection
,就像 ResNet
中使用的那样。 YOLO
中使用了 5 种类型的层:
Convolutional
[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky
Shortcut
[shortcut]
from=-3
activation=linear
shortcut layer
是一种 skip connection
,类似于 ResNet
中使用的连接。 from
参数为 -3,表示shortcut layer
的输出是将 shortcut layer
上一层和之前第三层的 feature map
相加得到的。
将上一层和指定的某一层的特征图相加后输出。
Upsample
[upsample]
stride=2
使用步长stride
的双线性上采样,对上一层中的特征图进行上采样。
Route
[route]
layers = -4
[route]
layers = -1, 61
Route
层值得稍微解释一下。它有一个属性layers
,可以有一个或两个值。
当layers
属性只有一个值时,输出该值索引的图层的特征图。在我们的示例中,它是 -4,因此该层将输出 Route
层之前第 4 层的特征图。
当layers
有两个值时,它返回由它的值索引的图层的串联特征图。在我们的示例中,它是 -1, 61,该层将输出来自前一层 (-1) 和第 61 层的特征图,沿深度维度连接后输出。
输出指定的某一层的特征图;或者先将指定的某两层特征图沿深度连接,再输出。
YOLO
[yolo]
mask = 0,1,2
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1
YOLO
层对应第1部分描述的检测层。anchors
描述了9个 anchor
,但只使用了由 mask
标签的属性索引的 anchor
。这里 mask
的值为 0,1,2,表示使用了第一个、第二个和第三个 anchor
。这是有道理的,因为检测层的每个单元格预测 3 个框。总的来说,有 3 个尺度的检测层,总共有 9 个 anchor
,3 个 yolo
层分别使用 3 个不同的 anchor
。
yolo
层还指定了类别个数。
Net
[net]
# Testing
batch=1
subdivisions=1
# Training
# batch=64
# subdivisions=16
width= 320
height = 320
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1
cfg
中有另一种称为 net
的块,但我不会称其为层,因为它仅描述有关网络输入和训练参数的信息。它不用于YOLO
的前向传递。然而它提供了网络输入大小等信息,我们用它来调整前向传递中的 anchor
。
解析配置文件
开始之前,在 darknet.py
文件的顶部添加必要的导入。
from __future__ import division # 引入精确除法
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np
我们定义了一个名为 parse_cfg
的函数,它以配置文件的路径作为输入。
def parse_cfg(cfgfile):
"""
Takes a configuration file
:param cfgfile:
:return:a list of blocks. Each blocks describes a block in the neural
network to be built. Block is represented as a dictionary in the list
"""
这里的想法是解析 cfg
,并将每个块存储为字典。块的属性及其值作为键值对存储在字典中。当我们解析 cfg
时,把这些由代码中的变量 block
表示的字典附加到列表 blocks
中。函数将返回 blocks
。
我们首先将 cfg
文件的内容保存在字符串列表中。以下代码对此列表执行一些预处理。
file = open(cfgfile, 'r')
lines = file.read().split('\n') # 存每一行
lines = [x for x in lines if len(x) > 0] # 去掉空行
lines = [x for x in lines if x[0] != '#'] # 去掉注释
lines