很多刚入门模型量化的同学经常在后台问我:那么多 pytorch 模型量化工具,到底有什么区别?初学者要选择哪个学习?
刚好最近在学习公司量化工具 MQBench 的源码,今天就简单聊聊这个问题。
其实我自己实际用过的量化工具并不多,有些只是简单看过一些代码和文档,而部署的平台也基本以手机上的 DSP/NPU/APU 芯片为主。
下面这张表总结了我比较了解的几个量化工具 (有些只看过一些文档,有错误请指出):
公司 | 量化工具 | 推理引擎 | 部署平台 |
---|---|---|---|
Intel | NNCF | OpenVino | x86 CPU |
高通 | AIMet | SNPE/QNN | 高通 DSP/NPU 等芯片 |
MTK | Neuropilot | Neuron SDK | 联发科 APU 芯片 |
Nvidia | TensorRT | TensorRT | 英伟达部分 GPU 芯片 |
商汤 | MQBench | -- | -- |
腾讯 | NCNN | NCNN | 多种 CPU 平台 |
Meta | pytorch | Libtorch | arm/x86 CPU |
其中,除了 NCNN 外,其他工具都集成了 pytorch 量化训练的功能。另外,为什么 MQBench 没有对应的推理引擎和平台,这个后面再讲。
很多刚入门的同学常问:为什么不直接用 pytorch 官方推出的量化工具呢?
还有一些同学用 pytorch 官方量化训练了一个模型后,问怎么部署到高通芯片上。
......
看了上面这张表后,大家应该会有一个基本印象:不同量化训练工具能部署的推理框架和平台,是不一样的。这就意味着,如果你要把量化的模型部署到高通的 DSP/APU 平台,你就得用高通的 AIMet 进行量化训练然后导出模型。
同样地,如果要用 Nvidia 的 GPU 来部署,就得用 TensorRT。
很繁琐有没有。
对于 fp32 的模型 (全精度模型),不管是用 tensorflow 还是 pytorch 训练的模型,都可以很方便地转换到 tflite/SNPE/NCNN 等平台,做到一次训练多平台部署。为什么到了量化这里就不行了呢?
我个人觉得,一个很重要的原因是,量化这块缺乏统一的标准,各个芯片产商都在抢夺市场,各自为战,因此具体到量化推理框架上,各家的算法实现也有很大差异。此外,量化推理和硬件本身有很强的关联性,不同芯片由于支持的指令集和硬件单元的差异,也会导致量化算法有一些差别。
不过,好消息是,这些量化框架基本都遵循 Google 白皮书里的规范 (不清楚的同学欢迎看我之前的系列教程),因此大体上量化算法是类似的。
接下来回到最初的问题:这些工具之间的差异在哪呢?
最近刚好看了琦哥的分享,这里面的差异主要在三点:
这里所说的量化方式主要指,
采用的是线性量化还是非线性量化。目前大部分推理框架都支持线性量化,少部分支持非对称量化 (像海思的 NNIE 这种极少数情况)。
采用的是 per-channel 量化还是 per-layer 量化。比如, libtorch、NCNN 采用的就是 per-channel 量化的方式,早期的 SNPE 采用的是 per-layer 量化。随着这些推理引擎的进步,大部分框架会逐步支持这两种量化方式。
采用的是对称量化还是非对称量化。这里面又分为 weight 和 feature map 的量化。比如 TensorRT 在 weight 上采用的就是对称量化的方式,而有些框架对这两种量化方式都支持。
通常来说,对于每一个 weight 和 feature map,在量化训练时应该都会插入伪量化节点以统计数值范围,并做梯度回传。但在有些推理引擎中,有些 layer 的量化参数是沿用前面 layer 的量化参数。
比如,在 MTK 的 Neuron SDK 中, interpolate 和 avgpooling 这两个 layer 会共享前后节点的量化参数。再比如,有些推理引擎在 Add、Concat 这些节点也会共享前后的量化参数。
不同的量化训练框架,会针对自己对应的推理引擎设计自己的伪量化节点插入方式。如果你使用了某个平台的量化框架,那么在转换到其他平台的推理框架时,就需要事先根据其他平台的特点,相应地修改量化训练框架的代码。否则很大可能要么转模型失败,要么实际推理的精度不如量化训练的精度。
除此以外,还有些比较特殊的设置,比如 Conv-ReLU、Conv-BN-ReLU 这样的组合,在大部分框架中会合并成一个卷积,但不排除有一些框架会有自己特殊的设置。
再比如,我在使用 MTK 的量化工具时,发现在量化推理的时候,有些 layer 强制要求输出的通道数要大于等于 4,而其他平台并没有这种要求,这就导致你在其他平台量化好的模型,在 MTK 的 APU 上用不了。
这部分差异属于特定平台的特性,需要踩坑摸索才能逐步了解。
上面提到的三点都是跟推理引擎相关的差异,还有最后一点,跟工具本身的设计有关。看过我之前文章的读者,或者使用过较多量化框架的应该知道,pytorch 的量化工具虽然多,但大部分不好用。由于 pytorch 动态图的设计,很多工具在量化复杂网络的时候,需要手工修改很多代码,从而给初学者带来很大的负担。
这部分差异可能是小白用户最关心的问题,总的来说,像 AIMet、pytorch eager quantization 这些工具,由于不能自动跟踪网络前向的代码,使用起来可能需要很多手工的操作,而具备自动代码跟踪的框架,比如 NNCF 以及 torch.FX 加持的 MQBench,自动化程度就高得多。
对于小白用户而言,直接用后者上手学习是最佳选择。
这么多量化框架,导致的后果就是,你辛辛苦苦量化训练好一个模型,换个平台就得换套量化框架再重新训练,做的平台多了,感觉自己就是个人工智障。
那有没有什么量化框架,可以像全精度模型那样,一次训练多平台部署呢?
其实有一些更加通用的策略。
第一种方式是选择一个通用的推理引擎。从前面的分析可以看出,这些量化框架之所以会有差异,主要是各个平台的量化推理引擎在算法上存在差异。因此,如果有一个推理引擎可以支持多个部署平台,比如说,哪天高通的 SNPE 可以支持 MTK APU、Nvidia GPU、x86 CPU 等等多个平台,那我们就可以直接用 AIMet 量化,然后转换到 SNPE 的模型格式,之后就可以多平台部署了。
不过,现实中不同硬件产商之间的技术细节不可能互通,因此这是永远也做不到的。
第二种方式,就是保持多个推理引擎,而在量化训练框架中加入对各个推理引擎的支持。就如我前面说的,这些框架的量化训练流程,都遵循 Google 那套量化训练算法,只是细节上有差异。那理论上,我们完全可以对某个量化训练框架进行修改,针对每个平台对应的推理引擎,设计对应的量化算法设置,保证量化训练和推理一致,最后针对不同的推理引擎导出各自需要的模型格式就可以了。
第二种方式理论上比第一种更容易实现,因为不需要知道硬件细节,只要量化算法上保持一致就可以,至于硬件层面会用什么指令做什么加速,那是硬件产商自己的事情。
不过,做一个这样的框架是一件很费力的事情,需要开发者熟悉每个推理引擎的量化算法,关注各个推理引擎的更新,而且,很多细节没有在文档中体现,还需要你去实际使用对应的推理框架,看代码或者直接根据输出结果猜出一些算法细节。
接下来又要给公司带货了。文章最开始那个表中,MQBench 那一栏没写对应的推理引擎和部署平台,就是因为这个推理框架要做的是支持多个后端推理引擎。不过,要实现一次训练多平台部署,目前还是做不到的。因为不同平台采用的量化算法和策略不同,导致量化训练的配置是不一样的。不过好处就是,不需要知道不同平台的量化设置,选择对应平台的量化设置直接训练就可以。
不过还是要吐槽一下,这个框架目前文档比较少,我都是看源码学习,其次就是,需要用 pytorch1.8 及以上的版本,很多显卡比较旧的同学,更新不了这个版本的 pytorch,只能自己手动编译新版本的 pytorch 源码。。
今天写了这么多有的没的,其实就是想说,pytorch 的量化工具虽然五花八门,但对初学者来说,反而更不好选择,对做模型量化的同学来说,也经常因此感到「裂开」。
我之前做量化部署的时候,也经常因为不同推理引擎的不对齐,导致量化的工作量陡增。记得最开始部署 MTK APU 平台的时候,因为对量化算法的细节不太清楚,导致量化训练的效果和量化部署的结果差异很大。当时 leader 都准备让我用 MTK 自带的量化工具来训练了。那会他们的量化工具还只支持 tensorflow,而我们项目代码都是用 pytorch 写的,这要全部用 tensorflow 写一遍我直接就 007 上天了。最后硬是把 MTK 量化训练的代码挖了一遍找出最大的差异点,才勉强对齐效果。
说多了都是泪,反正我以后应该会用 MQBench 一条路走到黑了,这样出了问题,还能把锅甩给其他同事
最近正在用平时摸鱼的时间阅读这个框架的代码,顺便也捋一下 pytorch 官方量化框架的设计逻辑,后面会分享一下,权当弥补一下 MQBench 文档缺乏的问题。
联系客服