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