Bootstrap

四. TensorRT模型部署优化-quantization(quantization granularity)

前言

自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》,链接。记录下个人学习笔记,仅供自己参考

本次课程我们来学习课程第四章—TensorRT 模型部署优化,一起来学习量化粒度的选择

课程大纲可以看下面的思维导图

在这里插入图片描述

0. 简述

本小节目标:量化粒度是什么,如何正确的选择量化粒度,以及量化粒度与精度/计算量/计算效率的关系

这节我们学习量化的第二个小部分—Quantization Granularity(量化粒度),首先我们会讲量化粒度是什么以及为什么需要考虑它,然后讲下量化粒度的一个正确的选择方式是什么,我们什么时候选择什么样的量化粒度,以及这个量化粒度它跟精度,计算量和计算效率之间的关系是什么。

下面我们开始本次课程的学习🤗

1. 量化粒度

量化粒度Quantization Granularity)是量化中有一个非常重要的概念,也是很容易被大家忽视的一个概念,我们在利用 tensorRT 做模型部署的时候其实很少会去接触这个东西,因为 tensorRT 内部其实已经帮你选好了一个最好的量化粒度方案,那这个的话就是 tensorRT 的开发人员他们通过大量的实验得到的一个普遍性的方案。但值得我们注意的是这种普遍性的方案并不是说适用所有的模型,不是说每一个模型的量化都用同一种策略是最好的,我们需要根据不同的情况自己去修改一些量化方案中的量化粒度

那量化粒度到底指的是什么呢?量化粒度主要描述的是量化操作的细致程度或者说作用的局部范围,它其实指的是对于一个 tensor 以多大的粒度去共享 scale 和 z 或者 dynamic range,常见的量化粒度主要有以下几种:

  • per-tensor quantization
  • per-channel quantization
  • per-element quantization

在这里插入图片描述

per-tensor quantization 量化粒度方式我们可以理解为一个 tensor 中所有的 element,所有的 FP32 的数据量化成 INT8 数据时都共享同一个 dynamic range,共享同一个动态范围。上面的左图体现了 per-tensor 量化粒度,其中大的方块我们可以将它理解为一个 tensor,包含 H,W,C 三个维度,其中的每个小方块代表 tensor 中的一个 element 元素,左图中所有小方块都是同一个颜色代表在量化时这个 tensor 中所有的 element 的动态范围都是共享的

per-channel quantization 量化粒度我们可以理解为一个 tensor 中每一个 channel 它都有一个自己的 dynamic range,上面中间的图体现了 per-channel 量化粒度,我们可以看到每一个 channel 的小方块都是同一个颜色,不同 channel 的小方块颜色不同,这就代表在量化时这个 tensor 中每个 channel 中的 element 的动态范围都是共享的

per-element quantization 量化粒度我们可以理解为一个 tensor 中每一个 element 都有一个属于自己的 dynamic range,上面的右图体现了 per-element 量化粒度,我们可以看到每一个小方块的颜色都不同,这就代表在量化时这个 tensor 中每个元素都有一个动态范围,不共享

从上面的描述中我们可以知道其实每种量化粒度它代表的计算量和计算精度不同,比如 per-tensor 量化粒度中所有 element 的动态范围共享,也就是说存储的 scale 和 zero-point 其实都一样只要存储一份就行,计算比较方便;而像 per-element 量化粒度中所有 element 都有一个动态范围,那需要存储 scale 和 zero-point 的空间可能会比较大,计算起来也比较麻烦

2. Per-tensor & Per-channel量化

对于三种不同的量化粒度,比较常见的是 per-tensor 和 per-channel 量化粒度,那我们下面来着重分析这两种量化粒度,它们有什么区别,各自的优缺点有哪些呢?

我们先来看 per-tensor 量化,如下图所示:

在这里插入图片描述

per-tensor

上面图中展示的是一个 tensor 中两个不同 channel 的数据分布,我们可以看到虽然这两个 channel 的数据分布有所不同,但是在 per-tensor 量化粒度下它们选择的 dynamic range 动态范围都是相同的

per-tensor 量化的优缺点我们可以总结如下:

  • 优点:低延迟,一个 tensor 共享同一个量化参数
  • 缺点:高错误率,一个 scale 很难覆盖所有 FP32 的 dynamic range

相比之下我们来看看 per-channel 的量化,如下图所示:

在这里插入图片描述

per-channel

从图中我们可以清晰的看到 per-channel 和 per-tensor 的区别,对于 tensor 中每一个 channel 不同的数据分布 per-channel 量化粒度下它都有不同的量化参数即 dynamic range 动态范围都是不同的

per-channel 量化的优缺点我们可以总结如下:

  • 优点:低错误率,每一个 channel 都有自己的 scale 来体现这个 channel 中的数据的 dynamic range
  • 缺点:高延迟,需要使用 vector 来存储每一个 channel 的 scale

那现在我们可以思考一下,既然 per-tensor 和 per-channel 量化粒度各有优缺点,那我们什么情况下该用 Per-tensor,什么情况下该用 Per-channel 呢?

3. 量化粒度选择的推荐方法

重点!NVIDIA 从很多实验结果与测试中对于量化粒度的选择总结出了一个一般性的经验,那就是对于 activation values 的量化方法一般会选取 per-tensor 量化,而对于 weights 的量化方法一般会选取 per-channel 量化

在这里插入图片描述

上图展示了典型的 convolution 算子的量化过程,Input Activation Feature Maps 由于是 per-tensor 量化粒度,因此量化系数只有一个,相反 Filter Weights 是 per-channel 量化粒度,因此量化系数有多个,不同的 channel 都有属于自己的 dynamic range,这个就是 per-tensor 和 per-channel 量化粒度的一个选择

那我们来思考下为什么 weight 需要 per-channel 量化粒度呢?为什么不选择和 activation values 相同的 per-tensor 量化粒度呢?这个其实主要有以下两点考虑:

  • BN 计算与线性计算的融合(BN folding)
  • depthwise convolution

我们先来看下 BN 层的计算公式,如下所示:

在这里插入图片描述

另外我们之前的课程中有提到过 Conv 和 BN 一般是可以融合的,因为二者都是线性变换,具体融合公式如下:

在这里插入图片描述

因此线性变换 y = w ∗ x y=w*x y=wx 的 BN folding 虽然可以把 BN 的参数融合在线性计算中,但是因为 BN 的参数是 per-channel 的,如果 weight 用 per-tensor 的话会掉精度,所以因为 BN 的存在我们对于 weights 的量化方法一般会选择 per-channel

另外一个原因是因为 depthwise convolution,如下图所示:

在这里插入图片描述

depthwise convolution

depthwise convolution 中 kernel 的 channel size 是 1,每一个 kernel 针对输入的对应的 channel 做卷积,所以每一个 channel 中的参数可能差别会比较大,如果用 per-tensor 的话容易掉精度比较严重。

下面是一些韩君老师做的一些关于量化粒度选择的实验结果:

模型精度top1 score
MobileNetFP3271.88
MobileNetINT8 Per-channel weight quantization71.56
MobileNetINT8 Per-tensor weight quantization66.88
EfficientNetFP3276.85
EfficientNetINT8 Per-channel weight quantization76.72
EfficientNetINT8 Per-tensor weight quantization12.93

一般像 MobileNet 中存在 depthwise convolution,它在 FP32 的精度是 71.88,INT8 量化时对于权重如果选择的是 per-channel 的量化方式它的精度是 71.56 掉点不是那么严重,但是一旦把权重的量化方式修改为 per-tensor 之后其精度掉点特别严重变成了 66.88,下降了 5%

另外一个模型 EfficientNet 它里面也有 depthwise convolution,因此也有和 MobileNet 同样的问题,在做 INT8 量化时如果权重选择 per-tensor 量化其精度直接变成了 12.93,那几乎整个模型就崩溃了,这个是完全不可取的da

由此可见,我们对于权重的量化粒度选择一般来说都是 per-channel

值得注意的是,目前的 TensorRT 已经默认对于 Activation values 选用 Per-tensor,Weights 选用 Per-channel,这是他们做了多次实验所得出的结果。很多其他平台的 SDK 可能不会提供一些默认的量化策略,这时我们需要谨慎选择,尽快找到掉点的原因

总结

本次课程我们学习了量化粒度以及量化粒度的选择,针对于 activation values 我们一般会选择 per-tensor 的量化方式,而针对于 weights 我们一般会选择 per-channel 的量化方式,这是因为 BN 和 depthwise convolution 的存在如果选择 per-tensor 掉点比较严重。此外 tensorRT 内部其实已经帮我们选好了量化方式,而针对于其他的平台我们需要慎重选择

OK,以上就是量化的第二个小部分量化粒度的全部内容了,下节我们来学习 Calibration 校准,敬请期待😄

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;