YoloV5 工程文档 学习自记

YoloV5 工程 代码学习

YOLOv5 工程文件中的需要注意的文件

runs: 存储结果/中间过程文件

weights :存储神经网络模型以及权重参数,一边可以在学习(train)结束后生成,一边在测试(detect)时可以通过调用直接检测

models:存储模型的相关函数,比如yolo.py和common.py(记录)

.yaml文件:神经网路主干内容的描述文件

nc 代表训练的类别数,depth_multiple(模型),width_multiple(层)代表倍率

anchors,暂时不清楚,后面的是检测网络和输出网路的构造。

# Parameters
nc: 80  # number of classes
depth_multiple: 0.67  # model depth multiple
width_multiple: 0.75  # layer channel multiple
anchors:
  - [10,13, 16,30, 33,23]  # P3/8
  - [30,61, 62,45, 59,119]  # P4/16
  - [116,90, 156,198, 373,326]  # P5/32

# YOLOv5 backbone
backbone:
  # [from, number, module, args]
  [[-1, 1, Focus, [64, 3]],  # 0-P1/2
   [-1, 1, Conv, [128, 3, 2]],  # 1-P2/4
   [-1, 3, C3, [128]],
   [-1, 1, Conv, [256, 3, 2]],  # 3-P3/8
   [-1, 9, C3, [256]],
   [-1, 1, Conv, [512, 3, 2]],  # 5-P4/16
   [-1, 9, C3, [512]],
   [-1, 1, Conv, [1024, 3, 2]],  # 7-P5/32
   [-1, 1, SPP, [1024, [5, 9, 13]]],
   [-1, 3, C3, [1024, False]],  # 9
  ]

# YOLOv5 head
head:
  [[-1, 1, Conv, [512, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 6], 1, Concat, [1]],  # cat backbone P4
   [-1, 3, C3, [512, False]],  # 13

   [-1, 1, Conv, [256, 1, 1]],
   [-1, 1, nn.Upsample, [None, 2, 'nearest']],
   [[-1, 4], 1, Concat, [1]],  # cat backbone P3
   [-1, 3, C3, [256, False]],  # 17 (P3/8-small)

   [-1, 1, Conv, [256, 3, 2]],
   [[-1, 14], 1, Concat, [1]],  # cat head P4
   [-1, 3, C3, [512, False]],  # 20 (P4/16-medium)

   [-1, 1, Conv, [512, 3, 2]],
   [[-1, 10], 1, Concat, [1]],  # cat head P5
   [-1, 3, C3, [1024, False]],  # 23 (P5/32-large)

   [[17, 20, 23], 1, Detect, [nc, anchors]],  # Detect(P3, P4, P5)
  ]

detect.py :检测的py 文件

train.py:训练的py文件

export.py:从现有的.pt文件生成更可视化/通用的onnx文件

python 类相关

定义

Python 的类是 C++ 和 Modula-3 中类机制的结合体,而且支持所有面向对象编程(OOP)的标准特性:类继承机制支持多个基类,派生类可以覆盖基类的任何方法,类的方法可以调用基类中相同名称的方法。对象可以包含任意数量和类型的数据。和模块一样,类也拥有 Python 天然的动态特性:在运行时创建,创建后也可以修改。

python 的作用域和命名空间

namespace (命名空间)是一个从名字到对象的映射。不同命名空间中的名称之间绝对没有关系,例如,两个不同的模块都可以定义一个 maximize 函数而不会产生混淆 — 模块的用户必须在其前面加上模块名称。

关于self 的代码

class Box:
    def __init__(self, boxname, size, color):
        self.boxname = boxname
        self.size = size
        self.color = color  # self就是用于存储对象属性的集合,就算没有属性self也是必备的
  
    def open(self,myself):
        print('-->用自己的myself,打开那个%s,%s的%s' % (myself.color, myself.size, myself.boxname))
        print('-->用类自己的self,打开那个%s,%s的%s' % (self.color, self.size, self.boxname))
 
    def close(self):
        print('-->关闭%s,谢谢' % self.boxname)
 
 
b = Box('魔盒', '14m', '红色')
c = Box('魔盒', '8m', '绿色')
b.close()
b.open(c)  # 本来就会自动传一个self,现在传入b,就会让open多得到一个实例对象本身,print看看是什么。
print(b.__dict__)  # 这里返回的就是self本身,self存储属性,没有动作。

关于类的继承

class parent(object):
     
    def implicit(self):
        print("Parent implicit()")
    def override(self):
        print("Parent override()")
    def altered(self):
        print("Parent altered()")
         
class child(parent):
     
    def override(self):
        print("Child override()")
    def altered(self):
        print("Child,Before Parent altered()")
        super(child,self).altered()
        print("Child,After Parent altered()")
         
dad=parent()
son=child()
 
dad.implicit()
son.implicit()
 
dad.override()
son.override()
 
dad.altered()
son.altered()

可以看出 class child(parent),定义的child类继承了parent类

关于super 函数

描述

super() 函数是用于调用父类(超类)的一个方法。

super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。

python 基础知识

assert

当判断的文件存在时,使用断言说明文件存在程序运行没有任何错误。而删除断言中判断的存在文件之后,程序运行时候报错。实际上,断言的条件不成立时程序是直接报错并且终止执行。这不仅仅是保证程序运行可靠的一种方式,同时也算是一种程序问题定位的一种手段。因为程序运行停止时,错误信息会给出出现错误所在的代码行,而相应的条件都是程序员自己设定的,比较容易排查。

stride = 5
assert stride in [3, 5]
stride = 4
assert stride in [3, 5]
stride = 3
assert stride in [3, 5]

综上,判定 stride是否为3/5,如果都不是,报错抛出异常,否则程序继续

a =3 if True else 5

a =3 if True else 5
print(a)# 3
a =3 if False else 5
print(a)# 5

语句大意是,判定为真则a =3 ,为假则等于5

然后说下优先级的问题

state = False
a = 3+2 if state else 10 
print(a) #10 
state = True
a = 3+2 if state else 10 
print(a) #5 

由于输出的是5和10而不是5和12 那么可以说明,后缀的if语句的优先级比‘+’的优先级低,

and 、or

在不加括号时候, and优先级大于or

x or y 的值只可能是x或y. x为真就是x, x为假就是y

x and y 的值只可能是x或y. x为真就是y, x为假就是x

print(3 and 5)# 5
print(5 and 3)# 3
print(False and 3)# False
print(True and 3)# 3
inp = 2
oup = 2
a = True and inp == oup
print(a)# True
oup = 1
a = True and inp == oup
print(a)# False
oup = 2
a = False and inp == oup
print(a)# False
print(0 and False)# 0

总而言之,and这个语句,优先级相对较判等语句低,第一个参数作为判定语句,为真时,不论第二个值为何,直接输出,为假时候则直接输出第一个参数,也就是,他的输出只可能是两个值,False/0 或者是第二个参数

数组切片操作[::2]/[1::2]

x=[1,2,3,4,5,6]
x[::1]  # [1, 2, 3, 4, 5, 6]
x[::2]  # [1, 3, 5]
x[::3]  # [1, 4]
x[1::2] # [2, 4, 6]
x[2::2] # [3, 5]

由代码可知::2代表的是间隔两个取一个数,1::2代表偏置为1,缺省为0

维数超过 3 的多维数组,可通过 ‘…’ 来简化操作

arr[1, …] 等价于 arr[1, :, :]

arr[…, 1] 等价于 arr[:, :, 1]

numpy中对切片元素的操作会影响原数组本身

math.gcd() 求最大公约数

import math 
math.gcd(15,3)# 3
math.gcd(5,3) # 1

// 的含义

Python 3以后 " / “表示 浮点数除法,返回浮点结果;” // "表示整数除法,

for _ in range(n)

_ 在 Python 中用于表示“我正在丢弃这个结果”,尤其是在 for 循环等语法上需要变量但不会使用的地方。它实际上是一个普通的变量,可以读取,但不要这样做,它很混乱。

意思是,用在不需要调用循环计数变量的循环场景

*[list]

列表前面加星号作用是将列表解开成多个独立的参数,传入函数。

另外字典前面加两个星号,是将字典解开成独立的元素作为形参。

举个例子:

print(*[3 for _ in range(3)])# 3 3 3
print(2,3,4)			    # 2 3 4
print([3 for _ in range(3)]) # [3, 3, 3]

根据输出结果可以看出两个语句的区别

nn 相关的几个函数

nn.Conv2d

nn.Conv2d(self, in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros')

​ in_channel: 输入数据的通道数,例RGB图片通道数为3;
​ out_channel: 输出数据的通道数,这个根据模型调整;
​ kennel_size: 卷积核大小,可以是int,或tuple;kennel_size=2,意味着卷积大小2, kennel_size=(2,3),意味着卷积在第一维度大小为2,在第二维度大小为3;
​ stride:步长,默认为1,与kennel_size类似,stride=2,意味在所有维度步长为2, stride=(2,3),意味着在第一维度步长为2,意味着在第二维度步长为3;
​ padding: 零填充
​ dilation:控制卷积核点之间的距离,如图,蓝色输入,青色输出

dilation


​ groups:分组卷积,将对应的输入通道与输出通道数进行分组, 默认值为1, 也就是说默认输出输入的所有通道各为一组。 比如输入数据大小为90x100x100x32,通道数32,要经过一个3x3x48的卷积,group默认是1,就是全连接的卷积层。
​ 如果group是2,那么对应要将输入的32个通道分成2个16的通道,将输出的48个通道分成2个24的通道。对输出的2个24的通道,第一个24通道与输入的第一个16通道进行全卷积,第二个24通道与输入的第二个16通道进行全卷积。
​ bias:卷积后是否加偏移量

nn.Sequential

每一层的输出作为下一层的输入,这种前馈nn可以不用每一层都重复的写forward()函数,通过Sequential()和ModuleList(),可以自动实现forward。这两个函数都是特殊module,包含子module。ModuleList可以当成list用,但是不能直接传入输入

下面是官网描述

​ 一个顺序容器。模块将按照它们在构造函数中传递的顺序添加到其中。或者,可以传入一个OrderedDict模块。Sequentialforward() 方法接受任何输入,并执行其它包含的第一个模块的forward。然后它将输出“链接”到每个后续模块的输入,最后返回最后一个模块的输出。

A sequential container. Modules will be added to it in the order they are passed in the constructor. Alternatively, an OrderedDict of modules can be passed in. The forward() method of Sequential accepts any input and forwards it to the first module it contains. It then “chains” outputs to inputs sequentially for each subsequent module, finally returning the output of the last module.

Sequential手动调用一系列模块的价值在于,它允许将整个容器视为单个模块,这样对 Sequential执行转换操作将同时适用于它存储的每个模块(每个模块都是Sequential的注册子模块)。

The value a Sequential provides over manually calling a sequence of modules is that it allows treating the whole container as a single module, such that performing a transformation on the Sequential applies to each of the modules it stores (which are each a registered submodule of the Sequential).

Sequentialtorch.nn.ModuleList什么不一样?ModuleList正是它听起来的样子— 一个用于存储Modules的列表!另一方面,Sequential中的层以级联方式连接。

What’s the difference between a Sequential and a torch.nn.ModuleList? A ModuleList is exactly what it sounds like–a list for storing Module s! On the other hand, the layers in a Sequential are connected in a cascading way.

# Using Sequential to create a small model. When `model` is run,
# input will first be passed to `Conv2d(1,20,5)`. The output of
# `Conv2d(1,20,5)` will be used as the input to the first
# `ReLU`; the output of the first `ReLU` will become the input
# for `Conv2d(20,64,5)`. Finally, the output of
# `Conv2d(20,64,5)` will be used as input to the second `ReLU`
model = nn.Sequential(
          nn.Conv2d(1,20,5),
          nn.ReLU(),
          nn.Conv2d(20,64,5),
          nn.ReLU()
        )

# Using Sequential with OrderedDict. This is functionally the
# same as the above code
model = nn.Sequential(OrderedDict([
          ('conv1', nn.Conv2d(1,20,5)),
          ('relu1', nn.ReLU()),
          ('conv2', nn.Conv2d(20,64,5)),
          ('relu2', nn.ReLU())
        ]))

下面来说下这个语句

nn.Sequential(*[Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)])

意思是按照第一种初始化方式,把n个Bottleneck放到Sequential容器中,参数完全一致,顺序执行。

几个小函数的语句翻译

autopad

def autopad(k, p=None):  # kernel, padding
    # Pad to 'same'
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad
    return p

判定 p是否为None ,如果不为None 直接返回p,如果为None,判定 k的数据类型是不是int,如果是,返回k//2(k除以二取整结果),否则返回k中的每个函数都除以2取整的结果。

综上,本函数功能为:对两个对象操作,如果p不为空,直接返回p,如果p为空,返回k//2,对单个数k,直接除法,对数组k,每个元素都除以2,并返回。

在本函数中应用于求填充函数,输入参数k的含义为卷积核的维度,如果k=3,即3*3的卷积核,如果k = [3,4]则为3 * 4的卷积核。由此求解 填充参数padding。

语句翻译

关于nn.Module

根据整理到的情报已知,神经网络层的主干都是要继承nn.Module,然后继承的东西重写两个玩意,一个是初始化,一个是forward,然后再nn.Module的类中会再被调用时候,在一个call函数中调用forward函数将输入参数传进去

个人理解,nn.Module继承出来的部分,主要用于图像的处理(卷积,池化等操作),也就是前向传播部分,反向传播部分(损失函数,以及参数更新等)一般是处于另一个体系。但他们也都基本属于一个torch的框架。

Conv

定义了一个卷积类Conv,继承nn.Module

构造函数的初始化变量有两个必输入项c1,c2,后面跟可选输入项k=1(卷积核),s=1(步长),p=none,g=1,act=true

主动调用父类的无参数初始化构造函数

并在自己的构造函数中赋予函数方法Conv.conv,Conv.bn,Conv.act

class Conv(nn.Module):
    # Standard convolution
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())
  • nn.Conv2d : 前面说过,卷积操作,输入通道c1,输出通道c2,卷积核维度k,步长s,
def forward(self, x):
    return self.act(self.bn(self.conv(x)))

def fuseforward(self, x):
    return self.act(self.conv(x))

Bottleneck

继承nn.Module,

cv1是Conv(c1,e*c2,1,1)(输入层为c1,输出层为e *c1,卷积核为1 步长为1)

cv2是Conv(e*c2,c2,3,1)(输入层为c1,输出层为e *c1,卷积核为3 *3 步长为1)

short = True 且c1=c2 时,add = True 否则add = False

add = True : 返回x+cv2(cv1(x)); 否则:返回cv2(cv1(x))

C3

cv1 : Conv(c1,e*c2,1,1)(输入层为c1,输出层为e *c1,卷积核为1 步长为1)

cv2 : Conv(c1,e*c2,1,1)(输入层为c1,输出层为e *c1,卷积核为1 步长为1)

cv3 : Conv(2* e* c2,c2,1)(输入层为2* e* c2,输出层为c2,卷积核为1 步长为1)

m:上一层输出做下一层输入,循环执行n次Bottleneck(e* c2,e* c2,shortcut,g,1),此时满足c1=c2,故应执行加法

forward:

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

define_mine

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值