YOLOV3 Pytorch版本代码解读
代码与coco数据集关注
wx
公众号JokerTong
回复yolov3
即可获取
参考视频 YOLO系列算法
文章目录
- YOLOV3 Pytorch版本代码解读
- 数据集准备与关键文件说明
- 前提准备
- 代码大致流程
- 需要自行修改代码的部分
- 项目代码解读
- 一 数据与标签的读取
- 二 模型构造
- convolutional层的构建
- rout层与shortcut层的构建
- yolo层的构建
- 三 前向传播
- 四 计算损失
数据集准备与关键文件说明
使用经典的coco2014
数据集,下载地址点击此处进入官网下载(也可以自行去网上搜索)
下载之后解压到项目对应的文件夹, 如下
下载的数据集image
和label
的版本需要一一对应
trainvalno5k.txt
文件
5k.txt
文件 : 验证集的数据的位置
PyTorch-YOLOv3\config\yolov3.cfg
网络配置文件
前提准备
代码大致流程
第一步: 加载配置参数
第二步: 构造模型
第三步: 读取数据
Tips:
以前在小数据集上进行训练的时候我们可以将数据集全部加载到内存中,但是由于coco数据集太大了, 内存放不下, 因此我们使用generator
来提供数据, 在训练的过程中才读取数据, 根据模型的需要一个batch
一个batch
的为其提供数据
需要自行修改代码的部分
添加训练参数
--data_config config/coco.data
--pretrained_weights weights/darknet53.conv.74
coco.data
描述了训练数据集所需要的所有信息: 类别, 训练数据, 验证数据
pretrained_weights
迁移学习的思想, 加载一个预训练模型
修改数据集路径
项目代码解读
一 数据与标签的读取
在109
行打上断点, 观察数据的读取过程
点击Step Into My Code
进入项目中的datasets.py
文件, 可以看出, dataset
通过getitem
一张一张的读取图片
使用Image.open
实际读取图片,统一通道为RGB
,并且转换为tensor
格式
使用padding
的思想, 把原本的长方形图片padding
为正方形
与读取图像类似, 读取对应的标签, 里面包括了类别, box
的信息
这里要注意 图片的编号与标签的编号应该是对应的, 不然数据与标签不匹配训练结果啥也不是
读取label
文件中的物体框信息
原始图像经过了padding处理, 因此标签中框的坐标也需要进行padding的操作, 最后转化为网络中需要的x,y,w,h
格式
这一系列操作之后, 得到一个img
和 target
, 后面会反复进行batch
次, 然后返回模型一个batch
的数据
二 模型构造
重新把断点打在66
行
进入models.py
, init
函数中根据之前的yolov3.cfg
文件对网络整体架构进行定义, forward
函数规定了网络前向传播的整个过程
加载配置文件的信息到self.module_defs
变量中
self.hyperparams, self.module_list = create_modules(self.module_defs) # 逐层定义好网络结构
调用create_modules
来进行模型的构建
循环增加网络层
convolutional层的构建
注意这里的convolutional
其实可能包括了卷积
, BN
, 以及Relu
三种操作, 先将里面的参数信息读取出来
**根据参数信息 添加Conv2d
也就是卷积层 **
如果有bn
层以及激活函数, 将它们加入到网络结构当中
这里在控制台打印一下当前构造的modules
, 其中包含了我们想要的三种层
也就是将之前构造完成的第一个模块加入到大的module_list
当中, 并且记录下filters
的输出个数
rout层与shortcut层的构建
这里只是创建了个空的层, 具体的操作执行在前向传播中
rout
层主要起到拼接的作用, 起到了特征融合的作用
shortcut
层主要是加法的作用(resnet
的思想), 残差连接的功能已经很熟悉了, 这里就不介绍了
配置文件中的-3
表示跟
yolo层的构建
YOLOV3中有三种YOLO层, 它们分别可以检测大中小三种物体
YOLOV3中有三种scale的先验框, 对应着这三种YOLO层, 感受野比较大的YOLO层对应使用大的先验框
这里通过anchor的编号获取实际先验框的大小
调用YOLOLayer
来构建出YOLO
层, 具体的前向传播在后面介绍
三 前向传播
在各个模型的forward
处打上断点
首先进入的是Darknet
的forward
这里的卷积, 上采样, 和最大池化层的前向传播都非常简单, 根据pytorch内置的函数, 直接x = module(x)
就可以了
if module_def["type"] in ["convolutional", "upsample", "maxpool"]:
x = module(x)
elif module_def["type"] == "route":
x = torch.cat([layer_outputs[int(layer_i)] for layer_i in module_def["layers"].split(",")], 1)
elif module_def["type"] == "shortcut":
layer_i = int(module_def["from"])
x = layer_outputs[-1] + layer_outputs[layer_i]
route
层的效果是按某个维度做拼接shortcut
层的效果是对两层做加法
最重要的是YOLO层
这里的img_dim
可能有多种大小 但都能被32整除, 因为YOLOV3网络会随机的选择一个大小的图片进行训练
对于预测的维度进行调整, 4 * 3 * 15 * 15 * 85
4
表示batch_size
3
表示 先验框的有三种15 * 15
为特征图的大小85 = 80 + 5
表示80个类别 与x,y,w,h
与 置信度
取出其中的x,y,w,h,c
与每个类别的预测值
通过相对位置得到对应的绝对位置
得到特征图(如当前为15*15
)中各个坐标的实际位置
因为标签中的框是在原始图像中的, 所以output要把预测框也放大相应的倍数还原到原始图像中
四 计算损失
通过build_targets
函数将标签值进行转换, 转换成和预测值相同的格式, 这里可以点进去自己看一下build_targets
的内容
计算loss
- 对于
x,y,w,h
来说, 并不是所有的位置都要算一遍, 我们只计算有物体的位置处的损失, 所以这里用了obj_mask
作为index
loss_conf_obj
,loss_conf_noobj
计算的是前景和背景的损失, 也就是当前位置是不是物体. 因为这里的预测值和真实值只有0
和1
, 所以使用bce_loss
即可计算
他们两个相加, 乘上相应的权重参数就得到了置信度损失loss_conf_noobj
loss_cls
分类损失的原理也类似
最终将所有的损失相加就得到了总损失
这里附上一张损失函数的计算图像, 可以看出和代码中的一样, 很好理解
后面的操作基本上是通用的pytorch训练模式