EVO Quant
在 ONNX(Open Neural Network Exchange)标准中,量化(Quantization)和反量化(Dequantization)是用于减少模型大小和提高推理效率的重要操作。ONNX 提供了一些专门用于量化和反量化的算子,它们可以帮助将浮点数值映射到整数范围,并在推理时将其转换回浮点表示。
ONNX 的常见量化/反量化算子有:
QuantizeLinear
这是用于将浮点数量化为整数(如 INT8 或 UINT8)的 ONNX 算子。
输入:
- 输入张量(浮点数)
- scale:量化比例系数(浮点数)
- zero_point:零点,用于表示在量化范围内的零(可选,整数,默认值为 0)
输出:
用途:将浮点数值映射到一个整数值区间,用于量化推理。
示例:
quantized_tensor = QuantizeLinear(input_tensor, scale, zero_point)
DequantizeLinear 是用于将量化的整数数据反量化回浮点数值的 ONNX 算子。
输入:
输入张量(量化后的整数张量)
scale:量化比例系数(浮点数)
zero_point:量化零点(可选,整数)
输出:
反量化后的浮点数张量
用途:
将量化整数值转换回原始的浮点数值,用于模型推理中的精度恢复。
示例:
dequantized_tensor = DequantizeLinear(quantized_tensor, scale, zero_point)
量化/反量化流程的示例
在 ONNX 模型中,量化/反量化的常见操作如下:
量化:在推理开始之前,将输入的浮点数张量通过 QuantizeLinear 算子转换为整数张量。
反量化:在推理中间或结束时,将整数张量通过 DequantizeLinear 算子恢复为浮点数。
Example of quantizing and dequantizing in ONNX:
import numpy as np
import onnx
import onnxruntime as ort
Example float tensor
input_tensor = np.array([0.1, 0.2, 0.3], dtype=np.float32)
scale = np.array([0.02], dtype=np.float32)
zero_point = np.array([0], dtype=np.uint8)
Quantize the float tensor
quantized_tensor = QuantizeLinear(input_tensor, scale, zero_point)
Dequantize back to float
dequantized_tensor = DequantizeLinear(quantized_tensor, scale, zero_point)
其他相关的 ONNX 算子
虽然 QuantizeLinear 和 DequantizeLinear 是最常用的量化/反量化算子,但在具体模型中可能还会使用其他与量化相关的操作,例如:
MatMulInteger 和 ConvInteger:
这些是量化推理中常用的算子,用于整数矩阵乘法和卷积运算。
这些算子接受量化后的整数张量作为输入,而不是浮点数。
QLinearMatMul 和 QLinearConv:
这是支持量化数据的线性矩阵乘法和卷积运算的专用量化版本。它们支持直接在量化的数据上进行操作,避免反量化回浮点数进行计算。
总结:
- QuantizeLinear:用于将浮点数值量化为整数(如 INT8)。
- DequantizeLinear:用于将量化的整数数据恢复为浮点数。
- MatMulInteger、ConvInteger、QLinearMatMul 和 QLinearConv:提供了对量化数据的操作支持,常用于推理中的矩阵乘法和卷积计算。
常见量化方法
在实现不同的量化方法时,每种方法都有其适用的场景和优缺点。以下是 对称量化、非对称量化、动态范围量化 和 对数量化 的优缺点分析:
1. 对称量化 (Symmetric Quantization)
优点:
- 简单易用:对称量化只需要缩放因子(scale),不需要零点(zero point),实现起来简单。
- 计算高效:计算过程中仅需进行乘法和除法,硬件实现比较容易。
- 对称数据适用:对于分布大致对称的数据(如零均值的数据分布),对称量化表现良好。
缺点:
- 对非对称数据效果较差:当数据分布不对称时(例如数据的正负范围不一致),对称量化会浪费较多的表示空间,使得精度下降。
- 不适合零点偏移:当数据有较大的偏移(如数据集中在某个正或负区域),会造成量化范围不平衡,影响精度。
2. 非对称量化 (Asymmetric Quantization)
优点:
- 适应不对称数据:非对称量化通过引入零点
zero_point
,可以适应数据的偏移,使得量化表示范围更加合理。
- 更精确:可以更好地处理数据的偏移情况,避免量化过程中浪费大量表示空间。
缺点:
- 实现复杂:相比对称量化,非对称量化增加了零点的计算和使用,复杂度略高。
- 计算代价略高:在计算时需要引入零点偏移,虽然相比对称量化略显复杂,但通常对性能影响不大。
3. 动态范围量化 (Dynamic Range Quantization)
优点:
- 自适应数据范围:动态范围量化能够根据数据的最大值和最小值自适应缩放因子,确保使用最合适的量化范围。
- 适合全局缩放:对于全局数据分布跨度较大的情况,动态范围量化能够充分利用整数空间,提高表示精度。
- 适用于较宽数据分布:尤其适用于数据分布较广泛的情况,比如有些数据可能跨越多个数量级。
缺点:
- 动态计算开销:在量化前需要对数据进行扫描以找到最大值和最小值,额外引入了计算开销。
- 对局部动态变化不敏感:如果数据在不同区域的分布差异较大,单一的动态范围量化无法很好地处理局部数据的变化,可能会损失局部精度。
4. 对数量化 (Logarithmic Quantization)
优点:
- 适合跨度大的数据:对于分布跨度较大的数据(如数据范围在多个数量级上变化),对数量化表现优异。它可以压缩大范围的数据到较小的整数范围。
- 更均匀分布表示空间:对数缩放使得数据在较大范围内的变化更加均匀,适合处理有指数增长或对数分布的数据。
缺点:
- 低精度数据问题:对于较小范围的数据(如接近零的数据),对数量化可能丢失较多的精度,因为对数缩放会把较小的值压缩得过小。
- 计算复杂性:对数量化需要使用对数和指数运算,这些运算在某些硬件上可能更昂贵,尤其是对于嵌入式系统或硬件推理来说。
- 需要特殊处理 0:因为
log(0)
是未定义的,通常需要对 0 做特殊处理(如添加小偏移值),这可能引入额外的复杂性。
总结
量化方法 |
优点 |
缺点 |
对称量化 |
简单易实现,适合对称分布的数据,计算高效 |
不适合偏移较大的数据,难以处理非对称数据 |
非对称量化 |
能处理非对称数据,引入零点增加精度,适合偏移数据 |
实现稍复杂,需计算零点 |
动态范围量化 |
动态适应数据分布范围,适合全局数据分布跨度大的情况 |
动态计算开销大,无法适应局部分布变化 |
对数量化 |
适合数据范围跨度大的情况,对指数分布或变化快速的数据效果好 |
低精度时效果差,计算较复杂,需要处理 0 的特殊情况 |
不同的量化方法适用于不同的场景:对称量化和非对称量化通常用于神经网络推理优化,而动态范围量化和对数量化则更适用于跨越大范围或指数变化的数据。在实际应用中,可以根据具体的任务需求选择合适的量化方法。
以下是从易到难列举的具体量化算法,每种算法均附有公式,并且涵盖了从简单到复杂的量化方法:
1. 简单 Min-Max 对称量化
- 方法描述:假设数据是对称分布的,仅使用 Scale,不考虑 Zero Point。
- 公式:
[
\text{scale} = \frac{\max(|\text{float_min}|, |\text{float_max}|)}{127}
]
- 映射:将浮点数 ( x ) 转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{x}{\text{scale}}\right)
]
- 适用范围:对称权重量化、分布较均匀的数据。
2. 简单 Min-Max 非对称量化
- 方法描述:适用于数据分布不对称的情况,增加了 Zero Point 的计算。
- 公式:
[
\text{scale} = \frac{\text{float_max} - \text{float_min}}{255}
]
[
\text{zero_point} = \text{round}\left(-\frac{\text{float_min}}{\text{scale}}\right)
]
- 映射:将浮点数 ( x ) 转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{x}{\text{scale}}\right) + \text{zero_point}
]
- 适用范围:激活量化、非对称数据。
3. 最大绝对值对称量化(Max-Abs Symmetric Quantization)
- 方法描述:通过找到数据的最大绝对值,假设数据在
[-\text{abs\_max}, \text{abs\_max}]
对称分布。
- 公式:
[
\text{scale} = \frac{\text{abs_max}}{127}
]
其中 (\text{abs_max} = \max(|\text{float_min}|, |\text{float_max}|))。
- 映射:将浮点数 ( x ) 转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{x}{\text{scale}}\right)
]
- 适用范围:对称分布的激活量化,适合对称分布。
4. 逐通道量化(Per-Channel Quantization)
- 方法描述:对每个通道单独量化,计算每个通道的 Scale 和 Zero Point。
- 公式:
[
\text{scale}_i = \frac{\text{float_max}_i - \text{float_min}_i}{255}
]
[
\text{zero_point}_i = \text{round}\left(-\frac{\text{float_min}_i}{\text{scale}_i}\right)
]
- 映射:对于通道 ( i ) 上的浮点数 ( x ),转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{x}{\text{scale}_i}\right) + \text{zero_point}_i
]
- 适用范围:权重量化,特别适合卷积层。
5. 均值-标准差量化(Mean-Std Quantization)
- 方法描述:通过均值和标准差调整量化范围,减少极值影响。
- 公式:
- 量化范围为 ([ \text{mean} - k \times \text{std}, \text{mean} + k \times \text{std} ]),其中 ( k ) 是可调参数。
- Scale:
[
\text{scale} = \frac{(\text{mean} + k \times \text{std}) - (\text{mean} - k \times \text{std})}{255}
]
- Zero Point:
[
\text{zero_point} = \text{round}\left(-\frac{\text{mean} - k \times \text{std}}{\text{scale}}\right)
]
- 映射:将浮点数 ( x ) 转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{x}{\text{scale}}\right) + \text{zero_point}
]
- 适用范围:减小极值影响的数据量化。
6. 动态范围量化(Dynamic Range Quantization)
- 方法描述:推理时根据输入数据的动态范围实时调整 Scale 和 Zero Point。
- 公式:对于每次推理输入计算
float_min
和 float_max
,按非对称量化方式计算 Scale 和 Zero Point:
[
\text{scale} = \frac{\text{float_max} - \text{float_min}}{255}
]
[
\text{zero_point} = \text{round}\left(-\frac{\text{float_min}}{\text{scale}}\right)
]
- 映射:将浮点数 ( x ) 转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{x}{\text{scale}}\right) + \text{zero_point}
]
- 适用范围:推理过程中输入数据分布变化大的情况。
7. KL 散度量化(KL-Divergence Quantization)
- 方法描述:基于最小化量化分布与原始数据分布的 KL 散度,找到最优量化范围。
- 公式:
- 构建直方图,将数据分布划分为 ( n ) 个 bin。
- 计算截断点,依次尝试多个截断范围 ( T ),计算量化直方图 ( Q ) 和原始直方图 ( P ) 的 KL 散度:
[
D_{KL}(P || Q) = \sum_{i=1}^n P(i) \log\left(\frac{P(i)}{Q(i)}\right)
]
- 选择 KL 散度最小的截断范围,生成量化 Scale 和 Zero Point。
- 映射:将浮点数 ( x ) 转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{x}{\text{scale}}\right) + \text{zero_point}
]
- 适用范围:适合复杂数据分布的激活量化。
8. 对数量化(Logarithmic Quantization)
- 方法描述:将数据按对数刻度量化,适合处理动态范围大的数据。
- 公式:
- 对数刻度量化范围:
[
\text{scale} = \frac{\log(\text{float_max}) - \log(\text{float_min})}{255}
]
[
\text{zero_point} = \text{round}\left(-\frac{\log(\text{float_min})}{\text{scale}}\right)
]
- 映射:将浮点数 ( x ) 转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{\log(x)}{\text{scale}}\right) + \text{zero_point}
]
- 适用范围:适合动态范围跨度大的数据,如音频和亮度。
9. 感知量化(Perceptual Quantization, PQ)
- 方法描述:根据人类视觉或听觉的感知非线性分布进行量化。
- 公式:
- 按感知函数(例如对数、幂律等)计算量化范围,使得每个 bin 更符合人类感知的敏感性。
- 假设采用对数函数,则 Scale 计算为:
[
\text{scale} = \frac{\log(\text{float_max} + 1)}{255}
]
[
\text{zero_point} = 0
]
- 映射:将浮点数 ( x ) 转换为量化整数 ( q ):
[
q = \text{round}\left(\frac{\log(x +
1)}{\text{scale}}\right)
]
- 适用范围:人类感知相关的数据量化,例如图像和音频数据。