神经网络压缩方法总结

首先,为什么需要对神经网络模型进行压缩呢?我们在之前的课程中介绍过很多大型的深度学习模型,但当我们想要将这些大模型放在算力比较小的边缘设备或者其他IoT设备里面,就需要对大模型进行压缩。

Lower latency:低时延 Privacy:私密性

介绍5个网络压缩的方法,我们只考虑算法(软件)层面,不考虑硬件层面的解决方法。

Network Pruning(网络剪枝)

对于一个大的网络来说,我们能想到的是,众多网络参数中一定会有不重要/冗余的一些参数,因此我们将这些参数减掉达到网络压缩的目的。

网络剪枝的步骤如下:首先,我们预训练一个大规模的网络,然后评估里面参数的重要性,包括权重(weight)的重要性和神经元(neuron)的重要性。

  • 评价weight重要性,我们可以用绝对值衡量,即绝对值越大,weight越重要,或者采用之前介绍的life long learning的想法。
  • 评价neuron重要性,我们可以用其输出的结果为0的次数衡量,即输出0越多越不重要。

接着我们对多余的参数的重要性评估并修剪,得到一个小的网络,再对里面的参数微调,再评估、修剪。。。重复上述过程,直到满足要求,完成Network pruning过程。

刚才提到,剪枝有两种形式,一种是以权重(weight)为单位,一种是以神经元(neuron)为单位,这两者有什么不同呢?

首先Weight pruning,通过除去一些冗余的weight,使网络结构变得简单一些,但这样就造成网络结构不规则(irregular),难以编程实现,同时难以用GPU加速。通常的做法是将冗余的weight置为0,但这样做还是保留了参数(等于0),不是真正去除掉。

在这篇论文中有个关于参数pruning多少与训练速度提升关系的实验验证,其中紫色线表示参数去掉的量。可以发现,虽然参数去掉了将近95%,但是速度依然没有提升。

接着Neuron pruning,通过去除冗余的神经元,简化网络结构。这样得到的网络结构是规则的,相比于Weight pruning,这种方式更好实现,也更容易通过GPU加速。

为什么我们先训练一个大的network,再压缩成一个小的network,而不是直接训练一个小的network呢?一般来说,大的network更容易训练,如果直接训练小的网络可能达不到大的network的训练效果。这里有个大乐透假说(Lottery Ticket Hypothesis)对上述观点进行了说明。

什么是大乐透假说(Lottery Ticket Hypothesis)呢?

现在有一个训练好的大的网络,可以分解成若干个小的网络,只要某一个小的网络性能与大的网络相同或相似,就说明这个大的网络可以压缩。

大乐透假说在实验上是怎么被证实的呢?

我们首先初始化网络结构,随机初始化参数并对网络进行训练,将训练完的网络做pruned。如果这时候我们还是采用这个压缩的网络结构但是随机初始化参数(绿色部分),发现网络训练不起来。但是如果我们采用这个压缩的网络结构,但是参数是从大网络对应参数位置拿过来训练(红色部分),就能训练起来。说明留下来的这组参数组成的网络是可以train起来的sub-network。

关于大乐透假说的一个后续的研究如下,通过充分的实验得到了一些结论:

  • 第一个发现是,尝试了不同的pruning strategy,发现如果训练前和训练后参数的差距越大,将其pruning后得到的结果越有效。
  • 第二个发现是,小的sub-network只要我们不改变参数的正负号,就可以训练起来。
  • 第三个发现是,对于一个初始的大的网络(参数随机初始化),有可能不训练就已经有一个sub-network可以有一个比较好的效果。

大乐透假说一定是对的吗?不一定。下面这篇文章就“打脸”了大乐透假说。实验是这样的,我们用pruned完的小网络随机初始化参数,再训练,只要多训练几个epoch,就可以比不随机初始化训练小网络的效果要好。

当然这篇文章的作者也给出了一些对大乐透假说的回应,大乐透假说出现的前提是当learning rate很小,或者unstructured(做Weight pruning )时候才有可能出现大乐透假说现象。


Knowledge Distillation(知识蒸馏)

首先我们先定义一个Teacher Net(大网络),训练它做特定的任务,比如手写数字识别,如图所示,训练好的大网络收敛后,预测“1”的结果可能“1”的概率是0.7,“7”的概率是0.2,“9”的概率是0.1.现在我们再定义一个Student Net(小网络),让它学习Teacher Net的训练结果。为什么这么做呢?因为我们之前说过,如果让小的网络得到跟大网络一样的效果往往比较难train,所以我们直接按照大network的训练精度训练小网络,会更容易train一些。

在上面给出的论文中也有介绍说,利用这种方式训练出的小网络,可能就算不给它“7”数据集样本,也有可能有一定的概率预测出“7”这个数字。因为“1”跟“7”很相近。

这个Teacher Net不一定是一个巨大的network,也有可能是将多个network组合(ensemble)得到的。但是多个network组合的模型往往比较复杂,在实际应用中,我们可以训练一个Student Net,让结果逼近N Networks的结果,使得模型准确度差不多的情况下,复杂度大大减少。

关于Knowledge Distillation的一个小技巧,在softmax函数基础上对每个输出结果加一个超参数T(Temperature),这样会对最后的预测结果进行一个平滑处理,让Student Net更好训练一些。

Parameter Quantization(参数量化)

参数量化,也可以称为参数压缩。这种方法的主要是对weight在存储量上减少的一类方法,具体来说有如下几种方式:

  1. 对于Weight的精度可能不需要太高就可以获得一个比较好的效果,比如从64位调整为32位或者16位等等,这样就可以减少存储的数据量。
  2. Weight clustering(权重聚类):将神经网络所有weight按值大小进行分堆,数值差不多的聚成一类。然后对每个类取一个值(可以是平均值)替换里面所有的权值,相当于每一堆只用一个值就可以存储,这样存储的数据量也大大减少。
  3. 采用信号处理中常用的一种方法:Huffman encoding(哈夫曼编码),常出现的东西用比较少的bit描述,不常出现的东西用比较多的bit描述,这样平均起来存储的数据量将大大减少,这也是一种有效减少数据量的方法。

weight到底可以压缩到什么程度呢?这里有种方法叫Binary Weights,也就是网络中的weight要么是+1,要么是-1,这样每个weight只需要1bit就可以存下来。

那这样训练出的网络效果会不会不太好?这里有一篇文章介绍了该方法用于3种数据集的图像分类问题中,结果发现BinaryConnect的方法识别错误率更小,原文给出的解释是这种方法会在一定程度上减少过拟合情况的发生。

Architecture Design(Depth Separable Convolution)

这里介绍一种关于CNN的减少参数量的结构化设计。

首先回顾一下CNN,假设输入有2个channel,对应的filter也是2个channel。假设有4个filter,每个filter都是3*3的,那么输出就有4个channel。卷积层共有 [公式] 个参数。

接着介绍Depth Separable Convolution,它分为两个步骤:

  1. Depthwise Convolution

它在做卷积的时候与传统的对图像做卷积有很大的不同。图片有几个channel就对应有几个filter,每个filter只管一个channel。channel大小是 [公式] ,channel数为1,因此channel与channel之间的联系无法体现出来。

上述过程有 [公式] 个参数。

2. Pointwise Convolution

为了解决无法学习输入图像channel与channel之间联系的问题,将Depthwise Convolution的输出结果用 [公式] 的filter做卷积,以4个filter为例,效果如下:

上述过程有 [公式] 个参数。

将标准CNN和Depth Separable Convolution参数量做对比,可以发现Depth Separable Convolution参数量比CNN要少很多。

上述方法为什么有效呢?这里有个解释(Low rank approximation)。如果神经网络某一层输入为N,输出为M,那么对应的weight就有 [公式] 个。这时,如果我们在N和M中间加一层,对应输出为K,那么参数量就是 [公式] 。当K比较小时,参数量相比于 [公式] 会大大减少。

结合上述思想,Depth Separable Convolution相当于将CNN中间多加了一层,这样就可以减少整体网络的参数量。

关于网络结构设计方面还有一些文献参考,感兴趣可以看一下里面相关的内容,这里就不多介绍。

Dynamic Computation(动态计算)

所谓Dynamic Computation就是让神经网络可以自适应调整计算量,比如让神经网络自适应不同算力的设备,或者同一设备不同电量时对算力的分配。

为什么不在一个设备上放好多个模型呢?因为需要占更多的空间。

如何自适应调整网络的计算量呢,一种方式是调整网络的动态深度,举例来说,我们让每个layer之间都接一个输出层,并决定不同算力应该从哪个output 层输出出来。这样的网络训练过程也比较简单,一种很直接的方式就是把所有输出层的loss统统加起来,让其最小。这种方式效果到底如何,可以参考下面的文献(MSDNet)中给出的结果。

另外一种方式是调整网络的动态宽度,也就是对某一个网络,根据不同算力动态决定其宽度,训练方法与上述动态深度方法类似,所有输出的loss都加起来,让其最小。

上述两种方法都是人为决定根据设备不同的算力(比如电量)动态调整网络深度和宽度,但实际上来说,对于不同难度的训练样本可能需要的层数也不一样。具体来说,对于简单的样本可能几层就可以得到正确的结果,但是对于复杂的样本可能需要多训练几层才能得到正确的结果,关于通过样本自适应调整网络结构的研究可以参考一下下面的一些文献。

最后总结一下,关于神经网络压缩(Network Compression)的这几种方法,它们并不是互斥的,可以先用某一个方法,再接着用剩余的一个或几个方法,直到满足压缩条件。

猜你喜欢

转载自blog.csdn.net/SmartLab307/article/details/124484326