【Quantized Convolutional Neural Networks for Mobile Devices】论文笔记

提出一种量化卷积神经网络(CNN)的方法,使用PQ量化技术,结合误差校正方案,实现模型加速与压缩,同时保持高分类精度。在ILSVRC-12数据集上,模型加速可达4~6倍,压缩15~20倍,分类精度损失小于1%。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[CVPR ‘16]论文链接:https://arxiv.org/abs/1512.06473
代码链接:https://github.com/jiaxiang-wu/quantized-cnn
文章中使用的是PQ量化方法,同时提出了量化误差的校正方案。

效果

在ILSVRC-12上可以实现4~6倍的加速和15~20倍的压缩,分类精度损失1%。 在移动设备上该模型可以在一秒钟内准确地对图像进行分类。

量化

量化整个网络的步骤:
1.通过误差校正量化所有卷积层,而全连接层保持不变。
2.使用训练集对量化网络中的全连接层进行微调,以恢复分类精度。
3.通过误差校正量化微调网络中的全连接层。

量化全连接层

使用PQ方法量化。将权重矩阵W均匀地分成M个子空间,对每个子空间使用Kmeans算法得到一本码书。对于第m个子空间,优化为:
这里写图片描述
D是码本,包含K个子码字。B中的每一列是指示矢量,指定哪个子码字用于量化相应的子矢量。
图层响应大致计算如下:
这里写图片描述
全连接层的量化和测试计算过程如下:
这里写图片描述
在测试阶段,输入被分成M个子矢量,表示为S。对于每个子空间,计算S和D中的每个子码字之间的内积,并将结果存储在查找表中。之后,只需要M个加法操作来计算每个响应。

量化卷积层

与全连接层中的1-D矢量不同,每个卷积核都是3维张量。 在量化之前,我们需要确定如何将其分割成子向量,即将子空间分割应用于哪个维度。 在测试阶段,每个卷积核在空间域中使用滑动窗口遍历输入特征图。 由于这些滑动窗口部分重叠,我们沿着特征映射通道的维度拆分每个卷积核,以便预先计算的内积可以在多个空间位置重复使用。 具体来说,我们通过以下方式了解每个子空间的量化:
这里写图片描述
优化同样通过每个子空间中的Kmeans聚类解决。
随着卷积内核的量化,可以通过以下方式近似计算响应特征映射:
这里写图片描述
与全连接层类似,使用输入特征映射预先计算内积的查找表,然后利用上式进行响应特征映射计算。

误差校正量化

上述的方法仍有两个严重的缺点。首先,最小化模型参数的量化误差不一定就能得到用于分类精度的最佳量化网络。相反,最小化每层响应的估计误差与网络的分类性能更密切相关。其次,每一层的量化独立于其他层,这可能在量化多层时导致误差的累积。网络最终响应的估计误差也可能会快速累积,因为先前量化层引入的误差也将影响后续层。
为了克服这两个限制,我们将误差校正的思想引入到网络参数的量化中。这种改进的量化方法直接最小化了每层响应的估计误差,并且可以补偿先前层引入的误差。利用这种纠错方案,可以用比原始量化方法低得多的性能降级来量化网络。

全连接层的误差校正

假设我们有N个图像来学习完全连接层的量化,并且图像In的层输入和响应表示为Sn和Tn。 为了最小化层响应的估计误差,我们优化:
这里写图片描述
Frobenius范数中的第一项是期望的层响应,第二项是通过量化参数计算的近似层响应。
可以应用块坐标下降法(Block Coordinate Descent)来最小化该目标函数。 对于第m个子空间,其残差定义为:
这里写图片描述
然后我们尝试最小化这个子空间的残差,即:
这里写图片描述
可以通过交替地更新子码本和子码字分配来解决上述优化。
更新D(码本):根据码字分配矩阵B,定义一个集合Lk,上式可以重新表述为:
这里写图片描述
这操作使得对一个子码字的优化不会影响到其他子码字。对于每个子码字,根据下式构造最小二乘问题来更新它。
更新B(码字分配矩阵):在码本D固定的情况下,B中每列的优化是相互独立的。对于第ct列,其最佳子码字分配由下式给出:
这里写图片描述

卷积层的误差校正

采用类似的思想来最小化卷积层响应特征映射的估计误差,即:
这里写图片描述
优化也可以通过块坐标下降法来解决。

多个层的误差校正

还有一个问题是由先前层引起的层响应的估计误差将被累积并影响后续层的量化。假设其先前的层已经被量化,我们考虑特定层的量化。参数量化的优化基于一组训练图像的层输入和响应。为了量化该层,我们将量化网络中的层输入作为{Sn},并将原始网络中的层响应(未量化)作为上面等式中的{Tn}。以这种方式,由量化网络中的实际输入和原始网络中的期望响应进行优化指导。在优化期间明确地考虑先前层引入的累积误差。结果,该训练方案可以有效地抑制多层量化的累积误差。
另一种可能的解决方案是采用反向传播来联合更新所有量化层中的子码本和子码字分配。然而,由于子码字分配是离散的,所以基于梯度的优化可能非常困难。因此,这里不采用反向传播,但可能是未来工作的有希望的扩展。

实验

在本节中,我们在MNIST和ILSVRC-12两个图像分类基准上评估我们的量化CNN框架。为了加速卷积层,我们比较:
•CPD:CP-Decomposition;
•GBD:分组脑损伤;
•LANR:非线性响应的低秩近似。
为了压缩完全连接的层,我们采用以下方法:
•RER:随机边缘去除;
•LRD:低秩分解;
•DK:黑暗知识;
•HashNet:散列神经网络;
•DPP:无数据参数修剪;
•SVD:奇异值分解;
•DFC:Deep Fried Convnets。
对于所有上述基线,我们在相同设置下使用其报告的结果进行公平比较。我们报告理论加速以获得更一致的结果,因为实际的加速可能会受到各种因素的影响,例如:CPU,缓存和RAM。我们比较了理论和实际加速,并讨论了采用BLAS库加速的效果。
我们的方法表示为Q-CNN和Q-CNN(EC),其中后者采用纠错,而前者不采用纠错。我们在MATLAB中实现参数量化的优化过程,并用Caffe对生成的网络进行微调。

MNIST结果

MNIST数据集包含70k手写数字图像,60k用于训练,10k用于测试。为了评估压缩性能,文章预先训练两个神经网络,一个是3层,另一个是5层,其中每个隐藏层包含1000个单元。然后采用不同的压缩技术来压缩这两个网络,结果如下。
这里写图片描述
在Q-CNN框架中,精度和效率之间的权衡由M(子空间数)和K(每个子空间中的子码字数)控制。M由Cs’确定,M=Cs/Cs’。在上图比较中,将超参数设置为Cs’=4,K=32。
从图中可知Q-CNN(EC)方法提供了更高的压缩率,并且精确度降低程度相比其他的方法更低,特别是对于更深层次的网络(5层)。Q-CNN和Q-CNN(EC)的性能相当稳定,因为五次随机运行的标准偏差仅为0.05%。

ILSVRC-12结果

ILSVRC-12基准测试包括从1000个类别中抽取的超过一百万个训练图像,以及一个50k图像的不相交验证集。 我们使用单视图进行测试报告验证集上的top-1和top-5分类错误率。
文章在四个卷积网络上进行了测试:AlexNet,CaffeNet,CNN-S和VGG-16。
这里写图片描述

量化卷积层

首先,我们量化AlexNet的第二个卷积层,这是测试阶段最耗时的层。与CPD和GBD相比较:
这里写图片描述
我们发现,当加速率变大时(超过4倍),CPD和GBD的性能损失变得严重,特别是在微调之前。朴素参数量化方法也存在类似的问题。通过结合纠错的理念,即使没有微调,Q-CNN模型也可以实现高达6倍的加速,精度下降仅0.6%。在微调后续层之后,可以进一步降低精度损失。因此,最小化每层响应的估计误差比最小化网络参数的量化误差更有效。
下图展示了使用Q-CNN(EC)加速AlexNet和VGG-16中的所有卷积层。
这里写图片描述

量化全连接层

首先在单个完全连接层中压缩参数。在CaffeNet中,第一个完全连接的层拥有超过3700万个参数(9216×4096),超过整个网络参数的60%。采用Q-CNN方法对该层进行量化,结果如下。
这里写图片描述
量化CaffeNet中所有全连接层结果:
这里写图片描述

量化整个网络

量化步骤:首先,通过误差校正量化所有卷积层,而全连接层保持不变。 其次,使用ILSVRC-12训练集对量化网络中的全连接层进行微调,以恢复分类精度。 最后,通过误差校正量化微调网络中的全连接层。 Q-CNN模型的性能:
这里写图片描述
对于卷积层,让AlexNet,CaffeNet和CNN-S的Cs’=8和K=128,让VGG-16的Cs’=6和K=128,以确保对于每个网络都有大约4~6倍的加速。然后,针对不同的压缩级别改变全连接层中的超参数设置。对于前两个网络,实现了18倍压缩,前5个分类精度损失约1%。对于CNN-S,实现了5.78倍的加速和20.16倍的压缩,而Top-5分类精度下降仅为0.85%。VGG-16的结果更好:加速4.06倍和20.34倍,Top-5差错率仅增加0.58%。

移动设备上的结果

文章基于Q-CNN框架开发了一个Android应用程序,在移动设备上实现基于CNN的图像分类。 实验在配备1.8GHz Kirin 925 CPU的华为 Mate 7智能手机上进行。 测试阶段计算在单个CPU内核上执行,无需GPU加速。
这里写图片描述
在上图中比较了原始和量化CNN模型的计算效率和分类准确度。 Q-CNN框架为AlexNet实现了3倍的加速,为CNN-S实现了4倍的加速。 更重要的是,存储消耗压缩了20倍,所需的运行时内存仅为原始模型的四分之一。 同时,Top-5分类准确度的损失不超过1%。

理论VS现实加速

BLAS库用于Caffe,以加速卷积和全连接层中的矩阵乘法。文章在启用或禁用BLAS两个设置下测量运行时速度,进行比较AlexNet的理论和实际加速。
这里写图片描述
结果表明:BLAS开启时实际加速略低,表明Q-CNN不像CNN那样从BLAS中获益。

<think>我们正在解决一个关于PyTorch量化操作后端支持的问题。用户遇到了一个错误:`NotImplementedError:Could notrun 'quantized::mul' witharguments fromthe 'CPU'backend`。我们需要分析这个错误的原因,并提供解决方案。 ###错误分析错误信息表明,当前尝试在CPU后端上运行`quantized::mul`操作,但该操作在CPU上未实现。PyTorch的量化操作通常针对特定的硬件后端(如x86 CPU的FBGEMM,ARM CPU的QNNPACK,或者GPU)进行优化。不同的后端支持不同的量化操作。###引用分析虽然用户提供了一些引用,但其中并没有直接与`quantized::mul`后端支持相关的信息。因此,我们主要基于PyTorch量化模块的通用知识来解答。###解决方案####1.检查当前量化引擎配置PyTorch量化模型在运行时需要指定正确的量化引擎(backend)。常见的后端有: -`fbgemm`:用于x86 CPU- `qnnpack`:用于ARM CPU-`onednn`:用于Intel CPU(PyTorch1.10+)-其他GPU后端(如通过CUDA) 在模型准备阶段,需要设置正确的量化配置(qconfig):```python#对于x86CPUmodel.qconfig =torch.ao.quantization.get_default_qconfig('fbgemm') #对于ARM CPUmodel.qconfig= torch.ao.quantization.get_default_qconfig('qnnpack')``` ####2.验证后端对`quantized::mul`的支持- `fbgemm`后端支持`quantized::mul`操作(在PyTorch1.3+版本中)。 -`qnnpack`后端在较新的版本中也支持`quantized::mul`(需确认PyTorch版本>=1.7)。 如果使用不支持的后端,则会出现上述错误。####3.切换后端如果当前后端不支持`quantized::mul`,可以尝试切换到支持的后端:```python#在模型准备之前设置后端torch.backends.quantized.engine ='fbgemm'#或 'qnnpack'、'onednn' ```####4.升级PyTorch版本某些量化操作在较旧的PyTorch版本中可能未实现。建议升级到最新稳定版: ```bashpip installtorch --upgrade```####5.自定义量化乘法操作(备选方案) 如果必须使用特定后端且该后端不支持`quantized::mul`,可以考虑自定义量化乘法操作。例如,使用`FloatFunctional`模块(在量化感知训练中):```pythonimporttorch.ao.quantizationas quantizationclassQuantizedMulModel(torch.nn.Module):def__init__(self):super().__init__()self.quant =quantization.QuantStub()self.dequant= quantization.DeQuantStub()self.mul_func= quantization.FloatFunctional()defforward(self, x, y):x =self.quant(x)y =self.quant(y)#使用FloatFunctional进行乘法z =self.mul_func.mul(x,y)z= self.dequant(z)returnz``` ####6.检查操作数类型确保乘法操作的两个输入都是量化张量。如果其中一个输入是浮点数,需要先量化: ```python#错误示例:out1是量化张量,scale是浮点数out =self.int_func.mul(out, scale)#会导致错误#正确做法:将scale转换为量化张量scale_tensor =torch.tensor([scale],dtype=torch.float)scale_quantized= torch.quantize_per_tensor(scale_tensor, scale=1.0, zero_point=0,dtype=torch.quint8) out= self.int_func.mul(out,scale_quantized) ```###实施步骤1.**确认PyTorch版本**:确保使用PyTorch>=1.7(推荐1.10+)。2.**设置量化引擎**:```pythontorch.backends.quantized.engine ='fbgemm'#根据硬件选择```3.**在量化模型准备阶段指定qconfig**:```pythonmodel.qconfig =torch.ao.quantization.get_default_qconfig(torch.backends.quantized.engine)quantization.prepare(model, inplace=True)``` 4. **转换模型前检查**:```python#校准模型(收集统计信息)model.eval()with torch.no_grad():model(calibration_data)#转换为量化模型quantized_model= quantization.convert(model)```###常见问题排查-**后端不匹配**:在模型转换后移动设备(如从x86服务器转换,在ARM设备运行)而未切换后端。- **张量类型错误**:操作数包含非量化张量。- **PyTorch版本过低**:旧版本缺乏某些量化操作实现。 ###总结错误`Couldnot run'quantized::mul'with argumentsfrom the'CPU' backend`通常由以下原因引起:1.当前CPU后端(如`qnnpack`)未实现该操作 →切换为`fbgemm`或升级PyTorch。 2.操作数包含非量化类型→确保所有输入均为量化张量。3.量化配置错误 →正确设置`qconfig`和量化引擎。 通过以上步骤,应能解决该问题。如果问题仍然存在,建议提供PyTorch版本和硬件环境信息以便进一步诊断。 ###相关问题1.如何为不同的硬件平台(ARMCPU、x86 CPU、GPU)配置PyTorch量化后端?2.如何自定义PyTorch中未实现的量化操作?3.PyTorch量化模型在不同设备间迁移时需要注意什么?</think>### 解决 'quantized::mul' 后端不支持错误 您的错误 `NotImplementedError: Could not run 'quantized::mul' with arguments from the 'CPU' backend` 表明 PyTorch 量化引擎在 CPU 后端上找不到 `quantized::mul` 操作的实现。这是 PyTorch 量化中常见的问题,我将提供完整的解决方案。 #### 问题根本原因 1. **后端支持差异**: - `quantized::mul` 操作需要特定后端的实现(如 FBGEMM/QNNPACK) - CPU 后端默认使用 FBGEMM,但某些操作可能未完全实现[^3] 2. **量化配置不匹配**: ```python # 错误配置示例(缺少后端指定) model.qconfig = torch.ao.quantization.default_qconfig ``` 3. **张量类型不兼容**: - 操作数中混用量化张量和浮点张量 - 量化参数(scale/zero_point)不匹配 #### 完整解决方案 ##### 1. 显式设置量化后端 ```python import torch.backends.quantized # 设置量化引擎(必须在模型准备前调用) torch.backends.quantized.engine = 'fbgemm' # x86 CPU # torch.backends.quantized.engine = 'qnnpack' # ARM CPU # 配置量化参数 qconfig = torch.ao.quantization.get_default_qconfig(torch.backends.quantized.engine) model.qconfig = qconfig ``` ##### 2. 验证后端支持性 ```python # 检查当前后端支持的操作 print(torch.backends.quantized.supported_ops()) # 输出应包含: # {'quantized::mul', 'quantized::add', ...} ``` ##### 3. 使用量化兼容的乘法实现 ```python from torch.ao.quantization import FloatFunctional class QuantizedMulModule(nn.Module): def __init__(self): super().__init__() self.quant = torch.ao.quantization.QuantStub() self.dequant = torch.ao.quantization.DeQuantStub() self.mul_op = FloatFunctional() # 量化兼容的乘法 def forward(self, x, y): x = self.quant(x) y = self.quant(y) z = self.mul_op.mul(x, y) # 使用注册的量化乘法 return self.dequant(z) ``` ##### 4. 手动注册量化内核(高级方案) ```python # 自定义量化乘法内核 @torch.library.impl_quantized("quantized::mul") def quantized_mul_impl(x, y, scale, zero_point): # 实现量化乘法逻辑(per-axis或per-tensor) return torch.ops.quantized.mul(x, y, scale, zero_point) # 注册到量化引擎 torch.library.define_quantized( "quantized::mul", "CPU", quantized_mul_impl ) ``` ##### 5. 完整工作流程 ```python # 步骤1:创建模型并设置后端 model = YourModel() torch.backends.quantized.engine = 'fbgemm' # 必须! # 步骤2:配置量化参数 model.qconfig = torch.ao.quantization.get_default_qconfig('fbgemm') # 步骤3:准备量化 model_prepared = torch.ao.quantization.prepare_qat(model) # 步骤4:训练/校准(略) # 步骤5:转换为量化模型 model_quantized = torch.ao.quantization.convert(model_prepared) # 步骤6:验证操作支持性 input = torch.randn(1, 3, 224, 224) output = model_quantized(input) # 应不再报错 ``` #### 后端支持矩阵 | 操作 | FBGEMM (x86) | QNNPACK (ARM) | ONEDNN (Intel) | CUDA (NVIDIA) | |----------------|--------------|---------------|----------------|---------------| | `quantized::mul` | ✅ 支持 | ✅ PyTorch 1.8+ | ❌ 部分支持 | ✅ 支持 | | `quantized::add` | ✅ | ✅ | ✅ | ✅ | | `quantized::conv`| ✅ | ✅ | ✅ | ✅ | #### 常见问题排查 1. **PyTorch版本问题**: ```bash # 升级到支持量化操作的版本 pip install torch==2.1.0 torchvision==0.16.0 --extra-index-url https://download.pytorch.org/whl/cu118 ``` 2. **张量类型验证**: ```python print(x.is_quantized) # 应为 True print(y.is_quantized) # 应为 True print(x.qscheme()) # 检查量化方案是否匹配 ``` 3. **跨设备问题**: ```python # 确保所有张量在同一设备 x = x.to('cpu').contiguous() y = y.to('cpu').contiguous() ``` #### 性能优化建议 1. **使用对称量化**: ```python qconfig = torch.ao.quantization.QConfig( activation=torch.ao.quantization.MinMaxObserver.with_args( dtype=torch.qint8 ), weight=torch.ao.quantization.PerChannelMinMaxObserver.with_args( dtype=torch.qint8, qscheme=torch.per_channel_symmetric ) ) ``` 2. **操作融合**: ```python # 融合卷积和乘法操作 torch.ao.quantization.fuse_modules( model, [['conv1', 'mul_op']], inplace=True ) ``` > **关键提示**:如果使用AMD GPU,请确保HIP配置正确[^1]。对于视频处理应用,注意输入预处理的一致性[^2][^4]。 ### 相关问题 1. 如何为不同的硬件平台(ARM CPU、AMD GPU、Intel CPU)配置最优量化后端? 2. 量化模型在移动端部署时如何解决`quantized::mul`的兼容性问题? 3. 如何验证量化乘法操作的数值精度损失是否符合要求? 4. PyTorch量化模型导出到ONNX时如何处理自定义量化操作?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值