摘要
论文介绍
- 研究背景:神经网络量化是提升速度和降低内存使用量的常用方法,但无标记数据的量化问题受到的关注较少。
- 研究目的:引入DSConv卷积算子,旨在生成更小且推理速度更快的神经网络,同时保持高精度。
- 实验验证:在最流行的神经网络架构(ResNet、DenseNet、GoogLeNet、AlexNet和VGG-Net)上测试了DSConv模型。
创新点
- 即插即用替代品:DSConv可以作为标准卷积的替代品,无需使用已标记数据进行再训练。
- 超参数调整:提供了一个超参数,可以针对任何给定任务优先考虑精度或内存使用/计算速度。
- 基于蒸馏的自适应阶段:展示了如何使用未标记数据的基于蒸馏的自适应阶段来进一步提高结果。
方法
- 量化步骤:
- 从预训练网络中,将权重张量按深度方向分成可变长度B的块,并对每个块进行量化。
- 使用块浮点(BFP)格式对激活进行量化,其中块的大小与权重张量相同。
- 将激活和权重张量的整数值相乘,以最大化推理速度。
- 将最终值乘以它们各自的缩放因子,以将各个块的分布移动到正确的范围。
- 计算效率和速度权衡:
- B的值越高,裁剪误差就越大,但指数张量和KDS(知识蒸馏张量)会更浅。
- b和B的值彼此成反比,目标是以最少的尾数位数b和最大的块大小B值获得最高的精度。
模块作用
- 提升推理速度:DSConv模块通过使用低位定点计算进行大部分运算,从而显著提升神经网络的推理速度。
- 保持高精度:在不进行再训练的情况下,仅使用4位量化,精度损失小于1%。
- 节省内存:通过量化可以显著减少网络权重所占用的内存。
改进的效果
- 实验验证:在多种神经网络架构和设置下进行了实验,证明了DSConv模块的有效性和先进性。
- 精度和速度:DSConv能够在保持高精度的同时,显著提升神经网络的推理速度。
- 实际应用:DSConv模块可以作为Yolo11中Conv模块的替代品,以改进其性能和效率。通过替换Yolo11的Conv模块为DSConv模块,可以预期在保持精度的同时,提高模型的推理速度和降低内存使用量。
论文翻译:《DSConv:高效卷积算子》
量化是提升卷积神经网络(CNNs)速度并降低其内存使用量的常用方法。当存在已标记的训练数据时,网络权重和激活已成功量化到l位。然而,对于没有已标记训练数据的场景(例如,在量化预训练模型时),则不能这样说。目前的方法显示,在8位量化时,最佳情况下不会损失精度。
我们介绍了DSConv,这是一种灵活的量化卷积算子,它用成本更低的整数运算代替单精度运算,同时保持内核权重和输出上的概率分布。我们在最流行的神经网络架构(ResNet、DenseNet、GoogLeNet、AlexNet和VGG-Net)上测试了我们的模型,将其作为标准卷积的即插即用替代品,并展示了最先进的结果。在不进行再训练的情况下,仅使用4位量化,精度损失小于 1 % 1\% 1%。我们还展示了如何使用未标记数据的基于蒸馏的自适应阶段来进一步提高结果。
1. 引言
使神经网络更快、内存使用更少的一种流行方法是量化,即用更低位(即更低精度)的表示来替换32位浮点权重和可能的激活,同时保持精度。
量化常用于神经网络压缩。其目标是尽可能减少网络权重所占用的内存,例如,以降低存储网络所需的总体内存占用。当应用于权重和激活时,通过用更便宜的替代方案(如整数、位运算或仅加法运算)替代昂贵的浮点乘法和累加(MAC)运算,还可以提高神经网络推理速度(快速推理)。
当存在已标记的训练数据时,可以获得最佳的量化结果,因为量化模型可以拟合数据集,从而为训练算法提供有关激活图将呈现什么样以及预期输出将是什么的先验知识。当只有预训练模型可用时,保持高精度会变得更加困难。
在本文中,我们关注后一种场景,并对权重和激活进行量化,以生成更小且推理速度更快的神经网络。我们的关键见解是,在没有训练数据的情况下,最好通过强制低精度量化模型权重和激活的概率分布来反映原始全精度模型的概率分布来实现这一点。我们引入了一种新的卷积算子,称为DSConv,它将卷积权重分解为(i)与原始内核大小相同的低精度分量和(ii)具有可变大小(例如,每个内核一个32位浮点数那么小)的高精度分布位移分量。受区块浮点法[35]启发的类似程序用于量化激活。我们还展示了当使用受蒸馏[19]启发的权重自适应方法(该方法使用原始预训练模型和未标记的输入数据)时,可以提高精度。
本文的主要贡献是引入了一种卷积算子,该算子(i)可以作为标准卷积的“即插即用”替代品,并使用低位定点计算进行大部分运算,而无需使用已标记数据进行再训练,以及(ii)提供了一个超参数,可以对其进行调整以针对任何给定任务优先考虑精度或内存使用/计算速度。我们的量化策略能够获得最先进的结果,实验部分对此进行了证明。
本文的其余部分结构如下。第 82 82 82节介绍了之前的量化论文。第 3 3 3节详细解释了该方法。第 4 4 4节展示了在各种架构和设置下进行的实验结果。第 5 5 5节对本文的性能、可能的应用和局限性进行了讨论,并得出结论。
2. 相关工作
使用低位宽网络可以节省大量内存和计算量,特别是针对定制硬件时。例如,对于8位操作,[26]报告速度提高了多达10倍,而[12]报告节能和芯片面积节省了多达30倍。我们将之前尝试提高神经网络效率的研究分为两类:
使用标记数据的量化。神经网络量化的研究大多集中在涉及再训练的问题上,可以通过从头开始或从现有的预训练网络进行适应。BinaryConnect、BWN和TWN [11, 32, 28]使用1位和三元权重,仅进行FP-MACS加法。XNOR-Net和BNN [32, II]将1位量化权重和激活应用于ImageNet以实现快速推理,但准确性显著下降。WRPN [30]通过使用相同架构的更宽版本提高了准确性。在定制硬件中,低位网络加速的早期演示包括Ristretto [16],它也使用数据将网络量化为8位模型。随后,许多其他论文也通过训练量化方案来使用二进制基向量,如LQ-Nets [38]、Halfway Gaussian Quantization (HWGQ) [6]和ABC-Net [29]。DoReFa-Net [40]还对梯度、权重和激活进行了量化。
压缩问题也大多通过使用能够访问标记数据的再训练来解决。在DeepCompression [17]中,算法的三个步骤中有两个需要再训练(剪枝和量化),而Huffman编码则无需数据即可执行。HashedNet [7]使用“哈希技巧”在存储网络时节省了大量内存,但仍然需要标记数据进行调整。更近期的方法[31]使用蒸馏[3, 19]来获得压缩权重,但也需要完整的标记训练集。
一些方法引入了新的浮点数据格式。例如,Dynamic Fixed Point [10]用固定点和浮点数的混合替代了正常的浮点数;而Flexpoint [25]则旨在利用浮点数的范围和固定点的计算复杂性,并承诺在有限范围内执行前向和后向操作。在Tensorflow的bfloat16格式中[2],也采用了用其他格式替换单精度(FP32)值表示的思想,该格式采用二进制浮点16格式,其中尾数使用7位而不是通常的23位。
无标记数据的量化。虽然使用标记数据的量化问题已得到广泛研究,但无数据量化问题受到的关注却远少得多。探索这种可能性的最新论文有[39, 8, [23, 4],它们要么仅报告8位量化的结果,要么使用某种形式的校准数据,即用于权重调整的未标记验证数据集的一小部分。行业方法已实施了仅使用少量未标记数据的量化技术,例如在TensorRT [⿴囗]等系统中。在这种情况下,他们可以成功地将网络量化为8位,而不会损失准确性(有时甚至提高了准确性),这来自数千个采样图像[1]。其他示例还包括Google TPU和Project Brainwave([15, 9]),它们都将神经网络量化为8位以实现快速推理。另一项研究表明,8位量化不会显著影响效率[26],即使同时对激活和权重进行量化也是如此。
在本文中,我们证明了权重和激活都可以有效地量化到4位,而无需对标记数据进行再训练,并且当使用未标记数据进行适应时,还有进一步改进的潜力。
3. 方法
对于给定的神经网络推理 f ( x ) f(x) f(x),对于 M ∈ R M \in \mathbb{R} M∈R, f ( M x ) f(M x) f(Mx)的预测应该是一致的(考虑到偏差也相应地进行了缩放)。DSConv建立在这样一个直觉之上:只要权重和激活值的相对分布保持不变,这一性质就适用于 x x x的某些非线性变换。我们相信量化就是这样一种变换,即我们可以以低精度表示友好的方式缩放和偏置量化后的权重和激活值,同时仍然保持相同的神经网络精度,只要权重和激活值的分布保持不变。
如果将这一策略应用于整个4D张量,将会产生非常高的裁剪误差,因为单个缩放因子 M M M无法单独捕获整个张量分布。在本文中,我们采用了更一般的策略,即使用缩放因子张量,其大小被调整为以更高的保真度捕获值的范围。每个浮点值张量被分为两个组件:一个与原始张量大小相同的张量,由低位整数值组成;另一个大小是前者的一部分,由浮点缩放因子组成。每个缩放因子负责沿其张量深度维度对一组 B B B个整数值进行缩放,其中 B B B是块大小超参数。
DSConv的步骤如下:(I)从预训练网络中,将权重张量按深度方向分成可变长度 B B B的块,并对每个块进行量化;(II)使用块浮点(BFP)格式对激活进行量化,其中块的大小与权重张量相同;(III)将激活和权重张量的整数值相乘,以最大化推理速度;(IV)将最终值乘以它们各自的缩放因子,以将各个块的分布移动到正确的范围。
3.1. 权重量化
我们提出了一种量化权重的方法,该方法在每个权重张量滤波器的深度维度上,为每个大小为
B
B
B的块共享一个浮点值。每个滤波器所得大小的一个示例可见于图1。
给定大小为( C o , C i , K h , K w ) \left.C_{o}, C_{i}, K_{h}, K_{w}\right) Co,Ci,Kh,Kw)的权重张量和块大小超参数 B B B,我们首先将张量分为两个组件:可变量化内核(VQK),由低位值组成,且与原始张量大小相同;以及内核分布偏移(KDS),由单精度数 ξ \xi ξ组成,且大小为 ( C o , ⌈ C i B ⌉ , K h , K w ) \left(C_{o},\left\lceil\frac{C_{i}}{B}\right\rceil, K_{h}, K_{w}\right) (Co,⌈BCi⌉,Kh,Kw),其中 ⌈ x ⌉ \lceil x\rceil ⌈x⌉是向上取整操作。
B B B超参数可以无缝修改,以适应浮点运算和定点运算之间的权衡,从纯浮点( B = 1 B=1 B=1)到最大定点( B ≥ C i B \geq C_{i} B≥Ci)。
然后,VQK在2的补码中持有整数值,以便对于选定的特定位数 b b b,权重在区间内:
w q ∈ Z , b ∈ N ∣ − 2 b − 1 ≤ w q ≤ 2 b − 1 − 1 w_{q} \in \mathbb{Z}, b \in \mathbb{N} \mid-2^{b-1} \leq w_{q} \leq 2^{b-1}-1 wq∈Z,b∈N∣−2b−1≤wq≤2b−1−1
这允许使用2的补码算术执行所有操作,如第3.3节所述。
只需将普通卷积更改为DSConv,每个张量权重节省的内存为:
p = b 32 + ⌈ C i B ⌉ C i p=\frac{b}{32}+\frac{\left\lceil\frac{C_{i}}{B}\right\rceil}{C_{i}} p=32b+Ci⌈BCi⌉
等式2表明,对于足够大的
B
B
B和
C
i
C_{i}
Ci值,节省的内存大约是位数
b
b
b除以32。为了说明这一点,表1显示了GoogLeNet[34]架构中某些层的
C
i
C_{i}
Ci、
B
B
B、
b
b
b和
p
p
p的实际值的数值结果。可以看出,仅通过量化就可以实现显著的内存节省,而无需使用诸如霍夫曼编码[21]之类的附加方法。
给定已知的预训练模型,每个块的权重被拉伸并四舍五入以适合等式1中的区间,并存储在VQK中。接下来,我们探索了两种计算KDS值的可能方法:(i)最小化KL散度,它试图找到原始权重分布和内核分布移位器之间的最小信息损失,并强调了在移位后,所得VQK应具有与原始权重相似的分布这一思想;或(ii)最小化L2范数(欧几里得距离),这可以解释为参数应最接近原始网络的最佳值。
为了最小化KL散度,我们首先计算移位后的VQK和原始分布的softmax值:
T j = e w j ∑ i e w i , I j = e k ^ ⋅ w q j ∑ i e k ^ ⋅ w q i T_{j}=\frac{e^{w_{j}}}{\sum_{i} e^{w_{i}}}, I_{j}=\frac{e^{\hat{k} \cdot w_{q_{j}}}}{\sum_{i} e^{\hat{k} \cdot w_{q_{i}}}} Tj=∑iewiewj,Ij=∑iek^⋅wqiek^⋅wqj
然后,我们使用梯度下降来最小化每个切片的以下值:
ξ = min ξ ∑ j T j log ( T j I j ) , ∀ ( 1 , B , 1 , 1 ) slices \xi=\min _{\xi} \sum_{j} T_{j} \log \left(\frac{T_{j}}{I_{j}}\right), \quad \forall(1, B, 1,1) \text { slices } ξ=minξ∑jTjlog(IjTj),∀(1,B,1,1) slices
其中, ξ \xi ξ是该块的KDS值。
另一种方法是最小化每个切片的以下L2范数:
ξ = min ξ ^ ∑ i = 0 B − 1 ( w q i ξ ^ − w i ) 2 \xi=\min _{\hat{\xi}} \sum_{i=0}^{B-1}\left(w_{q_{i}} \hat{\xi}-w_{i}\right)^{2} ξ=minξ^∑i=0B−1(wqiξ^−wi)2
其封闭形式解为:
∴ ξ = ∑ i = 0 B − 1 w i w q i ∑ i = 0 B − 1 w q i 2 , ∀ ( 1 , B , 1 , 1 ) slices \therefore \xi=\frac{\sum_{i=0}^{B-1} w_{i} w_{q_{i}}}{\sum_{i=0}^{B-1} w_{q_{i}}^{2}}, \quad \forall(1, B, 1,1) \text { slices } ∴ξ=∑i=0B−1wqi2∑i=0B−1wiwqi,∀(1,B,1,1) slices
在实践中,这两种策略产生的值大约相等。由于L2范数方法具有封闭形式解,因此我们使用该方法进行了所有实验。
算法1总结了给定预训练网络模型时,初始化VQK和KDS的过程。
3.2. 激活量化
我们的方法旨在在没有任何训练数据的情况下实现良好的性能。这意味着我们对激活图的取值或分布没有先验知识。因此,这种量化不能是数据驱动的。相反,我们采用了一种受[35, 33, 14, 99, 15]中块浮点(BFP)方法启发的方法。
图2展示了我们的激活量化方法。对于给定的尾数宽度,激活张量被划分为块,对于每个块,我们找到最大指数。块中所有其他激活的尾数值被移位,以匹配最大指数,然后(使用 1 / 2 1/2 1/2 LSB舍入)裁剪以匹配指定的位数。这会产生两个张量:一个是尾数张量,它与原始张量具有相同的形状,但填充了 b b b位;另一个是指数张量,其大小为 ( C o , ⌈ C i B ⌉ , H , W ) \left(C_{o},\left\lceil\frac{C_{i}}{B}\right\rceil, H, W\right) (Co,⌈BCi⌉,H,W)。
我们称之为BFP方法,因为我们基本上是在为每个大小为 B B B的块“共享”指数。这可以控制量化的粗细程度,以及我们愿意接受多少裁剪误差以获得尾数张量的最低位长。
这种方法的一个额外好处是允许权重和激活之间进行低位整数运算,如我们在 3.3 3.3 3.3节中所示。因此,计算效率和速度之间的权衡如下: B B B的值越高,裁剪误差就越大,但指数张量和KDS(知识蒸馏张量)会更浅。这与位数 b b b的权衡不同, b b b会给尾数张量增加更多的计算复杂度和内存。 b b b和 B B B的值彼此成反比,并相互抵消彼此的正面和负面影响。然后,目标就变成了以最少的尾数位数 b b b和最大的块大小 B B B值获得最高的精度。
3.3. 推理
在推理过程中,硬件可以利用向量量化核(VQK)和尾数张量是低位整数值这一事实,从而可以节省执行整数运算而非浮点运算的时间。数据路径如图3所示。
首先,VQK和尾数张量的每个块进行点积运算,每个块产生一个值。所有这些操作都可以在低位定点算术中进行,从而节省了大量的处理时间。在块乘法结束时,结果是与指数张量和KDS大小相同的张量。
通过将指数张量的值加到KDS张量值的指数上,将指数张量与KDS张量合并。这会产生一个与浮点数大小相同的张量。最后,该张量乘以VQK和KDS乘积的结果,并产生一个单精度浮点数作为输出激活。
请注意,推理与标准卷积一样具有高度并行化的能力,但不同的是,大多数乘法运算不是使用浮点算术进行的,而是可以由整数乘法替代,从而节省能量、带宽和计算时间。
这也意味着,对于每个权重和激活乘法,块的数量与总浮点乘加(MAC)运算的数量成正比,而张量本身的大小给出了整数乘加(INT MAC)运算的数量。
批量归一化折叠。与[23]类似,我们对具有批量归一化(BN)[22]参数的模型执行“折叠”。由于已经证明批量归一化可以改善训练(见[22]),我们在训练阶段保留它,而仅在推理阶段对其进行折叠。
在折叠BN参数时,我们与KDS一起进行,因为它们是每个通道唯一的,并且使用FP32值。我们使用以下方程进行折叠:
ξ fold = ξ γ σ B 2 + ϵ b fold = β − γ μ b σ B 2 + ϵ \begin{array}{c} \xi_{\text {fold }}=\frac{\xi \gamma}{\sqrt{\sigma_{B}^{2}+\epsilon}} \\ b_{\text {fold }}=\beta-\frac{\gamma \mu_{\mathrm{b}}}{\sqrt{\sigma_{B}^{2}+\epsilon}} \end{array} ξfold =σB2+ϵ ξγbfold =β−σB2+ϵ γμb
其中,参数 γ , σ , ϵ , β \gamma, \sigma, \epsilon, \beta γ,σ,ϵ,β和 μ \mu μ如[22]中定义, ξ \xi ξ是KDS张量, b fold b_{\text {fold }} bfold 是DSConv(深度可分离卷积)的最终偏置。
3.4. 无标签数据适应的蒸馏
通常情况下,可能会获得无标签数据,如大量可用的无监督学习方法所示。对于这一特定场景,我们采用与[19]类似的策略。我们使用无标签数据的蒸馏损失,通过将FP32模型作为目标,并最小化回归损失,来尝试将FP32模型回归到量化模型。
我们创建了一个包含单精度数的“影子”模型。在每次推理之前,该模型被量化为VQK和KDS,执行推理并计算梯度。在更新阶段,梯度以单精度数累积,该方法一直执行到收敛为止。
每次推理后量化激活图会导致梯度在所有位置都为零。为了避免这个问题,我们使用直通估计器(STE)[5, 37]来计算反向梯度。特别是,我们使用ReLU STE,因为[37]表明,对于更深的网络,它比使用Identity STE具有更高的准确性。然后,梯度也在一个“影子”FP32模型中累积,该模型在每个批次迭代后进行量化。
我们使用ADAM优化器[24],初始学习率为 1 0 − 5 10^{-5} 10−5,在损失稳定后,该速率更改为 1 0 − 6 10^{-6} 10−6。所有其他超参数和数据增强细节均遵循其各自的原始论文。
我们使用验证数据集中的960张图像(30个批次,每个批次32张)进行蒸馏,并使用其余图像(共49,040张)测试准确性。
4. 实验与结果
我们在各种神经网络架构上测试了我们的方法:ResNet[18]、AlexNet[27]、GoogleNet[34]和DenseNet[20]。我们在ImageNet数据集[13](更具体地说是ILSVRC2012)上对我们的结果进行了基准测试,该数据集包含120万张训练集图像和5万张验证集图像。所报告的结果使用的是从验证集中抽取的图像。我们针对引言中提到的所有任务测试了我们的算法。本节内容如下: 4.1 4.1 4.1部分探讨了DSConv的理论计算节省; 4.2 4.2 4.2部分展示了未经过训练或适应的结果; 4.3 4.3 4.3部分展示了当模型使用未标记数据进行适应时的准确性;而 4.4 4.4 4.4部分,为了与之前的方法进行比较,展示了在DSConv中使用标记数据重新训练的结果。
4.1. 块大小的理论计算负载
传统上,计算负载被报告为完成算法所需的MAC(乘积累加)操作次数的函数。我们注意到两个注意事项:整数MAC远比浮点MAC简单,并且在硬件实现的支持下,其运行速度可以比浮点运算快几个数量级[26];我们的方法还依赖于动态创建尾数张量和指数张量的能力(VQK和KDS是静态创建的,因此这里不考虑它们)。这需要MAX、SHIFT和MASK操作。这些操作可以在自定义硬件中通过较少的时钟周期高效地实现。因此,我们将重点关注整数与浮点操作次数的比较,以评估使用该方法的优势。
为了使我们的方法比正常卷积更快,执行整数操作所花费的时间必须少于执行FP32操作所花费的时间。这种差异是块大小和通道参数的函数。等式9显示了整数操作时间与浮点操作时间之间的关系:
T int ≤ T F P C i − ⌈ C i B ⌉ C i ( 1 + η ) T_{\text {int }} \leq T_{F P} \frac{C_{i}-\left\lceil\frac{C_{i}}{B}\right\rceil}{C_{i}(1+\eta)} Tint ≤TFPCi(1+η)Ci−⌈BCi⌉
其中, T i n t T_{i n t} Tint和 T F P T_{F P} TFP分别表示执行整数和浮点操作所需的时间。参数 η \eta η是一个“理想性”参数,表示与正常卷积算子相比,执行DSConv时的MAX、SHIFT和MASK操作的总开销。
还请注意,如果 C i C_{i} Ci能被 B B B整除(这通常是情况),则等式9与通道大小无关,并简化为:
T i n t ≤ T F P 1 − 1 B 1 + η , if B ∣ C i T_{i n t} \leq T_{F P} \frac{1-\frac{1}{B}}{1+\eta}, \text { if } B \mid C_{i} Tint≤TFP1+η1−B1, if B∣Ci
当
η
=
0
\eta=0
η=0时,表2显示了最常见实验块大小的
1
−
1
B
1-\frac{1}{B}
1−B1的比率。可以看出,如果计算一个整数值的时间少于计算一个浮点运算时间的0.75倍,那么所有大于4的块大小都将比正常卷积更快。这通常很可能是这样的情况。例如,在现代CPU和一些GPU中,8位操作的速度可能比FP32操作快10倍[26],而在FPGA等自定义硬件中,更低位的操作可能会更快。在自定义软件中,小于8位的操作也通常更快。
块大小 B B B限制了DSConv相对于传统卷积算子能快多少。自然,DSConv的速度可以是传统卷积的 min ( C i , B ) \min \left(C_{i}, B\right) min(Ci,B)倍,因为其浮点运算次数比正常卷积少 min ( C i , B ) \min \left(C_{i}, B\right) min(Ci,B)倍。例如,对于128到256的块大小和超过256的通道大小,DSConv的速度可能比正常卷积快两个数量级。
4.2. 重新训练或适应前的准确性
我们的方法旨在通过从预训练网络进行量化,即使在没有训练数据的情况下也能产生准确的结果。
表3的第二行和第五行显示,对于压缩和快速推理问题,即使块大小非常高,8位网络也不会损失准确性,这正如之前的论文和实际应用[26, 16]所证明的。结果还表明,压缩到4位(在通道大小输入为256的卷积中,这将产生5倍的压缩率)时,根据架构的不同,准确性仅下降1%到2%。还可以看出,非常低位的量化变得明显不稳定,并且随着架构的不同而差异很大。在极端情况下,使用2位时,GoogLeNet的损失高达-40%,而ResNet50的损失仅为-11%。
最后四行显示了快速推理问题的结果。同样,如之前的研究论文[26, 16]所知,8/8位模型仅损失约0.1%的准确性。对于5/5和4/4位模型,我们的准确性下降了1%到3%。据我们所知,这是当模型既未重新训练也未适应时,快速推理所报告的最小位宽。
关于架构的方差表明,5位或更少位的量化是不稳定的。然而,即使对于8位准确性的快速推理,它也能在全精度模型的1%以内实现稳定且令人满意的结果。关于块大小的准确性,表4显示了与块大小相关的准确性。该表仅显示了量化权重的结果,括号中的数字表示权重的位宽。自然,这代表了内存和计算负载与网络精度之间的权衡。使用3位或2位权重的模型中,准确性的最大差异最为明显。例如,当将块大小从256更改为8时,GoogLeNet模型的3位权重将其Top1准确性从5.7%提高到56.8%。
当使用4位量化方案时,减小块大小可以实现与全精度网络相差1%到2%的准确性水平。例如,对于大多数块大小为16到32的网络来说,情况就是这样。
4.3. 使用未标注数据进行精度调整
表5报告了使用额外未标注数据调整网络时的结果。对于每种Block-Bit配置,报告了两个结果:上方显示调整前的结果,下方(加粗)显示使用未标注数据进行调整后的结果。此策略仅使用4位权重和激活值,就能将推理精度提高到网络FP32精度的
2
%
2\%
2%以内,即使在使用128作为块大小的极端情况下也是如此。
对于3位,尽管我们恢复了高达
30
%
30\%
30%的精度,但低比特精度与全精度之间仍存在相当大的差距。对于ResNet50,这个差距为
6
%
6\%
6%,而对于GoogLeNet,差距可达
10
%
10\%
10%。
表6显示了最近使用校准的论文(在文献综述中介绍)的结果。这与我们的调整阶段在精神上相似,因为两种方法都仅使用未标注数据。值得注意的是,我们使用蒸馏将全精度模型转换为低精度模型,而其他方法通常只校准最佳裁剪策略。可以看出,即使使用64的大块大小,我们也取得了更好的性能。据我们所知,这是仅使用调整数据实现快速推理的最佳结果。
4.4. 使用标注数据重新训练后的精度
我们还将DSConv与以前使用标注数据(文献中的绝大多数)进行再训练/微调的方法进行了比较。训练过程与调整过程类似,但现在我们使用标注数据,并在分类误差中使用交叉熵损失而不是逻辑值。
表7显示了ImageNet在各种架构上的结果。由于许多以前的论文为同一架构报告了不同的初始FP精度,因此我们还包含了单精度结果的初始FP,以进行考虑架构本身“上限”的评估。
从结果可以看出,只要适当调整块大小以更多地关注精度而非速度,我们的方法就可以在多种情况下击败最先进的方法。
当使用4位或5位的位大小时,DSConv可以击败最先进的方法。在这些情况下(例如ResNet 18使用 5 / 5 5/5 5/5,ResNet50使用 4 / 4 4/4 4/4和GoogLeNet使用 4 / 4 4/4 4/4),我们还使用了一个大块大小,在ResNet 18中使用 B = 128 B=128 B=128和 5 / 5 5/5 5/5位大小时,其FP效率略高,而在使用 4 / 4 4/4 4/4位大小时使用 B = 64 B=64 B=64。
为了获得3位或更少的最新结果,需要一个较小的块大小。DenseNet 121的结果证明了这一点,它使用2的位宽和16的块大小获得了 72.1 % 72.1\% 72.1%的精度。极低位的权重和激活值效果不太好,因为量化中信息损失较少对应于更高精度的假设开始失效。这一点得到了以下事实的支持:1位和2位权重的最新方法是从头开始训练的,这表明对于这些情况,从预训练网络进行量化并不理想。
我们还展示了压缩方面的好结果。具有4位和 B = 128 B=128 B=128的ResNet50表明,没有观察到精度损失,即使仅使用2位,在使用 B = 32 B=32 B=32时精度也能保持在 1 % 1\% 1%以内。
5. 结论
我们提出了DSConv,它提出了一种替代卷积算子,可以在将模型量化为最多4位的权重和激活值(无需重新训练或调整)的同时实现最先进的结果。
我们证明了我们的方法可以在少于8位设置下实现最先进的结果,而无需重新训练,这使得快速推理和减少定制硬件中快速部署的功耗成为可能。由于具有可通过块大小超参数进行调整且无需任何训练数据即可运行的优势,我们提出该方法非常适合加速任何类型的卷积神经网络。
当使用未标注数据和从FP32模型进行蒸馏时,我们可以为权重和激活值都使用4位,从而实现小于 1 % 1\% 1%的损失。此外,与以前的方法一样,我们证明了在量化到极低位(1、2或3位)时,权重量化中信息损失较少对应于更高推理精度的假设会失效。在这些情况下,由于它们与高精度模型本质上不同,因此重新训练似乎是不可避免的。
代码
import math
from torch.nn.modules.conv import _ConvNd
from torch.nn.modules.utils import _pair
class DSConv(_ConvNd):
def __init__(self, in_channels, out_channels, kernel_size, stride=1,
padding=None, dilation=1, groups=1, padding_mode='zeros', bias=False, block_size=32, KDSBias=False, CDS=False):
padding = _pair(autopad(kernel_size, padding, dilation))
kernel_size = _pair(kernel_size)
stride = _pair(stride)
dilation = _pair(dilation)
blck_numb = math.ceil(((in_channels)/(block_size*groups)))
super(DSConv, self).__init__(
in_channels, out_channels, kernel_size, stride, padding, dilation,
False, _pair(0), groups, bias, padding_mode)
# KDS weight From Paper
self.intweight = torch.Tensor(out_channels, in_channels, *kernel_size)
self.alpha = torch.Tensor(out_channels, blck_numb, *kernel_size)
# KDS bias From Paper
self.KDSBias = KDSBias
self.CDS = CDS
if KDSBias:
self.KDSb = torch.Tensor(out_channels, blck_numb, *kernel_size)
if CDS:
self.CDSw = torch.Tensor(out_channels)
self.CDSb = torch.Tensor(out_channels)
self.reset_parameters()
def get_weight_res(self):
# Include expansion of alpha and multiplication with weights to include in the convolution layer here
alpha_res = torch.zeros(self.weight.shape).to(self.alpha.device)
# Include KDSBias
if self.KDSBias:
KDSBias_res = torch.zeros(self.weight.shape).to(self.alpha.device)
# Handy definitions:
nmb_blocks = self.alpha.shape[1]
total_depth = self.weight.shape[1]
bs = total_depth//nmb_blocks
llb = total_depth-(nmb_blocks-1)*bs
# Casting the Alpha values as same tensor shape as weight
for i in range(nmb_blocks):
length_blk = llb if i==nmb_blocks-1 else bs
shp = self.alpha.shape # Notice this is the same shape for the bias as well
to_repeat=self.alpha[:, i, ...].view(shp[0],1,shp[2],shp[3]).clone()
repeated = to_repeat.expand(shp[0], length_blk, shp[2], shp[3]).clone()
alpha_res[:, i*bs:(i*bs+length_blk), ...] = repeated.clone()
if self.KDSBias:
to_repeat = self.KDSb[:, i, ...].view(shp[0], 1, shp[2], shp[3]).clone()
repeated = to_repeat.expand(shp[0], length_blk, shp[2], shp[3]).clone()
KDSBias_res[:, i*bs:(i*bs+length_blk), ...] = repeated.clone()
if self.CDS:
to_repeat = self.CDSw.view(-1, 1, 1, 1)
repeated = to_repeat.expand_as(self.weight)
print(repeated.shape)
# Element-wise multiplication of alpha and weight
weight_res = torch.mul(alpha_res, self.weight)
if self.KDSBias:
weight_res = torch.add(weight_res, KDSBias_res)
return weight_res
def forward(self, input):
# Get resulting weight
#weight_res = self.get_weight_res()
# Returning convolution
return F.conv2d(input, self.weight, self.bias,
self.stride, self.padding, self.dilation,
self.groups)
class DSConv2D(Conv):
def __init__(self, inc, ouc, k=1, s=1, p=None, g=1, d=1, act=True):
super().__init__(inc, ouc, k, s, p, g, d, act)
self.conv = DSConv(inc, ouc, k, s, p, g, d)
标签:Yolo11,权重,卷积,self,张量,DSConv,使用,量化
From: https://blog.csdn.net/m0_47867638/article/details/144506615