使用NVIDIA Transfer Learning Toolkit修剪网络

翻译自:https://devblogs.nvidia.com/transfer-learning-toolkit-pruning-intelligent-video-analytics/

在使用深度学习模型进行生产时,模型进行准确预测非常重要。这些预测的效率也很重要。效率测量的示例包括电气工程师测量能量消耗以选择最佳电压调节器,机械工程师定时推断延迟以确定电机控制回路最大速度,嵌入式软件工程师需要将推理引擎纳入严格的内存限制,以及公司财务估算成本评估利润潜力的预测。

NVIDIA迁移学习工具包提供了一个称为模型修剪的关键功能,可以解决这些问题。修剪已经长期被用于降低神经网络的复杂性,正如1990年广受好评的Optimal Brain Demage(OBD)[1]论文所证明的那样。术语修剪借鉴了决策树中使用的类比技术,从修剪树木的园艺实践。

树木通常带有大量死枝,既不产生叶子也不产生果实,但却导致树木不必要地沉重。这些枯枝可以安全地切割而不会影响引线分支,这在大多数情况下可以从额外的空间中受益。同样,您可以删除神经网络中不必要的连接,以便相应的计算不需要执行,从而释放内存、时间、能量和金钱。

修剪通常允许在TLT所针对的视觉应用中将参数的数量减少一个数量级,从而导致模型快许多倍。

1 移除不必要的链接

在这里插入图片描述
我们将看看如何识别要修剪的连接。首先,让我们看看如何在实践中进行删除以及为什么它有用。考虑图1所示的神经网络 - 您可能会在那里识别出多层感知器[2](MLP)。这包括六个输入,一个隐藏的四个神经元层和两个输出。我需要执行六次乘法累加(MLA)操作,以便计算隐藏层中每个神经元的激活。计算每个输出需要四个MLA。总共需要执行(2×4)+(4×6)= 32个MLA,存储器中存储32个参数(权重)。

假设通过一些神奇的过程,我确定隐藏层中的红色神经元可以完全去除,而不会显着影响我的神经网络的输出。我现在只有(2×3)+(3×6)= 24个MLAs。这将存储参数所需的计算复杂度和内存减少了25%。

我使用了上面的一些近似值来表达我的观点,所以让我们再次回顾计算MLP中层l内神经元i的激活 x l , i x_ {l,i} xli背后的数学原理,给定激活函数 ϕ \phi ϕ、权重向量 w l , i w_ {l,i} wli和偏差项 b l , i b_ {l,i} bli

在这里插入图片描述
上面的公式显示计算密集度最高的项是加权输入的总和。

修剪该层中神经元 k ∗ k^* k的方法有多种:

  • 考虑总和中的一些元素不需要评估,即对某些 j 而言, w l , k ∗ , j = 0 w_{l,k^{*},j} = 0 wl,k,j=0。这种近似需要框架对稀疏计算具有极好的支持,但这在深度学习框架中是不常见的;
  • 还要考虑可以忽略求和的所有元素,即对所有的 j 而言, w l , k ∗ , j = 0 w_{l,k^{*},j} = 0 wl,k,j=0,可以写作 x l , k ∗ = ϕ ( b l , k ∗ ) x_{l,k^{*}} = \phi(b_{l,k^{*}}) xl,k=ϕ(bl,k)。这样做完全移除了求和过程,大大减少了计算量,但需要前向传播偏差项 b l , k ∗ b_{l,k^{*}} bl,k
  • 偏差项和通过 ϕ \phi ϕ的非线性变化可以被忽略,因此有 x l , k ∗ = 0 x_{l,k^{*}} = 0 xl,k=0。这是一个粗略的近似但大大简化了计算过程。

NVIDIA Transfer Learning Toolkit只是忽略偏差项。我们这样做的主要原因是在任何深度学习框架中都可以直接实现。我们创建一个原始模型的克隆,只是在 l 层少一个神经元并复制所有保留的权重 w l , i , j w_ {l,i,j} wlij和偏置项 b l , i b_ {l,i} bli ( i ≠ k ∗ i \neq k^* i̸=k)。我将向您展示为什么这个粗略的近似在后面的部分中不会对实践造成太大影响。

我刚刚描述了如何对全连接层进行修剪。然而,我们在Transfer Learning Toolkit中提供的模型大部分由卷积神经网络组成,并且在多通道图像上运行。我们如何修剪卷积层?

考虑固定输入的大小, C i n C_{in} Cin个通道、高 h h h、宽 w w w,2D卷积层具有 C o u t C_{out} Cout个滤波器,每个滤波器大小为 m × n m \times n m×n。该卷积层近似于这样的一个全连接层,该全连接层具有 C o u t C_{out} Cout个神经元,每个神经元重复扫描输入,该卷积层和输入层具有大量的连接,总参数数量是 C o u t × C i n × m × n C_{out} \times C_{in} \times m \times n Cout×Cin×m×n加上 C o u t C_{out} Cout个偏差项。我们在上面解释了如何修剪全连接层的神经元。通过类比,我们还修剪了卷积层中的滤波器。

2 选择不必要的神经元

上一段解释了如何去除神经元,但我还没有解释如何选择它们。

2.1 数据驱动方法

确定地识别神经网络中作用最小的神经元意味着逐个从模型中移除每个神经元,然后在验证数据集上再次评估模型,挑选去除之后导致最佳验证度量的神经元。如果模型中有一千个神经元,就需要进行一千次评估。即使可以并行运行这些评估也意味着按顺序重复此过程的次数与要移除的神经元一样多。这样迭代处理会非常费力,因此需要做一些近似使消除神经元的过程更易于处理。

近三十年前OBD论文首先提出了识别可裁剪的神经元的问题。该文章建议设立一个分析模型确定参数小扰动对评估度量的影响。问题是该模型缺乏封闭形式的公式。

OBD论文提示我们可以使用度量 m ( x + δ u ) m(x+\delta u) m(x+δu)的近似解,其中 m 是度量函数,x 是当前神经网络参数,u 是应用于参数的小的扰动(在这里,表示设置某神经元的权重为0)。这种近似通常使用以下形式的泰勒展开式执行:

在这里插入图片描述
m ( i ) m^{(i)} m(i)表示函数 m 的 i 阶导数。由于我们只关心我们的度量 m 随 u 的变化,所以我们只需要考虑函数 m 的导数。有趣的是,OBD论文声称,由于假设网络已经收敛并达到局部最小值,因此一阶导数应该接近于零,而二阶导数带有大部分信息。

相反,最近的论文Pruning Convolutional Neural Networks For Resource Efficient Inference声称虽然一阶导数平均可能接近于零,但它们仍然保留了相当大的方差,可以作为是否去除某特定神经元的一个很好的指标。由于二阶导数很难计算(由于几乎所有的深度学习框架都缺乏支持),因此应该使用一阶导数,因为在训练模型时可以方便地在反向传播期间计算它们(但是在训练集上,不是验证集!)。这两种方法实施起来仍然有些复杂。此外,很难评估这些近似值所增加的噪声是否超过了制造它们的好处。

另一种数据驱动的启发式算法基于直觉,即不重要的神经元的激活值较小。实现很简单:将所有的验证数据输入模型并记录每个神经元的输出。那些具有最小输出的神经元可以被修剪。此外,我甚至不需要标签。但是,直觉是否有效?一个神经元可能具有大的输出,但如果后续层在该神经元激活上具有非常小的权重,则该神经元对总体最终输出几乎没有贡献。相反,一个具有较小输出的神经元可以在后续层中放大其作用。

2.2 非数据驱动方法

现在让我介绍最琐碎的神经元选择方法,基于直觉,即具有最小权重的神经元是最不重要的。这非常简单,甚至不需要数据!类似于基于激活的神经元选择,理论上没有理由这样做,除非我对学习计划引入了一个关键修改:惩罚具有大权重的神经元。这种技术通常被使用并称为权重衰减正则化,包括在总损失 L t o t a l L_ {total} Ltotal中添加一个正则化项以优化:
在这里插入图片描述
L是总损失值, W i W_i Wi是模型的权重。添加正则化项会改变一切;只有导致主要loss的神经元才能具有较大的权重。

我可以使用在训练循环之外运行的脚本来实现此方法,并且仅检查权重并修剪其范数低于任意阈值 t 的所有神经元。换句话说,API看起来像:

pruned_model = TLT.prune(model, t)

这里需要考虑其他因素,因此TLT.prune API采用可选参数。

一个参数指定我们在每个层中修剪神经元的粒度。模型模板中每层中的神经元数量是2的幂。这源于GPU计算单元的利用率通常最大化为并行执行 2 N 2 ^ N 2N次。由于滤波器数量从32减少到31可能不会节省太多时间,因此tlt-prune中的默认粒度设置为8。

例如,在卷积层的情况下,滤波器的范数取决于滤波器的大小。3×3滤波器可能具有比1×1滤波器更大的范数。为了使修剪阈值在各层之间具有可比性,我们将每个神经元的范数除以最大神经元的范数(默认)或一个层内的平均范数。tlt-prune的优化器参数可用于覆盖默认设置。

2.3 处理多个输入的逐元素操作

还有一个复杂的问题是我没有提到的识别要修剪的神经元的过程。这一点出现在存在多个输入元素操作的情况下。

例如,让我们看一下残差网络。TLT模板中包括几种变体,其中恒等直连将衰减信息直接传播到网络的输出。图1示出了逐元素加法用于实现直连x和残差块F(x)的和。如果裁剪底部权重层的神经元会发生什么?在一般情况下,因为 x 和 F(x) 的形状不同,因此无法计算 x 和 F(x) 的和。即便我们对 x 也进行裁剪,保证 x 和 F(x) 具有同样的大小,我们也需要百分百确认我们对x和F(x)裁剪的是相匹配的神经元对,否则取得的和是没有意义的。每个神经元在求和的每个其他输入中都具有相应的神经元,并且所有神经元都需要被相同地修剪。

在这里插入图片描述
我在实践中这样做的方法是通过一个名为均衡的方法。首先,我选择均衡运算符。我的运算符可以是任何多变量可交换运算符。例如:

  • 算术平均值。 F ( x 1 , x 2 , ⋯   , x n ) = ∑ x n / N F(x_1,x_2,\cdots,x_n) = \sum x_n / N F(x1,x2,,xn)=xn/N。如果在逐元素操作的输入上的所有对应神经元的平均范数低于修剪阈值,则算术平均值算子修剪该神经元。
  • 几何平均值。 F ( x 1 , x 2 , ⋯   , x n ) = Π   x n 1 / N F(x_1,x_2,\cdots,x_n) = \Pi~x_n^{1/N} F(x1,x2,,xn)=Π xn1/N。除了使用几何平均值之外,这与上述算术平均值算子类似。
  • 并集。 F ( x 1 , x 2 , ⋯   , x n ) = t i f a n y x n > = t F(x_1,x_2,\cdots,x_n) = t if any x_n >= t F(x1,x2,,xn)=tifanyxn>=t,如果至少一个相应神经元的范数低于阈值,则修剪神经元。
  • 交集。 F ( x 1 , x 2 , ⋯   , x n ) = t F(x_1,x_2,\cdots,x_n) = t F(x1,x2,,xn)=t 所有对应的神经元必须都小于阈值t。

我需要在存在和、减法乘积或在多个输入层上的任何其他元素操作的情况下找到紧靠元素操作之前的可修剪层。可修剪层由具有神经元的层组成,并可能改变张量的形状。不可修剪的层是诸如激活层的层。

假设有两个输入层 L 1 L_1 L1 L 2 L_2 L2,每层有K个神经元。在计算了它们的神经元范数后,得到了两个向量 ∣ ∣ L 1 ∣ ∣ ||L_1|| L1 ∣ ∣ L 2 ∣ ∣ ||L_2|| L2。我的均衡运算符给了我另一个向量 F ( ∣ ∣ L 1 ∣ ∣ , ∣ ∣ L 2 ∣ ∣ ) F(||L_1||,||L_2||) F(L1,L2),我用它来覆盖层的范数。在我这样做之后,我强制这两个层具有相同的范数。因此,两个层按照相同的规则被修剪,不再需要担心逐元素操作。

我将从输入到输出遍历神经网络三次:

  • 在第一遍中,我计算每个神经元的范数。
  • 在第二遍中,我寻找元素操作。如果我找到一个,我可以平衡其可修剪输入的范数。
  • 在第三遍中,我移除了其范数低于阈值 t 的所有神经元。

3 设定训练计划

如前所述,我需要训练相当数量的权重正则化,以便使用非数据驱动的方法来选择要修剪的神经元。一般而言,我选择 λ \lambda λ值使得惩罚项 λ ∑ i ∣ ∣ W i ∣ ∣ \lambda \sum_i ||W_i|| λiWi几乎等于主损失 L。使用默认的修剪标准化器和10%的阈值通常会导致可接受的修剪比率。

修剪后,我可以立即在验证数据集上再次评估较小的模型。可能会发生几种情况:

  • 指标几乎没有变化。这是我在大多数情况下观察到的。尽管去除了许多神经元,但该模型表现同样出色。
  • 指标有所改善。这种情况相对较少发生,但修剪是另一种可能有助于对抗过度拟合并提高模型泛化能力的正则化因子。
  • 度量指标变差。有时会观察到严重的精确度损失。如果我们意外删除了有用的神经元,可能会发生这种情况。但是,如果我继续训练我的模型,我可以看到模型会在大多数情况下很快恢复,表现会与原始模型一样。

在一般情况下,我不知道修剪后会发生上述三种情况中的哪一种。因此,我的典型训练计划如下:

  • 结合权重正则化进行模型训练;
  • 修剪;
  • 重训练。

4 为什么不要一开始就训练一个小模型 ?

如果我们可以在神经网络中移除90%的权重并保持原始准确度,为什么我们不一开始就训练一个较小的模型?实际上,较小的网络将在很短的时间内完成训练,我们根本不需要实施修剪。训练大型网络听起来有点矫枉过正。

事实是,训练一个小模型并达到更大模型的准确性是非常困难的。这归结为原则上听起来非常简单的事情:权重的初始化。看看关于这个名为The Lottery Ticket Hypothesis [5]的一篇引人入胜的论文。论文假设在一个大模型中,有一个较小的模型,如果单独训练,将匹配较大模型的性能。然而,问题在于,在训练较大的模型完成之前,几乎不可能确定较小的模型。作者使用的类比是我们模型中使用的随机初始化就像彩票一样:如果你有很多这样的彩票,那么你很有可能赢得彩票,但如果你只有少数,你就必须非常幸运。

图3中间的图像示出了训练的AlexNet的第一卷积层中的滤波器的表示。您可以看到很多滤波器都是灰色的,表明它们的幅度很小,而底部的图像显示它们的激活也非常小。看起来好像这些过滤器没有学到任何东西。根据[5]中的假设,这表明这些不幸的过滤器仅仅得到了不利的初始化。

在这里插入图片描述图3:在ImageNet上训练的AlexNet的第一个卷积层中权重的可视化。顶部的图像显示了一个示例输入图像。中间的图像显示了第一层中96个11×11卷积滤波器的权重。底部的图像显示了96个滤波器的激活。

NVIDIA Transfer Learning Toolkit提供端到端的深度学习工作流程,用于加速深度学习培训,并在Tesla GPU上使用DeepStream SDK 3.0进行部署。用于智能视频分析的传输学习工具包(IVA)TLT现已开放供早期访问。如果您想试用TLT,请转到Transfer Learning Toolkit for IVA的开发人员早期访问计划。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值