一、与yoloV4相比,yoloV5的改进
- 输入端:在模型训练阶段,使用了Mosaic数据增强、自适应锚框计算、自适应图片缩放
- 基准网络:使用了FOCUS结构和CSP结构
- Neck网络:在Backbone和最后的Head输出层之间插入FPN_PAN结构
- Head输出层:训练时的损失函数GIOU_Loss,预测筛选框的DIOU_nns
二、yolov5网络结构
预处理
在模型预处理阶段,使用了Mosaic数据增强、自适应锚框计算、自适应图片缩放
**CutMix:**将2张图进行拼接
**Mosaic:**在CutMix基础上进行改进,采用4张图片,按照随机缩放,随机裁剪和随机排布的方式进行拼接
**优点:**将几张图组合成一张图,不仅可以丰富数据集,极大的提升网络训练速度,降低模型内存
自适应锚框计算(可选)
就是model的yaml的
anchors:
- [10,13, 16,30, 33,23]
- [30,61, 62,45, 59,119]
- [116,90, 156,198, 373,326]
分别对应预设的不同尺度的锚框的长宽,这些值是预先通过K-means聚类 得到的,每个长宽比都是一个点,通过统计每个框的长宽比得到一组具有代表形的锚框
在yolo系列算法中,针对不同的数据集,都需要设定特定长宽的锚点框。在网络训练阶段,模型在初始阶段,模型在初始锚点框的基础上输出对应的预测框,计算其与GT框之间的差距,并执行反向更新操作,从而更新整个网络的参数,因此设定初始锚点框是比较关键的一环。
在yoloV3和yoloV4中,训练不同的数据集,都是通过单独的程序运行来获得初始锚点框。而在yoloV5中将此功能嵌入到代码中,每次训练,根据数据集的名称自适应的计算出最佳的锚点框,用户可以根据自己的需求将功能关闭或者打开,指令为:
parser.add_argument("-nuautoanchor", action='store_true', help='disable autoancher check') # 如果需要打开,只需要在训练代码时增加'-nuautoanchor'选项即可
自适应图片放缩,
若原图为1000x800,网络输入为640x640,注意并不是等比例放缩的,而是按照更长边的比例放缩,短的边就用黑色填充
黑边填充数值:
- 计算需要填充黑边宽度:104
- yoloV5执行5次下采样:2^5=32,对该数值取模:104%32=8
- 将填充区域分散到两边:8/2=4
网络结构
1、Focus结构:
原始的640x640x3的图像输入FOCUS结构,采用切片操作(邻近下采样、类似于swin的plat操作),先变成320x320x12的特征图,再经过一次卷积操作,最终变成320x320x32的特征图。
2、CSP结构
- CSP1_X结构应用于Backbone主干网络中
- CSP2_X结构应用于Neck网络中
- 其实就是n个带残差的卷积 + 1个残差卷积
3、SPP结构
多个并行的maxpool操作+cat,注意C3就是:CSP Bottleneck with 3x3 Convolution
v5主要使用Leaky ReLU
它是 ReLU 的变体。与 ReLU 在输入为负数时输出 0 不同,Leaky ReLU 在输入为负数时输出一个较小的负数值。这可以缓解 ReLU 的"死亡神经元"问题,即一些神经元在训练过程中可能会完全停用。
以上网络为backbone,实现5倍下采样,最终下采样倍数为5倍,即大小为20、40、80
4、FPN+PAN的结构
yoloV5的Neck仍采用了FPN+PAN结构,FPN层自顶向下可以捕获强语义特征,而PAN则通过自底向上传达强定位特征,通过组合这两个模块,可以很好的完成目标定位的功能。
借一下yolov8的图,来描述这个neck,为什么需要上采样,因为融合不同尺度的特征
损失函数
yolov5有三个损失函数分别是回归损失(边界框)、置信度损失(对象损失)、分类损失
回归损失用的是CIOU loss 置信度和分类损失用的是BCE loss,分别是二分类和多分类
IoU Loss :即预测框与GT框之间的交集/预测框与GT框之间的并集。这种损失存在一些问题:
(1)如状态1所示,当预测框和GT框不相交时,即IoU=0,此时无法反映两个框之间的距离,此时该损失函数不可导,即IoU Loss无法优化两个框不相交的情况。(2)如状态2和状态3所示,当两个预测框大小相同时,那么这两个IoU也相同,IoU Loss无法区分两者相交这种情况。
GIoU Loss:为了解决以上的问题,GIoU损失应运而生,GIoU Loss中增加了相交尺度的衡量方式,缓解了单纯IoU Loss时存在的一些问题。
DIoU Loss:针对IoU和GIoU损失所存在的问题,DIoU为了解决如何最小化预测框和GT框之间的归一化距离这个问题,DIoU Loss考虑了预测框与GT框的重叠面积和中心点距离,当GT框包裹预测框的时候,直接度量2个框的距离,因此DIoU Loss的收敛速度更快一些。
CIoU Loss:在DIoU Loss的基础上增加了一个影响因子,将预测框和GT框的长宽也考虑进来。计算方法如下:将GT框的重叠面积、中心点距离和长宽比全部考虑进来
IOU_Loss主要考虑了检测框和GT框之间的重叠面积。
GIOU_Loss在IOU的基础上,引入最小外接矩形及外接矩形与并集的差集
DIOU_Loss在IOU和GIOU的基础上,同时考虑了边界框中心点距离信息。
CIOU_Loss在DIOU的基础上,又考虑了边界框宽高比的尺度信息。
后处理
NMS:非极大值抑制用于去除重复的边界框,只保留置信度最高的边界框,过程不赘述
训练的文件与指标
查准率
与confidence成正比,好理解
查全率
与confidence成反比,好理解
准确率
用的少
PR曲线
即以Recall为横坐标,Precision为纵坐标组成的曲线
AP(PR曲线下面积)
平均精度,不是严格的面积
mAP50(mean Average Precision)
在iou=50%的各类别的平均精度
F1-Score
查准率和查全率的综合评估指标
BECWithLogits loss
计算objectness score和class probability score的损失,结合了Sigmiod和BCELoss函数
三、yolov8
yolov8主要涉及到:backbone 使用C2f模块,检测头使用了anchor-free + Decoupled-head,损失函数使用了分类BCE、回归CIOU + DFL(新增项目)的组合,框匹配策略由静态匹配改为了Task-Aligned Assigner匹配方式、最后 10 个 epoch 关闭 Mosaic 的操作、训练总 epoch 数从 300 提升到了 500。
预处理
基础输入仍然为640*640,预处理如:缩放、填充、mosiac等
网络结构
1)连续使用两个3*3卷积直接降低了4倍分辨率。
最终下采样倍数为5倍,即大小为20、40、80
2)c2f 模块
残差分支+多个带残差的卷积主分支+split操作,split操作通常用于将特征图分成多个部分,以便进行不同的处理或用于不同的路径。
SiLU
SiLU 也被称为 Swish 激活函数,它类似于线性函数和 Sigmoid 函数的组合,是一种平滑且具有非线性特性的激活函数。
3)sppf 模块
对比v5的spp,将简单的并行max pooling 改为串行+并行的方式。对比如下(左边是SPP,右边是SPPF):
neck & head
检测头以及匹配机制
Head 部分变化最大,从原先的耦合头变成了解耦头,并且从 YOLOv5 的 Anchor-Based 变成了 Anchor-Free。
Yolov5: 检测和分类共用一个卷积(coupled head)并且是anchor based ,其 卷积输出为(5+N class)*3,其中 5为bbox 四个值+ 一个obj 值 (是否有目标),N class 为类别数,3为anchor 的数量,默认是3个
YOLOv8:检测和分类的卷积是解耦的(decoupled),如右图,上面一条支路是框的卷积,框的特征图channel为4*regmax,关于这个regmax 后面我们详细的解释,并不是anchor;分类的channel 为类别数。
因此主要的变化可以认为有三个:1)coupled head -> decoupled head ;2)obj 分支消失;3)anchor based------> anchor free
1. Anchor-Based
v5中Anchor-Based的使用方式为one_stage------在网络的多尺度上展开anchor boxes,以直接预测物体的类别和 anchor box偏移量。
v5中目标的反算为:特征图数据 * 预设锚框参数的宽高 = 目标的位置(并不是直接预测目标的位置)。即回归
其中特征图数据则包含了中心点偏移量、宽高偏移量等数据。
2. Anchor-Free
Anchor-Based的局限性
①Anchor的设置需要手动去设计(长宽比,尺度大小,anchor的数量),对不同数据集也需要不同的设计,相当麻烦。
②Anchor的匹配机制使得极端尺度(特别大/小的object)被匹配到的频率,相对于大小适中的object被匹配到的频率更低,网络在学习时不容易学习这些极端样本。
③Anchor的庞大数量使得存在严重的不平衡问题,涉及到采样、聚类的过程。但聚类的表达能力在复杂情况下是有限的
④Anchor-Based为了兼顾多尺度下的预测能力,推理得到的预测框也相对较多,在输出处理时的nms计算也会更加耗时。
原理
训练中直接学习各种框形状。推理时不依靠聚类 ,而是根据学习到的边框距离/关键点位置,拟合物体尺寸。
v8中使用的方法是:Center-based methods------基于中心点的方法。先找中心/中心区域,再预测中心到四条边的距离。
v8中目标的反算为:在特征图每个grid(特征图中的单个框)上,预测框四条边与grid中心点的偏移值 ("左上右下"相对于中心点的距离)。
个人理解:Anchor-Free通过不依赖数据集中的先验知识,使网络对"物体形状"有更好的表达能力,更具泛化潜能。在运动物体、尺寸不一物体检测上有所提升,同时检测被遮挡物体时也能更加灵活。
3、样本分配策略
正样本总很少,负样本(即无目标区域)总太多。会导致模型学习不均衡。因此,我们需要样本分配策略来解决这一问题。
1. 静态策略
静态分配策略是训练开始之前就确定的,这种分配策略通常基于经验得出,可以根据数据集的特点进行调整。但是不够灵活,可能无法充分利用样本的信息,导致训练结果不佳。
损失函数
Loss 计算包括 2 个分支: 分类和回归分支,没有了之前的 objectness 分支。
分类损失:
回归损失
基于CIOU,虽然v8的CIOU使用方式与v5保持一致,但引入Anchor-Free的Center-based methods(基于中心点)后,模型从输出"锚框大小偏移量(offest)"变为"预测目标框左、上、右、下边框距目标中心点的距离(ltrb = left, top, right, bottom)"。
为配合Anchor-Free、以及提升泛化性,在v8中,增加了DFL损失( Distribution Focal Loss**)** 。DFL以交叉熵的形式,去优化与标签y最接近的一左一右2个位置的概率 ,从而让网络更快的聚焦到目标位置及邻近区域的分布。
在实际训练中,DFL将与CIOU损失结合使用。
在第一部分 ,通过DFL对"边界框分布概率"和标签的"分布概率"进行损失计算,从而对每条边进行优化。
在第二部分,将"边界框分布概率"还原为预测框,通过CIOU损失对预测框和标签的"实际框"进行损失计算,从而对预测框整体进行优化。
yolo中训练和推理时不一样的地方
如图像预处理,NMS等
目标检测相关知识
两阶段检测和一阶段检测
代表算法分别是fastrcnn、yolo,都是利用了锚框,区别在于:
**两阶段的fastrcnn(准):**Region Proposals+RoI Pooling,即预设锚框,再将生成的候选区域(区域提议)映射到卷积特征图上,并通过 RoI Pooling 操作特区锚框的特征,类似于LaneATT
一阶段的yolo(快):YOLO 将图像划分为网格(grid),每个网格负责预测特定区域的目标。以直接预测相对于网格单元的中心和尺寸的偏移量,以及边界框的宽度和高度的偏移量。
过拟合和欠拟合
过拟合是指模型在训练数据上表现很好,但在未见过的测试数据上表现不佳的现象。换句话说,模型对训练数据的噪声和细节进行了过度学习,导致它无法很好地泛化到新数据上
解决:减少模型复杂度、正则化、数据增强
欠拟合是指模型在训练数据和测试数据上都表现不佳的现象。模型无法捕捉到数据中的潜在模式,导致训练误差和测试误差都较高
解决:增加模型复杂度、减少正则化
梯度消失和梯度爆炸
梯度消失:通常发生在深层网络中,当梯度从输出层反向传播到输入层时,经过多个层次的链式法则,梯度值可能会变得非常小,几乎无法对权重进行有效的更新。
**原因:网络太深、激活函数问题:**如 Sigmoid 和 Tanh,在输入值较大或较小时,会导致梯度变得非常小。
**梯度爆炸:**梯度值变得非常大,导致网络的权重更新过度,模型参数发散,训练过程不稳定。梯度爆炸使得模型参数更新幅度过大,可能导致训练失败或模型不收敛。
原因:
- 激活函数问题:一些激活函数(如在激活值较大时)可能导致梯度值快速增大。
- 深层网络:梯度在经过多个层次的链式法则传播时,可能会逐渐增大。
- 大学习率:过大的学习率会导致梯度更新过度,从而引发梯度爆炸。
解决:
合适的激活函数、合适的权重初始化方法、调整学习率
激活函数的了解
**sigmoid:**输出范围固定在 0 到 1,适合二分类问题。在输入较大或较小时梯度接近于零,导致梯度消失;梯度计算较慢。
**Tanh:**常用于隐藏层,比 Sigmoid 更适合处理数据,因为输出范围是 −1-1−1 到 111。输入过大或过小时仍然会导致梯度消失。
**ReLU:**计算简单,能有效缓解梯度消失问题。但可能导致"死亡 ReLU"问题,即某些神经元在训练过程中始终输出 0,无法更新。
**Leaky ReLU:**它是 ReLU 的变体。与 ReLU 在输入为负数时输出 0 不同,Leaky ReLU 在输入为负数时输出一个较小的负数值。这可以缓解 ReLU 的"死亡神经元"问题
softmax和sigmoid
softmax:适合多分类问题,将输出的类别转换为和为1的概率分布
sigmoid:适合二分类问题,映射到0-1的值,但和不为1,只是起一个映射方法的作用:大的越大,小的越小
归一化、正则化
正则化 是用于防止模型过拟合 的一种技术,通过在损失函数中添加额外的约束,来减少模型的复杂度,从而提高模型的泛化能力。常见的正则化技术包括:L1、L2、Droupt随机丢弃
批量归一化(Batch Normalization,BN) 是一种用于加速训练和稳定模型的方法,通过对每一层的输入进行归一化处理,使其具有均值为零和方差为一的分布。能够缓解梯度消失和梯度爆炸问题,加速模型的训练过程,通常使模型收敛更快。
inpalce操作
Inplace 操作 是指在计算过程中直接修改输入变量的值,若为false:创建新的变量来存储结果
残差网络为什么能解决梯度消失的问题
同时Resnet确实也存在解决梯度消失的作用,主要作用机理是跳跃连接的存在使得求导时可以将倒数直接回传到上一层,实现梯度的跨层传播
描述一下各类优化器及其公式
-
梯度下降
-
随机梯度下降(sgd)
-
小批量梯度下降
-
Adagrad
-
Adam
python 深拷贝浅拷贝的区别
在浅拷贝时,拷贝出来的新对象的地址和原对象是不一样的,但是新对象里面的可变元素(如列表)的地址和原对象里的可变元素的地址是相同的, 因此对原对象的元素改变时,新对象也会随之改变
深拷贝会对原对象里的可变元素指向新地址,原对象的改变不会对新对象造成影响。
python列表和元组区别
列表是动态数组,它们不可变且可以重设长度(改变其内部元素的个数)。
元组是静态数组,它们不可变,且其内部数据一旦创建便无法改变。
元组缓存于Python运行时环境,这意味着我们每次使用元组时无须访问内核去分配内存
python 生成器和迭代器什么区别?
都是用于迭代遍历时的
迭代器:迭代器是一个对象,实现了 __iter__()
和 __next__()
方法,允许你逐个遍历元素,直到没有元素可遍历。
class MyIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index < len(self.data):
result = self.data[self.index]
self.index += 1
return result
else:
raise StopIteration
# 使用自定义迭代器
my_iter = MyIterator([1, 2, 3])
for item in my_iter:
print(item)
生成器::生成器是使用 yield
关键字的函数,每次调用 yield
时,生成器会返回一个值并记住当前的执行状态,以便下次迭代继续执行。
def my_generator():
yield 1
yield 2
yield 3
# 使用生成器
gen = my_generator()
for item in gen:
print(item)
Dice loss的优缺点。
优点:对于正负样本不均衡的情况效果好。
缺点:对小样本训练波动大; 拟合不平滑,如果没有IoU则网络不知道该怎么引导梯度进行衰减。
python的装饰器
装饰函数,在不修改代码的前提下,封装一些其他简易的功能,使用:@func
python的一些魔法函数
也称为双下划线方法(Dunder Methods)
def repr(self)当直接打印类的实例化对象时,系统将会自动调用该方法,输出对象的自我描述信息,用来告诉外界对象具有的状态信息 def str(self),好像和repr类似
def len(self):同上
__getitem__(self, key)
:获取指定键的值,obj[key]
访问时使用。
@property 将方法变成属性调用,调用时不加括号,并且它的属性类似私有属性,无法进行赋值,由构造函数就定好
@staticmethod:静态方法:可以在不实例化的情况下从类中调用该方法,没有self参数,且无法用任何init的参数,就跟一个扎根在类中的外部函数一样。
@classmethod:可以在不实例化的情况下从类中调用该方法,没有self参数,但需要一个'cls'参数,且可以用类的属性,方法,可以用init的参数