手写AI推出的全新TensorRT模型量化课程,链接。记录下个人学习笔记,仅供自己参考。
本次课程为第三课,主要讲解动态范围的常用计算方法。
课程大纲可看下面的思维导图
在之前的课程中我们学习了对称量化和非对称量化的知识,在tensorRT中的INT8量化使用的方法就是对称量化。上节课提出在对称量化中存在一个问题,就是当数据中存在极端值时,会对量化精度造成不利影响,这节课我们就一起来学习相关解决方案。
首先来看下本次课程的题目,动态范围的常用计算方法,之前似乎没有提过呀,有点抽象()。
动态范围(Dynamic Range)指的是输入数据中数值的范围,计算动态范围是为了确定量化时使用的比特位数(还是抽象)。个人理解计算动态范围就是为了获得更好的Scale,毕竟Scale会影响到整个量化的精度,而Scale的计算和输入数据的值域范围息息相关(即值域的动态范围),在上节课中非对称量化过程中Scale计算为 S c a l e = ( R m a x − R m i n ) ( Q m a x − Q m i n ) Scale = \frac{(Rmax-Rmin)}{(Qmax-Qmin)} Scale=(Qmax−Qmin)(Rmax−Rmin),而对称量化过程中的Scale计算为 S c a l e = ∣ R m a x ∣ Q m a x Scale = \frac{|Rmax|}{Qmax} Scale=Qmax∣Rmax∣,都与输入数据的数值范围相关。
现在来看看动态范围的计算方法,动态范围的计算方法与量化的方式相关,对称量化和非对称量化使用的计算方法略有不同。在对称量化中,通常采用的是
输入数据的绝对值的最大值作为动态范围的计算方法
;而在非对称量化中,通常采用最小值和最大值的差作为动态范围的计算方法
。经过上面分析就不难理解题目动态范围的常用计算方法,在之前对称量化中的动态范围的计算方法就是
Max
方法即采取输入数据的绝对值的最大值,但是这种计算方法存在问题,就是容易受到离散点即噪声的干扰,我们的考虑改进,或者说采取其它的动态范围计算方法,也就是本节课程的Histogram以及Entropy方法。
常用的动态范围计算方法包括:(from chatGPT)
对称量化和非对称量化的选择与动态范围的计算方法有一定的关系。对称量化要求量化的最大值和最小值的绝对值相等,可以采用Max方法或Histogram方法进行计算。非对称量化则可以采用Entropy方法进行计算,以最小化量化后的误差。
直方图(histogram)是统计学中常用的一种图形,它将数据按照数值分组并统计每组数据的出现频率,然后将频率用柱状图的方式表示出来。直方图通常用于描述一组数据的分布情况,可以帮助人们了解数据的特征,例如数据的中心位置、离散程度、对称性、峰态等(from chatGPT)
下面是一张直方图的示例:
下面是该直方图生成的示例代码:
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(1000)
plt.hist(data, bins=50)
plt.title("histgram")
plt.xlabel("value")
plt.ylabel("freq")
# plt.savefig("histgram.png", bbox_inches="tight")
plt.show()
在上述代码中首先使用numpy
生成了一组包含1000个随机数的数据,然后调用matplotlib
库中的plt.hist()
函数生成该数据的直方图。其中data
参数表示要绘制的数据,bins
表示直方图的柱子数量,这里设置为50个。
histogram方法
为什么能克服Max方法
中离散点即噪声干扰问题呢?主要在于直方图统计了数据出现的频率,它可以将数据按照一定的区间进行离散化处理,并计算每个区间中数据点的数量。这种方法相对于Max方法
来说,能够更好地反映数据的分布情况,从而更准确地评估数据的动态范围。我们假设数据服从正态分布,即离散点在两边,我们可以通过从两边向中间靠拢的方法,去除离散点,类似于双指针的方法,算法具体流程如下:
下面是该算法流程的一个简单示例:
其中0~5代表算法执行的步骤,首先双指针位于直方图的左右区间,然后计算其覆盖率发现不满足设定的阈值要求,计算此时的左指针的直方图值小于右指针的直方图值,左指针右移即步骤1,继续上述步骤发现覆盖率还是不满足,且此时右指针值小故右指针左移即步骤2,以此类推到步骤3、步骤4,到步骤4计算发现覆盖率满足阈值要求,故将此时的双指针的直方图值的绝对值返回即可。
通过上面的分析,示例代码如下:
def scale_cal(x):
max_val = np.max(np.abs(x))
return max_val / 127
def histogram_range(x):
hist, range = np.histogram(x, 100)
total = len(x)
left = 0
right = len(hist) - 1
limit = 0.99
while True:
cover_percent = hist[left:right].sum() / total
if cover_percent <= limit:
break
if hist[left] > hist[right]:
right -= 1
else:
left += 1
left_val = range[left]
right_val = range[right]
dynamic_range = max(abs(left_val), abs(right_val))
return dynamic_range / 127
if __name__ == "__main__":
np.random.seed(1)
data_float32 = np.random.randn(1000).astype('float32')
# print(f"input = {data_float32}")
scale = scale_cal(data_float32)
scale2 = histogram_range(data_float32)
print(f"scale = {scale} scale2 = {scale2}")
上述代码中histogram_range
函数是基于数据直方图计算缩放因子的,该函数先将数据进行直方图统计,然后使用双指针算法去除数据直方图中的离散点,最后通过剩余数据的动态范围计算出缩放因子。
Histogram方法
虽然能够解决Max方法
中的离散点噪声问题,但是使用数据直方图进行动态范围的计算,要求数据能够比较均匀地覆盖到整个动态范围内。如果数据服从类似正态分布,则直方图的结果具有参考价值,因为此时的数据覆盖动态范围的概率较高。但如果数据分布极不均匀或出现大量离散群,则直方图计算的结果可能并不准确。此时可考虑其它的动态范围计算方法。
np.histogram
是用于生成直方图的函数,其参数和返回值如下:
参数:
a
:待处理的数据,可以是一维或者多维数组,多维数组将会被展开成一维数组bins
:表示数据分成的区间数range
:表示数据的取值范围,可以是一个元组或数组density
:是否将直方图归一化。如果为True,直方图将归一化为概率密度函数。默认Falseweights
:每个数据点的权重,可以是一维数组或与a
数组相同的形状的数组。默认为None,表示所有的数据点权重相同。返回值:
hist
:一个长度为bins
的一维数组,表示每个区间中数据点的数量或者归一化后的概率密度值。bin_edges
:长度为bins + 1
的一维数组,表示每个区间的边界。本次课程学习了动态范围计算中的直方图计算,该方法可解决Max方法中离散点问题,但同时要求数据的分布要均匀,不能出现过多的离散群。期待下次的动态范围计算方法Entropy