最近在工作中大量使用了yolov8的迁移学习来作为实现工程化应用小样本学习的问题,在这里特意记载几个遇到的网络上比较少相关资料的问题点,作为自己日后学习和复盘的一个记录。
先说项目概要:通过yolov8迁移学习,实现类似增量学习的功能,并且通过类似嫁接的方法合成一个新的模型并且转换为 tensorRT应用在现场。具体的方法就不详细记录了,涉及到商业方面的问题。
主要遇到的问题可以概括为以下两个:
1. 使用ultralytics官方方法冻结backbone后进行训练后,各个backbone的输出仍然产生变化,无法达到绝对的一致;
2. 一些潜在的转换为tensorRT后产生的pt模型与trt模型输出差距较大的问题。
一、冻结backbone参数仍然发生变化
首先来说第一个问题,即backbone即使在冻结后也会发生参数的变化,在进行嫁接的时候会对后续的输出产生较大影响,对检测产生不稳定的不可知影响。按常规的理解来说,通过ultralytics里面给的方法,即freeze=10这种就可以使得backbone的参数完全冻结,但是在实际使用中,发现backbone的参数还是有一定变化的,那么是什么原因呢?
为了解决这个问题,我主要参考了一些csdn的文章(具体来源不记得了,有人知道的可以和我说下),总的来说问题大概是出在这两个地方:1. 官方框架的freeze方法对于一些bn层里的mean之类的参数没有实际冻结到;2. EMA的开启会对冻结backbone的参数产生一定影响。
1、BN层有一些参数没有实际冻结到:
这个问题很多博文也有提到,具体是否真的产生了变化我也暂时没有考证,之后可以进一步探讨,该文只记录如何手动在ultralytics框架里面冻结所需要所有参数。比如我的冻结层数是22,进入到yolov8的训练部分的代码,具体位置在ultralytcis/engine/trainer.py里面,位置大约是在326行的循环里面加上:
for layer in layers_to_freeze:
layer.eval()
2. EMA的开启会对冻结backbone产生影响
首先EMA是一种平滑训练方法,具体的公式原理大家可以去相关博文下面看,这里就不过多阐述原理了。通俗的理解是,EMA会使得整体训练更加平滑,在训练刚开始阶段会让参数的更新更加倾向于新数据,当迭代次数多了之后,参数的更新更加接近于历史数据。在标准yolov8框架下,在 ultralytics\utils\torch_utils.py中,无论是否对层进行冻结,都会进行ema的更新,具体修改方法如下:在class ModelEMA的update更新函数下面,改成这样,这里我的冻结层数是22。
def update(self, model):
"""Update EMA parameters."""
if self.enabled:
self.updates += 1
d = self.decay(self.updates)
msd = de_parallel(model).state_dict() # model state_dict
for k, v in self.ema.state_dict().items():
if v.dtype.is_floating_point: # true for FP16 and FP32
#冻结多少层就对应修改model后面的数字
if 'model.22' in k and 'dfl' not in k:
#print(f'updating EMA :{k}')
v *= d
v += (1 - d) * msd[k].detach()
assert v.dtype == msd[k].dtype == torch.float32, f'{k}: EMA {v.dtype}, model {msd[k].dtype}'
通过上面的两个修改,实测是可以实现完全冻结backbone的,原理原因这些还需要进一步的学习和复盘。
二、TensorRT模型和PT模型精度差距较大
这个问题在我的算法里面主要就是不小心把amp关掉了,开启amp之后精度差距又回到了正常的范围。这其中的原因有可能是
1. 混合精度训练与推理精度的关系
AMP 简介
AMP训练通过混合使用16位浮点数(FP16)和32位浮点数(FP32)来加速训练过程,同时减少显存使用量。许多现代GPU(如NVIDIA的Volta、Turing和Ampere架构)都支持高效的FP16运算。
TensorRT 优化
TensorRT是一个高性能深度学习推理库,它也广泛支持FP16运算。启用FP16模式可以显著提升推理速度,并减少显存使用。
训练与推理的一致性
- 使用AMP进行训练:模型在训练过程中已经适应了FP16和FP32之间的数值变化和精度损失。当你将这种模型转换为TensorRT并在FP16模式下进行推理时,由于模型在训练过程中已经处理过FP16的数值范围和精度,推理时的数值表现会更稳定且一致。
- 不使用AMP进行训练:模型在训练过程中只使用FP32浮点数,因此它可能没有适应FP16的数值范围和精度。转换为TensorRT并在FP16模式下进行推理时,可能会引入更多的数值误差和不稳定性,导致精度下降。
2. 数值稳定性和精度损失
在使用FP16进行计算时,数值的表示范围和精度比FP32要低得多。这可能导致一些数值不稳定性和精度损失。当模型在训练过程中没有经历过FP16的数值范围和精度时,其在FP16推理过程中的表现可能会不如预期。
3. TensorRT的优化手段
TensorRT在进行模型转换和优化时,可能会进行一些特有的操作,如层融合、权重量化等。这些操作在FP16精度下可能会引入一定的数值误差。如果模型在训练过程中已经使用过AMP,那么这些操作的影响可能会被最小化,因为模型已经适应了FP16的上下文。
4. 测试不同的精度模式
如果你发现精度损失较大,可以尝试以下几种方法来缓解:
- 使用FP32模式:在TensorRT转换过程中,可以指定使用FP32模式进行推理,这样可以避免FP16带来的数值误差,但会增加计算成本和显存使用。
- 混合精度模式:可以尝试使用混合FP16和FP32的模式,来平衡精度和性能。
结论
在使用AMP进行训练时,模型已经适应了FP16和FP32之间的数值变化和精度损失,因此在转换为TensorRT并启用FP16推理模式时,精度变化较小。而未使用AMP进行训练的模型在FP16推理模式下可能会遇到更多的数值误差和不稳定性,导致精度下降。
为了确保转换后的模型在TensorRT中具有较好的精度表现,建议在训练过程中启用AMP,并仔细调试和测试不同的精度模式。