YOLOv3.cfg文件解析
[net]
batch=16
subdivisions=4
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1
learning_rate=0.001
burn_in=1000
max_batches = 500200
policy=steps
steps=400000,450000
scales=.1,.1
--------------------- 卷积层 ----------------------
[convolutional]
batch_normalize=1
filters=32
size=3
stride=1
pad=1
activation=leaky
------------------------ 下采样 ------------------------
[convolutional]
batch_normalize=1
filters=128
size=3
stride=2
pad=1
activation=leaky
可以通过带入以上公式,可以得到 OutFeature 是 InFeature 的一半。
也可以使用 maxpooling 进行下采样:
[maxpool]
size=2
stride=2
------------------------ 上采样 -------------------------
[upsample]
stride=2
上采样是通过线性插值实现的。
----------------- Shortcut 和 Route 层 ------------------
[shortcut]
from=-3
activation=linear
[route]
layers = -1, 36
[route]
layers = -4
------------------------------ YOLO 层 ---------------------------
[convolutional]
size=1
stride=1
pad=1
filters=18
activation=linear
[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=1
num=9
jitter=.3
ignore_thresh = .5
数据组织、加载
参考:https://blog.csdn.net/DD_PP_JJ/article/details/104709299
在pytorch中,数据集加载主要是重构datasets类 ,然后再使用dataloader中加载dataset,就构建好了数据部分。
下面是一个简单的使用模板(pytorch中的数据加载机制):
import os
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
class MyDataset(Dataset):
def __init__(self):
xy = np.loadtxt('label.txt', delimiter=',', dtype=np.float32)
self.x_data = torch.from_numpy(xy[:, 0:-1])
self.y_data = torch.from_numpy(xy[:, [-1]])
self.len = xy.shape[0]
def __getitem__(self, index):
return self.x_data[index], self.y_data[index]
def __len__(self):
return self.len
myDataset = MyDataset()
train_loader = DataLoader(dataset=myDataset,
batch_size=32,
shuffle=True)
for epoch in range(2):
for i, data in enumerate(train_loader):
inputs, labels = data
inputs, labels = Variable(inputs), Variable(labels)
建议阅读:
矩形训练相关:https://blog.csdn.net/songwsx/article/details/102639770 仿射变换:https://zhuanlan.zhihu.com/p/93822508 Rectangle Trainning:https://github.com/ultralytics/yolov3/issues/232 数据自由读取:https://zhuanlan.zhihu.com/p/30385675
超参数搜索与进化
参考:https://blog.csdn.net/DD_PP_JJ/article/details/104709330
YOLOv3代码中也提供了参数搜索,可以为对应的数据集进化一套合适的超参数。
在train.py,其中包含了一些数据增强参数设置:
hyp = {'giou': 3.54,
'cls': 37.4,
'cls_pw': 1.0,
'obj': 64.3,
'obj_pw': 1.0,
'iou_t': 0.20,
'lr0': 0.01,
'lrf': 0.0005,
'momentum': 0.937,
'weight_decay': 0.0005,
'fl_gamma': 0.0,
'hsv_h': 0.0138,
'hsv_s': 0.678,
'hsv_v': 0.36,
'degrees': 1.98 * 0,
'translate': 0.05 * 0,
'scale': 0.05 * 0,
'shear': 0.641 * 0}
在训练的时候,train.py提供了一个可选参数--evolve , 决定了是否进行超参数搜索与进化(默认是不开启超参数搜索的)。
python train.py --data data/voc.data --cfg cfg/yolov3-tiny.cfg --img-size 416 --epochs 273 --evolve
实际使用的时候,需要进行修改,train.py中的约444行:
for _ in range(1):
将其中的1修改为你想设置的迭代数,比如200代,如果不设置,结果将会如下图所示,实际上就是只有一代。
网络模型的构建
参考:https://blog.csdn.net/DD_PP_JJ/article/details/104709403
1、如何从cfg文件构造模型。本文涉及到一个比较有用的部分就是bias的设置,可以提升mAP、F1、P、R等指标,还能让训练过程更加平滑。 2、修改网络结构很容易,只需要修改cfg文件即可。目前,cfg文件支持convolutional, maxpool, unsample, route, shortcut, yolo这几个层。 3、如果想要添加自定义的模块也很方便,比如说注意力机制模块、空洞卷积等,都可以简单地得到添加或者修改。 4、为了更加方便的理解cfg文件网络是如何构建的,在这里推荐一个Github上的网络结构可视化软件:Netron
train.py:
model = Darknet(cfg, arc=opt.arc).to(device)
然后沿着Darknet实现进行讲解:
class Darknet(nn.Module):
def __init__(self, cfg, img_size=(416, 416), arc='default'):
super(Darknet, self).__init__()
self.module_defs = parse_model_cfg(cfg)
self.module_list, self.routs = create_modules(self.module_defs, img_size, arc)
self.yolo_layers = get_yolo_layers(self)
self.version = np.array([0, 2, 5], dtype=np.int32)
self.seen = np.array([0], dtype=np.int64)
比较关键的就是成员函变量module_defs、module_list、routs、yolo_layers 四个成员函数
YOLOLayer解析和推理过程
模型构建中最重要的YOLOLayer还没有梳理,本文将从代码的角度理解YOLOLayer的构建与实现
1、 Grid创建 需要注意的是这是针对某一层YOLOLayer,而不是所有的YOLOLayer
def create_grids(self,
img_size=416,
ng=(13, 13),
device='cpu',
type=torch.float32):
nx, ny = ng
self.img_size = max(img_size)
self.stride = self.img_size / max(ng)
yv, xv = torch.meshgrid([torch.arange(ny), torch.arange(nx)])
self.grid_xy = torch.stack((xv, yv), 2).to(device).type(type).view(
(1, 1, ny, nx, 2))
self.anchor_vec = self.anchors.to(device) / self.stride
self.anchor_wh = self.anchor_vec.view(1, self.na, 1, 1,
2).to(device).type(type)
self.ng = torch.Tensor(ng).to(device)
self.nx = nx
self.ny = ny
2、 YOLOLayer 训练过程: YOLOLayer的作用就是对上一个卷积层得到的张量进行处理,具体可以看training过程涉及的代码(暂时不关心ONNX部分的代码):
class YOLOLayer(nn.Module):
def __init__(self, anchors, nc, img_size, yolo_index, arc):
super(YOLOLayer, self).__init__()
self.anchors = torch.Tensor(anchors)
self.na = len(anchors)
self.nc = nc
self.no = nc + 5
self.nx = 0
self.ny = 0
self.arc = arc
if ONNX_EXPORT:
stride = [32, 16, 8][yolo_index]
nx = int(img_size[1] / stride)
ny = int(img_size[0] / stride)
create_grids(self, img_size, (nx, ny))
def forward(self, p, img_size, var=None):
'''
onnx代表开放式神经网络交换
pytorch中的模型都可以导出或转换为标准ONNX格式
在模型采用ONNX格式后,即可在各种平台和设备上运行
在这里ONNX代表规范化的推理过程
'''
if ONNX_EXPORT:
bs = 1
else:
bs, _, ny, nx = p.shape
if (self.nx, self.ny) != (nx, ny):
create_grids(self, img_size, (nx, ny), p.device, p.dtype)
p = p.view(bs, self.na, self.no, self.ny,
self.nx).permute(0, 1, 3, 4, 2).contiguous()
if self.training:
return p
在理解以上代码的时候,需要理解每一个通道所代表的意义,原先的P是由上一层卷积得到的feature map, 形状为(以80个类别、输入416、下采样32倍为例):【batch size, anchor×(80+5), 13, 13】,在训练的过程中,将feature map通过张量操作转化的形状为:【batch size, anchor, 13, 13, 85】。 测试过程:
else:
io = p.clone()
io[..., :2] = torch.sigmoid(io[..., :2]) + self.grid_xy
io[..., 2:4] = torch.exp(
io[..., 2:4]) * self.anchor_wh
io[..., :4] *= self.stride
if 'default' in self.arc:
torch.sigmoid_(io[..., 4])
elif 'BCE' in self.arc:
torch.sigmoid_(io[..., 5:])
io[..., 4] = 1
elif 'CE' in self.arc:
io[..., 4:] = F.softmax(io[..., 4:], dim=4)
io[..., 4] = 1
if self.nc == 1:
io[..., 5] = 1
return io.view(bs, -1, self.no), p
理解以上内容是需要对应以下公式:
io[..., :2] = torch.sigmoid(io[..., :2]) + self.grid_xy
io[..., 2:4] = torch.exp(io[..., 2:4]) * self.anchor_wh
class部分: 在类别部分,提供了几种方法,根据arc参数来进行不同模式的选择。以CE(crossEntropy)为例:
io[..., 4:] = F.softmax(io[..., 4:], dim=4)
io[..., 4] = 1
Loss部分计算
YOLOv1是一个anchor-free的,从YOLOv2开始引入了Anchor,在VOC2007数据集上将mAP提升了10个百分点。YOLOv3也继续使用了Anchor,本文主要讲ultralytics版YOLOv3的Loss部分的计算, 实际上这部分loss和原版差距非常大,并且可以通过arc指定loss的构建方式, 如果想看原版的loss可以在release的v6中下载源码。
详见:https://blog.csdn.net/DD_PP_JJ/article/details/105173151
|