目录
- 模型量化原理
- 前言
- 1. What、Why and How
- 1.1 What
- 1.2 Why
- 1.3 How
- 2. 拓展-export参数详解
- 3.总结
- 参考
模型量化原理
前言
手写AI推出的全新TensorRT模型量化课程,链接。记录下个人学习笔记,仅供自己参考。
本次课程为第一课,主要讲解量化的定义及意义。
课程大纲可看下面的思维导图
1. What、Why and How
问题:什么是模型量化?为什么需要量化?如何去量化?
1.1 What
什么是模型量化?
定义:量化(Quantization)是指将高精度浮点数(如float32)表示为低精度整数(如int8)的过程,从而提高神经网络的效率和性能
常见的模型量化技术包括权重量化和激活量化(from chatGPT)
- 权重量化是将浮点参数转换为整型参数,常用的量化方法包括对称量化和非对称量化
- 对称量化是将权重范围均匀地分配在正负两个方向,将浮点数映射到一个整数范围中。
- 非对称量化则将权重范围分配在一个方向,即只使用非负整数表示
- 激活量化是将输入和输出数据转换为低比特宽度地数据类型,同样可以采用对称量化和非对称量化两种方法
需要注意的是,模型量化会对模型的精度和准确度产生一定的影响,因为量化后的模型可能无法完全保留原始模型中的所有信息和特征。因此,在进行模型量化时需要进行适当的权衡和优化。
我们平时训练出的模型如YOLOv5、ResNet50正常导出默认都是FP32的精度,现在我们来看下ResNet50导出的ONNX模型的输入、权重、偏置以及输出的数据类型,ResNet50导出至ONNX的代码如下:
import torch
import torchvision.models as modelsmodel = models.resnet50(pretrained=True)input = torch.randn(1, 3, 224, 224)
torch.onnx.export(model, input, "resnet50.onnx")
运行后可在当前文件夹下生成resnet50.onnx模型,可以Netron可视化工具查看,如下图所示,可以看到其输入、输出、权重以及偏置的类型均为float32。
1.2 Why
为什么要学习模型量化?Copy自神经网络量化教程
我们都知道,训练好的模型模型权重一般来说是FP32即单精度浮点型,在深度学习训练和推理的过程中,最常用的精度就是FP32。如下图所示
其中FP32是单精度浮点数,采用32位二进制数表示,其中1位为符号位,8位为指数位,23位为尾数位。FP16是半精度浮点数,采用16位二进制数表示,其中1位为符号位,5位为指数位,10位为尾数位。对于浮点数来说,指数位表示该精度可达到的动态范围,而尾数位表示精度。INT8是8位整数,采用8位二进制数表示,其中1位为符号位,7位为数值位。
从FP32=>FP16是一种量化(如下图所示),只不过因为FP32=>FP16几乎是无损的(CUDA中使用__float2half直接进行转换),不需要calibrator去校正、更不需要retrain。并且FP16的精度下降对于大部分任务影响不是很大,甚至有些任务会提升。NVIDIA对于FP16有专门的Tensor Cores可以进行矩阵运算,相比于FP32来说吞吐量直接提升一倍,提速效果明显
实际来说,量化就是将我们训练好的模型,不论是权重、还是计算op,都转换为低精度去计算。实际中我们谈论的量化更多的是INT8量化。
意义:在深度学习中,量化有以下优势:
-
减少内存占用,模型容量变小,如FP32权重变成INT8,大小直接缩小了4倍数
-
加速计算,实际卷积计算的op是INT8类型,在特定硬件下可以利用INT8的指令集去实现高吞吐,不论是GPU还是INTEL、ARM等平台都有INT8的指令集优化
-
减少功耗和延迟,有利于嵌入式侧设备的应用
-
量化是模型部署中的一种重要的优化方法,可以在部分精度损失的前提下,大幅提高神经网络的效率和性能
1.3 How
如何去量化?
即解决方案:先来看两个例子
如何将一个浮点数(5.214)用int的方式进行描述?
- 计算线性映射的缩放值Scale
- 量化操作
- 反量化操作
上述例子描述了量化的两个重要过程:一个是量化(Quantize),另一个是反量化(Dequantize)
- 量化就是将浮点数量化为整型数(FP32=>INT8)
- 反量化就是将整型数转换为浮点数(INT8=>FP32)
如何将一个浮点数组[-0.61,-0.52,1.62]用int的方式进行描述?
当然你可以存储多个Scale值,然后按照上面的方式进行量化和反量化操作,但是这样一来存储空间增加,计算资源浪费,能不能用一个Scale搞定呢?
- 计算数组共同的Scale
Scale = (float_max - float_min) / (quant_max - quant_min) = (1.62-(-0.61)) / (127 - (-128)) = 0.0087109
- 量化操作
-0.61 / 0.0087109 = -70.0272072 -0.52 / 0.0087109 = -59.6953242 ==> [-70,-59,185] 取整 1.62 / 0.0087109 = 185.9738947
- 截断
[-70,-59,185] ==> [-70,-59,127]
- 反量化
[-0.609763,-0.5139431,1.1062843]
可以看到截断的数值最后反量化与原数值相差较大(1.62与1.1062843)
如何解决这个问题:1、最大绝对值对称法 2、偏移
2. 拓展-export参数详解
torch.onnx.export()
是将PyTorch模型导出为ONNX格式的函数。其参数如下:
- model:需要导出为ONNX格式的PyTorch模型
- args:模型的输入参数,可以是一个tensor或者一个包含多个tensor的元组
- f:导出的ONNX模型的保存路径或文件对象
- export_params:是否导出模型参数,默认为True
- verbose:是否打印导出过程信息,默认为False
- do_constant_folding:是否进行常量折叠优化,默认为True
- input_names:模型输入节点的名称,默认为[“input”]
- output_names:模型输出节点的名称,默认为[“output”]
- dynamic_axes:动态轴,可以用来标记输入和输出节点的动态维度
- opset_version:导出的ONNX版本号,默认为9
- keep_initializers_as_inputs:是否将初始化器保存为输入节点,默认为False
- operator_export_type:导出的算子类型,默认为torch.onnx.OperatorExportTypes.ONNX
3.总结
本次课程主要了解了量化的一些基础知识,期待后续课程😏
参考
- 神经网络量化教程