1.参考文献
链接1
2.网络模型解析
2.1卷积神经单元(conv.py)
在该文件中定义了yolov8网络中的卷积神经单元,位置如图所示。
def autopad(k, p=None, d=1): # kernel(卷积核), padding(填充), dilation(扩张)
"""Pad to 'same' shape outputs."""
# 返回pad的大小,使得padding后输出张量的shape不变
if d > 1: # 如果采用扩张卷积,则计算扩张后实际的kernel大小
k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
if p is None:
p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad # 自动pad
return p
2.1.1代码解释
- 扩张卷积的处理:
if d > 1: # 如果采用扩张卷积,则计算扩张后实际的kernel大小
k = d * (k - 1) + 1 if isinstance(k, int) else [d * (x - 1) + 1 for x in k] # actual kernel-size
这部分代码的作用是,如果 d
大于1,则计算扩张卷积后的实际卷积核大小。对于扩张卷积,其有效卷积核大小会增大:
- 如果
k
是一个整数,新的卷积核大小为d * (k - 1) + 1
。 - 如果
k
是一个列表,则对列表中的每一个元素x
进行相同的计算,生成新的列表。
- 自动填充计算:
if p is None:
p = k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad # 自动pad
这部分代码的作用是,如果没有显式指定填充大小 p
(即 p
为 None
),则自动计算填充大小以保持输出形状不变:
- 如果
k
是一个整数,则填充大小为k // 2
。 - 如果
k
是一个列表,则对列表中的每一个元素x
进行x // 2
的计算,生成新的列表。
- 返回填充大小:
return p
最后返回计算出的填充大小 p
。
2.1.2示例
1:普通卷积,3x3卷积核
假设我们有一个3x3的卷积核,扩张率 d=1
(即没有扩张),并且没有指定填充 p
。
k = 3
p = autopad(k, p=None, d=1)
print(p) # 预期输出: 1
- 计算过程:
- 没有扩张,卷积核大小保持3x3。
- 自动填充
p
计算:p = k // 2 = 3 // 2 = 1
。
输出的填充大小为1,这意味着每边需要填充1个像素,使得输出张量的形状与输入张量相同。
2:扩张卷积,3x3卷积核,扩张率2
假设我们有一个3x3的卷积核,扩张率 d=2
,并且没有指定填充 p
。
k = 3
p = autopad(k, p=None, d=2)
print(p) # 预期输出: 2
- 计算过程:
- 扩张后,实际卷积核大小:
k = d * (k - 1) + 1 = 2 * (3 - 1) + 1 = 5
。 - 自动填充
p
计算:p = k // 2 = 5 // 2 = 2
。
输出的填充大小为2,这意味着每边需要填充2个像素,使得输出张量的形状与输入张量相同。
3:矩形卷积核,5x7卷积核
假设我们有一个5x7的矩形卷积核,扩张率 d=1
,并且没有指定填充 p
。
k = [5, 7]
p = autopad(k, p=None, d=1)
print(p) # 预期输出: [2, 3]
- 计算过程:
- 没有扩张,卷积核大小保持5x7。
- 自动填充
p
计算:p = [x // 2 for x in k] = [5 // 2, 7 // 2] = [2, 3]
。
输出的填充大小为 [2, 3]
,这意味着高度方向需要填充2个像素,宽度方向需要填充3个像素,使得输出张量的形状与输入张量相同。
4:矩形卷积核,5x7卷积核,扩张率2
假设我们有一个5x7的矩形卷积核,扩张率 d=2
,并且没有指定填充 p
。
k = [5, 7]
p = autopad(k, p=None, d=2)
print(p) # 预期输出: [4, 6]
- 计算过程:
- 扩张后,实际卷积核大小:
k = [d * (x - 1) + 1 for x in k] = [2 * (5 - 1) + 1, 2 * (7 - 1) + 1] = [9, 13]
。 - 自动填充
p
计算:p = [x // 2 for x in k] = [9 // 2, 13 // 2] = [4, 6]
。
输出的填充大小为 [4, 6]
,这意味着高度方向需要填充4个像素,宽度方向需要填充6个像素,使得输出张量的形状与输入张量相同。