train.py
model = Model(cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device)
参数:
ch
输入通道nc
类别数目anchors
导入hyp中的anchors
yolo.py
class Model(nn.Module):
- 解析yaml文件,定义模型
- 初始化模型权重
- 前向推理
以下均以默认Yolov5文件为例
一、解析文件
parse_model(deepcopy(self.yaml), ch=[ch])
参数:
self.yaml
=yolov5s.yamlch
=[3] 输入通道数,将其变为一个list用于记录每一层的输出通道数,如[32,64,……],第一层输出32个,第二层输出64。
1.1定义部分参数
def parse_model(d, ch): # model_dict, input_channels(3)
LOGGER.info(f"\n{'':>3}{'from':>18}{'n':>3}{'params':>10} {'module':<40}{'arguments':<30}")
anchors, nc, gd, gw = d['anchors'], d['nc'], d['depth_multiple'], d['width_multiple']
na = (len(anchors[0]) // 2) if isinstance(anchors, list) else anchors # number of anchors
no = na * (nc + 5) # number of outputs = anchors * (classes + 5)
参数
gd
即depth_multiple
对模块的深度进行缩放,即缩小n
gd
即width_multiple
na
anchors的数量,len(anchors[0])=6,na=3no
输出数量=锚框数量×(类别数+4个偏移量+1个置信度)
1.2 把txt的字符串参数转为数字
layers, save, c2 = [], [], ch[-1] # layers, savelist, ch out
for i, (f, n, m, args) in enumerate(d['backbone'] + d['head']): # from, number, module, args
m = eval(m) if isinstance(m, str) else m # eval strings
for j, a in enumerate(args):
try:
args[j] = eval(a) if isinstance(a, str) else a # eval strings
except NameError:
pass
参数:
layers
存储解析到的model的layerc2
输入通道数
# [from, number, module, args]
[[-1, 1, Conv, [64, 6, 2, 2]], # 0-P1/2
f
输入层的序号n
这个参数下的conv的数量module
模块的类别,如有Conv, C3, SPPF等args
参数
1.3 根据不同module解析
n = n_ = max(round(n * gd), 1) if n > 1 else n # depth gain
if m in (Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, MixConv2d, Focus, CrossConv,
BottleneckCSP, C3, C3TR, C3SPP, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x):
c1, c2 = ch[f], args[0]
if c2 != no: # if not output
c2 = make_divisible(c2 * gw, 8)
args = [c1, c2, *args[1:]]
if m in [BottleneckCSP, C3, C3TR, C3Ghost, C3x]:
args.insert(2, n) # number of repeats
n = 1
elif m is nn.BatchNorm2d:
args = [ch[f]]
elif m is Concat:
c2 = sum(ch[x] for x in f)
elif m is Detect:
args.append([ch[x] for x in f])
if isinstance(args[1], int): # number of anchors
args[1] = [list(range(args[1] * 2))] * len(f)
elif m is Contract:
c2 = ch[f] * args[0] ** 2
elif m is Expand:
c2 = ch[f] // args[0] ** 2
else:
c2 = ch[f]
n = n_ = max(round(n * gd), 1) if n > 1 else n # depth gain
gd
缩放该module的深度,如果n>1,则进行缩放,若缩放后不足1个,则取1,若缩放后n>1,则四舍五入;如果n<1,那就是0,不能让它变成1,就直接取0。
if m in (Conv, GhostConv, Bottleneck, GhostBottleneck, SPP, SPPF, DWConv, MixConv2d, Focus, CrossConv,
BottleneckCSP, C3, C3TR, C3SPP, C3Ghost, nn.ConvTranspose2d, DWConvTranspose2d, C3x):
c1, c2 = ch[f], args[0]
if c2 != no: # if not output
c2 = make_divisible(c2 * gw, 8) #如果c2不是最终输出通道数,则按gw缩小其宽度
args = [c1, c2, *args[1:]] #修改args,如从[64,6,2,2]->[3,32,6,2,2]
if m in [BottleneckCSP, C3, C3TR, C3Ghost, C3x]:
args.insert(2, n) # number of repeats 把缩放后的C3模块数目插入第三位,agrs=[输入,输出,宽度]
n = 1
参数:
c1
输入通道数,c2
输出通道数。如c1=3,c2=64。gw
缩小网络宽度(即输出通道数)make_divisible
让最终的网络宽度是8的倍数,向下取最近的args = [c1, c2, *args[1:]]
将args从[原始输出通道数,卷积核大小,步长,padding]
改为[输入通道数,缩放后的输出通道数,卷积核大小,步长,padding]*args[1:]
即从args的第二位开始切片
m_.i, m_.f, m_.type, m_.np = i, f, t, np
m_.i
层序列号
m_.f
从哪层来,-1指上一层
m_.type
类别,‘models.common.C3’
m_.np
参数数量
save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist
如果输入不是从上一层来,就记录该层到save
这个list中
layers.append(m_)
if i == 0:
ch = []
ch.append(c2)
- 把创建好的
module
(type如为’models.common.C3’)添加到layer
的list中 - 如果是第一层则初始化
ch
(该之前的[3]为[],因为不要输入),再在后续中,逐渐增加各层输出通道