模型量化(基于pytorch)
1、量化简介
1.1、量化介绍
-
基于pytorch的量化官方地址https://pytorch.org/docs/stable/quantization.html
-
量化的作用:
减小模型大小(减小四倍左右),方便模型部署到小型设备中(如低成本嵌入式设备) -
量化的数据:
weight的8 bit量化 :data_type = qint8,数据范围为[-128, 127]
activation的8 bit量化:data_type = quint8,数据范围为[0, 255]
<1>一般我们都将float32量化到qint8。
<2>模型参数中的bias一般是不进行量化操作的,仍然保持float32的数据类型。
<3>weight在浮点模型训练收敛之后一般就已经固定住了,所以根据原始数据就可以直接量化。
<4>activation会因为每次输入数据的不同,导致数据范围每次都是不同的,所以针对这个问题,在量化过程中专门会有一个校准过程,即提前准备一个小的校准数据集,在测试这个校准数据集的时候会记录每一次的activation的数据范围,然后根据记录值确定一个固定的范围。
1.2、量化方法
1.2.1、训练后动态量化(Post Training Dynamic Quantization)
官方地址:torch.quantization.quantize_dynamic()
<1>顾名思义,就是在训练完成后进行量化。
<2>编译器中书写方法
Class torch.quantization.quantize_dynamic(model, qconfig_spec=None, dtype=torch.qint8, mapping=None, inplace=False)
<3>将float型model转化为动态量化(仅weight)model。
<4>最简单的是将模型变为float16或qint8,且动态量化只针对一些权重大的层,例如Linear或RNN。
<5>代码示例(假设我们需要写了一个名叫lstm_for_demonstration的类,其中包括nn.LSTM层与nn.Linear层,我们将该类实例化为float_lstm再进行量化)
# 实例化类
float_lstm = lstm_for_demonstration(model_dimension, model_dimension,lstm_depth)
#进行动态量化(量化nn.LSTM, nn.Linear层为qint8)
quantized_lstm = torch.quantization.quantize_dynamic(
float_lstm, {nn.LSTM, nn.Linear}, dtype=torch.qint8
)
<6>动态量化公式讲解,请看这里:https://blog.csdn.net/kuan__/article/details/108928491
查看公式后请注意:动态量化系统自动选择最合适的scale (标度)和 zero_point(零点位置),不需要自定义。量化后的模型,可以推理运算,但不能训练(不能反向传播)
1.2.2、训练后静态量化(Post Training Static Quantization)
官方地址:torch.quantize_per_tensor()
<1>训练后进行量化。
<2>静态量化之前,可能需要修改模型,请参考此处:静态量化前修改模型
<3>训练后的静态量化不仅包括将权重从浮点数转换为整数,还执行额外的步骤,首先通过网络输入一批数据并计算不同激活的结果分布(具体来说,这是通过插入观察者完成记录此数据的不同点的模块)。然后使用这些分布来确定在推理时如何具体量化不同的激活(一种简单的技术是将整个激活范围简单地划分为 256 个级别,但也支持更复杂的方法)。重要的是,这个额外的步骤允许我们在操作之间传递量化值,而不是在每个操作之间将这些值转换为浮点数 - 然后再转换回整数,从而显着加快速度。
<5>静态量化公式讲解,请看这里:https://blog.csdn.net/kuan__/article/details/108928491
查看公式后请注意:scale (标度)和 zero_point(零点位置)需要自定义。量化后的模型,不能训练(不能反向传播),也不能推理,需要解量化后,才能进行运算
1.2.3、量化意识训练
官方地址:https://pytorch.org/tutorials/advanced/static_quantization_tutorial.html
<1>系统自动选择最合适的scale (标度)和 zero_point(零点位置),不需要自定义。但这是一种伪量化,量化后的模型权重仍然是32位浮点数,但大小和8位定点数权重的大小相同。伪量化后的模型可以进行训练。虽然是以32位浮点数进行的训练,但结果与8位定点数的结果一致
2、总结与注意事项
<1>个人觉得静态量化和量化意识训练都比较麻烦,做简单的量化可以选择动态量化。直接调用函数即可。
<2>有在wenet上做过动态量化。能够将513M的模型量化到180M左右;测试的CER与不做量化前基本保持一致,但仍有0.1-0.2(cer为字符错误率)的差距;RTF更高。
<3>在wenet中对已训练好的模型进行stage6动态量化后,得到量化后的模型quant_model.zip,这里用于C++,我们也可以修改代码生成python测试可用的.pt等文件类型。
<4>wenet中实验:在测试文件里量化我们原始model,便能进行python上的推理。代码如下(recognize.py)
# Init asr model from configs
model = init_asr_model(configs)
quantized_model = torch.quantization.quantize_dynamic(model, {torch.nn.Linear}, dtype=torch.qint8)