目前,人工智能领域的先进技术层出不穷,如计算机视觉、自然语言处理、图像生成、深度神经网络等。然而神经网络参数回写,这些新技术的成本在计算能力、内存或能耗方面可能令人望而却步,其中一些对于硬件资源有限的大多数用户来说是完全无法承受的。因此,人工智能的许多领域将根据需要修剪神经网络,以降低其运行成本,同时确保其性能。
这就是神经网络压缩的重点。在这个领域,实现神经网络压缩的方法有很多,比如量化、分解和蒸馏等,本文的重点是神经网络的剪枝。 .
神经网络剪枝旨在去除网络中性能良好但耗费大量资源的冗余部分。虽然大型神经网络的学习能力是有目共睹的,但事实上,并不是所有的神经网络在经过训练的过程之后都是有用的。神经网络剪枝的思想就是在不影响网络性能的情况下去掉这些无用的部分。
在这个研究领域,每年发表数十篇(如果不是数百篇)论文,大量论文揭示了这个想法的复杂性。通过阅读这些文献,可以在训练前后快速识别和去除神经网络的无用部分。值得强调的是,并不是所有的剪枝都能加速神经网络,有些工作甚至可以事半功倍。
本文在阅读原生神经剪枝论文的基础上,提出了神经网络剪枝的解决方案,进而回答了该领域的三个核心问题:“哪部分应该剪枝?”、“如何判断哪些部分要剪枝? ”,以及“如何在不损害网络的情况下进行修剪?”。综上所述,本文将详细介绍神经网络剪枝的结构、剪枝准则和剪枝方法。
1 — 修剪结构1.1 — 非结构化修剪
谈到神经网络的成本,参数数量和 FLOPS(每秒浮点运算)是使用最广泛的指标之一。看到网络展示天文参数(对某些人来说花费数十亿美元)真的令人生畏。这种方法肯定是通过直接去掉参数来实现的,从而直观地减少了参数的数量。事实上,这种方法在几篇文献中都有提及,而剪枝参数是文献中提到的最广泛使用的例子,被视为处理剪枝时的默认框架。
直接剪枝参数的方法有很多优点。首先,它非常简单。在参数张量中,将其权重值设置为零,实现参数剪枝。在 Pytorch 深度学习框架中,网络的所有参数都可以轻松访问,实现起来非常简单。尽管如此,修剪参数的最大优势在于它们是网络中最小、最基本的元素,因此只要有足够多的参数,就可以在不影响性能的情况下对它们进行大量修剪。这种精细修剪的粒度可以在非常精细的模式下进行修剪,例如修剪卷积核内的参数。由于剪枝权重完全不受任何约束,是剪枝网络的最佳方式,因此这种方法称为非结构化剪枝。
但是,这种方法的致命缺点是大多数深度学习框架和硬件无法加速稀疏矩阵的计算,这意味着无论你在参数张量中填充多少个零,都不会对训练成本产生实质性影响,并且只是一种直接更改网络架构以进行剪枝的方法,而不是针对任何框架的一刀切的方法。
非结构化(左)和结构化(右)剪枝的区别:结构化剪枝会同时移除卷积滤波器和内核线,而不仅仅是修剪参数。这样可以减少中间特征图的数量。
1.2 — 结构化修剪
Structured Pruning 侧重于修剪较大的结构,例如修剪整个神经元,或者,在现代深度卷积网络中,直接修剪卷积滤波器。大型网络往往包含许多卷积层,每层有数百或数千个过滤器,允许对卷积层过滤器进行细粒度修剪。去掉这个结构,不仅让深度神经网络的层结构更加稀疏,这样做也去掉了滤波器输出的特征图。
使用更少的参数,这个网络不仅存储起来更轻,而且计算量也更少,从而产生更方便的中间表示,在运行时需要更少的内存。事实上,有时减少带宽比减少参数数量更有益。对于涉及大图像的任务,例如语义分割或对象检测,中间表示可能比网络本身更占用内存,因此可以将过滤器修剪视为默认的结构化修剪。
在应用结构化剪枝时,应注意以下几个方面:首先,应考虑如何构建卷积层。对于 Cin 输入通道和 Cout 输出通道,卷积层每个滤波器分别计算 Cin 核;每个过滤器输出一个特征图,每个输入通道专用于一个内核。基于这种架构,卷积网络是多个卷积层的堆叠。当整个filter被剪枝时,可以观察到剪枝每个filter的过程,然后输出特征图。这个过程也会导致后续层内核的剪枝。这意味着在对过滤器进行剪枝时,在第一次移除参数后,实际移除的参数数量是最初认为要移除的参数数量的很多倍。
让我们考虑这种特殊情况,当所有卷积层都被不经意地剪枝时(虽然卷积层被剪枝,但神经网络并没有被破坏,这是由神经网络引起的。它是由网络的架构决定的)并且不能链接到前一层的输出。这也可以是神经网络的一种剪枝方法:剪枝所有卷积层其实就相当于剪掉了前一层的所有输出,所以只连接到其他地方(比如残差连接或者并行通道)。
在剪枝过滤器时,应先计算出实际参数的确切个数神经网络参数回写,然后再根据神经网络架构中过滤器的分布情况剪除相同数量的过滤器。剪枝参数数量不同,结果不具有可比性。
在继续下一个主题之前,应该提到的是,仍有少量工作集中在修剪卷积核、内核内架构甚至特定参数结构上。但是,这些架构需要特殊的方法(例如非结构化修剪)。另外,另一种方法是对每个内核中的某个参数进行剪枝,将卷积层变成“移位层”,这可以通过一个移位操作和一个 1×1 卷积的组合来实现。
结构化剪枝的缺点:输入和输出维度的变化会引入一些偏差。
2——修剪的原则
在决定使用什么结构进行修剪后,下一个问题是:“现在,您如何找出要保留哪些部分以及要保留哪些部分?需要修剪吗?”要回答这个问题,请根据排名参数、过滤器或其他特征生成适当的修剪标准。
2.1 — 权重标准
一个非常直观有效的标准是用最小的绝对权重修剪那些参数。事实上,在权重衰减的约束下,那些对函数没有显着贡献的参数在训练过程中的幅度不断减小。因此,那些权重相对较小的参数是多余的。原理很简单,这个准则在当前神经网络的训练中被广泛使用,成为了该领域的主角。
尽管这个标准在非结构化剪枝的实现中似乎微不足道,但人们想知道它如何应用于结构化剪枝。一种简单的方法是根据它们的规范对过滤器进行排序(例如 L1 或 L2) 。这种方法通过将多个参数封装在一起来实现简单粗暴:例如卷积过滤 过滤器的偏差和批量归一化参数被打包在一起,以融合并行层中的滤波器输出,从而减少通道数。
一种方法是在不计算所有参数的组合范围的情况下执行此操作。为需要修剪的每一层的特征图插入一个可学习的乘法因子,当它减少到零时,有效地去除了负责该通道的所有参数集,该方法可用于修剪具有较小幅度参数的权重。
2.2 — 梯度大小修剪标准
Weight Size Pruning Criterion 并不是唯一流行的准则,其实还有一个重要的准则,那就是梯度大小剪枝准则也很适用。根据 1980 年代的一些基本理论,通过泰勒分解消除了参数对损失的影响,而一些指标,如反向传播的梯度,可以提供很好的判断方法。 , 以确定可以在不损害网络的情况下修剪哪些参数。
在实际项目中,这个准则是通过先计算mini-batch训练数据的累积梯度来实现的,然后根据这个梯度和每个参数对应的权重之间的乘积进行剪枝。
2.3 — 全局或局部修剪
最后一个要考虑的因素是选择网络的剪枝标准是适用于网络的所有参数或过滤器,还是设计为每个层独立计算。虽然神经网络的全局修剪可以产生更好的结果,但它可能导致层崩溃。避免这个问题的简单方法是在使用的全局剪枝方法不能防止层崩溃的情况下,使用逐层局部剪枝。
局部剪枝(左)与全局剪枝的区别(右):局部剪枝分别对每一层进行剪枝,而全局剪枝同时应用于整个网络
3——剪枝方法
在明确了剪枝结构和剪枝标准之后,剩下的就是应该使用哪种方法来剪枝神经网络。这其实是最让人迷惑的话题,因为每篇论文都自带了自己独特的剪枝方法,让大家可能会对选择什么样的方法来实现神经网络的剪枝感到困惑。
这里,这个话题将用于讨论当前流行的神经网络。网络剪枝方法概述,强调训练过程中神经网络稀疏性的演变。
3.1 — 经典框架:训练、剪枝和微调
训练一个神经网络的基本框架是:训练、剪枝和微调,其中涉及到1)训练网络2)根据剪枝结构和剪枝准则的要求,设置要修剪的参数为0(这些参数以后无法恢复),3)添加额外的epochs来训练网络,将学习率设置为最低,这样神经网络就有机会从性能损失中恢复修剪造成的。通常,最后两个步骤可以迭代,每次迭代都会提高剪枝率。
具体剪枝方法如下:根据权重剪枝原理,在剪枝和微调之间进行5次迭代。实验表明,通过迭代可以显着提高训练性能,但代价是额外的计算能力和训练时间。这个简单的框架是许多神经网络剪枝的基础,可以看作是训练神经网络的默认方法。
3.2——经典框架的扩展
有一些方法可以进一步修改上述经典框架。越多,迭代过程越快,从而受益于迭代的优势,同时去掉了整个微调过程。在每个 epoch 中逐渐将 pruable 过滤器的数量减少到 0 并不会阻止神经网络继续学习和更新,允许它们的权重在修剪后重新增长,同时增强训练中的稀疏性。
最后,Renda 等人的方法。表示在网络被修剪后执行再训练。与使用最低学习率的微调不同,重新训练使用与原始相同的学习率,因此这种剪枝方法称为“学习率倒带”。这种在剪枝后重新训练的方法优于微调网络。
3.3 — 在初始化时修剪
为了加快训练,避免微调,并防止在训练期间或之后对神经网络架构进行任何更改,很多工作都集中在训练前的剪枝上:Smolenski 在网络初始化时对网络进行剪枝; OBD(Optimal Brain Damage)在修剪网络初始化时使用了多种近似,包括一种“极端”近似,即“假设训练收敛后将执行参数去除”并不常见;一些研究也对这种方法生成掩码的能力持保留态度,神经网络为每一层随机生成的掩码具有相似的分布特征。
另一组研究剪枝和初始化之间关系的方法围绕着“彩票假说”展开。这个假设指出“一个随机初始化的密集神经网络包含一个初始化的子网络,当单独训练时,在训练相同数量的迭代后可以匹配原始网络的测试精度”。在项目实践中,收敛剪枝掩码在刚初始化时使用。但是,对于这种剪枝方法的有效性仍有很多疑问,有专家认为,使用特定掩码训练的模型的性能甚至可以优于“赢家”假设下获得的性能。
经典剪枝框架与彩票假设的学习率调整对比
3.4 —稀疏训练
p>
上述方法都具有相同的基本主题:在稀疏约束下进行训练。该原则的核心是一系列稀疏训练方法,其中包括强制执行恒定的稀疏率并随着训练分布的变化而逐渐调整。由 Mocanu 等人提出,包括:
1)用随机掩码初始化网络,并按一定比例修剪网络
2)在一个时期内训练这个修剪后的网络
3)修剪一定数量的较小权重,并4)重新生成相同数量的随机权重。
在这种情况下,修剪掩码是随机的,并且会逐步调整以针对最小的导入权重,同时在整个训练过程中强制执行稀疏性。稀疏程度可以跨层或全局相同。
稀疏训练在训练过程中周期性地切割和增长不同的权重,调整后的权重与相关参数的mask相关
3.5 — 面具学习
还有一些方法专注于在训练期间学习掩码修剪,而不是利用特定标准来修剪权重。在这个领域,比较流行的有以下两种方法:1)分离网络或层掩码学习,2)通过辅助参数进行掩码学习。第一种方法可以采用各种策略:每层修剪尽可能多的过滤器,插入基于注意力的层或使用强化学习,同时最大限度地提高准确性。第二种方法将剪枝视为一个优化问题,倾向于最小化网络的 L0 范数和监督损失。
由于 L0 是不可微的,一些方法主要围绕使用辅助惩罚参数。在前向传播中,辅助惩罚参数乘以它们对应的参数来规避这个问题。还有一些方法采用类似“二元连接”的方法,即在选择参数时应用随机门的伯努利分布,p 使用“直接估计器”或其他学习方法获得。
3.6 — 基于惩罚的方法
有很多方法可以应用各种惩罚来增加权重,使它们逐渐缩小到 0,而不是手动修剪连接或惩罚辅助参数。其实这个概念已经很老了,因为权重衰减是衡量权重大小的基本标准。除了单独使用权重衰减之外,还有许多专门设计用于执行稀疏惩罚的方法。目前,还有一些方法在权重衰减之上应用不同的正则化,以试图进一步增加稀疏性。
在最近的研究中,有几种方法使用 LASSO(最小绝对收缩和选择算子)来修剪权重或组。其他一些方法也使用弱连接惩罚来增加保留参数和修剪参数之间的距离,以尽量减少它们对性能的影响。经验表明,通过对整个训练过程进行惩罚,可以逐步对参数进行剪枝,从而实现无缝剪枝。
4 - 可用的框架
在神经网络的训练过程中,无需从头实现(重用论文作者提供的源代码),将上述基础知识应用到一些现成的框架上。剪枝方法相对容易实现。
4.1 — Pytorch
Pytorch 为网络修剪提供了各种高质量的功能。使用 Pytorch 提供的工具,可以轻松地将掩码应用到网络上,在训练期间维护此掩码,并在需要时轻松恢复它。 Pytorch还提供了一些基本的剪枝方法,比如全局剪枝或者局部剪枝,无论是结构化剪枝还是非结构化剪枝,都可以实现。结构化修剪适用于任何维度的权重张量,可以修剪过滤器、内核行,甚至内核中的某些行和列。这些内置的基本方法还允许随机或根据各种标准进行修剪。
4.2——张量流
Tensorflow 的 Keras 库提供了一些基本工具来修剪具有较低权重的参数。修剪的效率基于所有插入零的引入。测量冗余,可以更好地压缩模型(它适用于量化)。
4.3 — ShrinkBench
布莱洛克等人。开发了一个自定义库 ShrinkBench 来帮助规范化社区修剪算法。这个自定义库基于 Pytorch 框架,旨在使修剪方法的实现更容易,同时规范化训练和测试条件。它为随机剪枝、权重大小剪枝或梯度大小剪枝等不同剪枝方法提供了不同的基准。
5—结论
综上,可以看出
1)剪枝结构定义了什么样的好处
2)修剪标准基于多种理论和实践技术的结合
3)剪枝方法倾向于在训练期间引入稀疏性以调整性能和成本。
另外,虽然神经网络的基础工作可以追溯到1980年代后期,但神经网络剪枝目前是一个非常活跃的领域,新的基础理论和新的基本概念有望涌现。 .
虽然该领域正在蓬勃发展,但仍有很大的探索和创新空间。每种剪枝方法都试图回答一个问题(“如何重建剪枝权重?”“如何通过优化学习剪枝掩码?”“如何释放已剪枝的权重......”),以上所有研究都指向一个方向:整个训练过程中的稀疏性。同时,这个方向带来了许多新的问题,例如:“剪枝准则在尚未收敛的网络上是否有效?”、“如何判断剪枝选择的权重是否有利于所有稀疏训练?”