深度学习模型压缩(剪枝、量化、哈夫曼编码)

本文选自即将出版的《白话人工智能与大数据》。

第一种我们来介绍剪枝法,英文叫做Pruning。

剪枝的思路在工程上是一种非常常见的思路,比如前面我们介绍决策树训练的时候就有提到过用剪枝来防止过拟合,在这里我们同样采用这样一种思路,来删除收益过低的一些计算成本。

那么什么样的计算成本是收益过低的呢?那就是计算和不计算区别不大。什么样的计算会有这种感觉呢,当然就是用一个权值为0的去和输入值相乘。注意,虽然输入如果是0,我们用任何去乘也是一种可做可不做的计算,但是输入值你是没办法控制的,我们只能对一个训练好的模型中的数以万计的进行分析。

还记得我们原来在第16章关于深度学习训练注意事项的那一部分提过有关加入L1和L2两种正则化惩罚项后对于权值的约束。当时我们只是说两种方法有区别,但是没说这种用法还有什么影响,等于卖了一个关子。在这里我们就做个呼应,不能光挖坑不填坑。如果你用的是L1惩罚项的话,权值会大量被约束在0附近,或者理解为几乎等于0。

看图就觉得会稍微清晰一些,如果你用L2对训练进行约束,权值会向原点集中;用L1对训练进行约束,有大量的权值会集中在0附近。

当这种训练结束以后,我们就可以做Pruning了,把那些几乎为0的权值去掉。当然以什么值为界取决于你对精度的要求。比如小于0.0001的都砍掉,和小于0.000001的都砍掉,两种不同的策略一定是删掉了不同数量的权值,前者砍掉的更多,也就意味着压缩率更高,但是也同时意味着信息丢失越多。所以当你砍掉了这些值,保存后,在你还原的时候还要把这些“空洞”的位置补充上0才能进行完整的矩阵运算。此时要格外注意精度的损失,也就是用你剪完枝的这个网络对数据集进行一次精度测试,再跟你原来未剪枝前进行对比,看看这种损失是不是在可接受的范围之内,进而判断这个精度和模型尺寸的Tradeoff是不是值得你做。

不过请注意,存储的时候由于砍掉了一些接近0的权值,能够节省一部分空间。但是如果你是用Tensorflow或者类似的框架来做的话,在模型还原到内存之后,仍然需要补0才能还原成完整的卷积层或者全连接层才能进行矩阵计算。因此这种方法虽然能够减小存储空间,但是不能加快计算速度。

第二种方法是量化。

量化与权值共享是一种也是一种非常典型的压缩方式。从原理上来说,仍然是一种把矩阵稀疏压缩的思路。这种思路肯定是可行的,因为到目前为止的所有压缩算法不论是有损的还是无损的都在采用类似的思路,这都是信息论这颗大树上结出的果实。

为了把基于这种思路的压缩过程讲解清楚,我们结合一篇著名的论文来把它剖析一下。这就是2015年,斯坦福大学的SongHan等人写的一篇名为《DeepCompression: Compressing Deep Neural Networks with Pruning, TrainedQuantization and Huffman Coding》的论文,我们一起来看。

这篇论文其实还是很饱满的,它结合运用了剪枝(Pruning)、量化共享权值(Quantization)以及哈夫曼编码的多重压缩手段。可以把一个240MB的AlexNet压缩到只有6.9MB大小,压缩率为35倍;也能够把一个552MB的VGG-16网络压缩到仅有11.3MB大。更厉害的是,这种压缩是没有精度损失的。

这个压缩过程是一个完整的分步骤的流程,先进行剪枝,这会带来9到13倍的尺寸压缩;再进行量化和权值共享,这会累计带来27到31倍的尺寸压缩;最后进行哈夫曼编码,到这一步累计达到35到49倍的尺寸压缩。

(1) 网络剪枝

在这篇论文中同样提到了重要的剪枝方式,就是把那些足够小(绝对值低于某些阈值)的权值从网络中移除。注意,这一定是针对一个已经对于某个特定任务训练好的深度学习网络而言的。这个步骤可以把AlexNet缩小到原来的

,把VGG-19缩小到原来的

。可见,在一个训练好的深度学习网络中有大量的权值在0附近。

一旦做了这种剪枝以后,整个网络的结构就有了很大的变化。原来很小但是不是0的位置都变成了0,既然如此肯定是没有必要原原本本地去记录那么多0的,只需要记录那些留下来的比较大的w值是在哪里,具体多少就够了。那么原来本身是用数组的方式来表示的完整的一个一个32bit的数据就可以用类似“链表”的方式去标记了。如图所示:

Idx就是指原先压缩前的权值序号(Index),如果是AlexNet的话应该是从1标记到240,000,000,本来就应该是用这种顺序的方法来表示的,现在稀疏化后不用了。例如下面的3.4、0.9、1.7这些是在剪枝中“幸存”下来的这些权值,那么它们怎么表示呢?用偏移量Diff来表示,3.4这个原来的序号是1,那么Diff也就是1;0.9原来的序号是4,但是相对于前面一个有效值来说偏移了3,所以它的Diff值就是3;注意看中间这个Filler Zero的部分,相对前一个有效值0.9来说偏移量是8,所以故意在这里加一个偏移量为8(正好3bit)的值来占位,这样后面的Idx为15的1.7的值,就可以只参照FillerZero的位置,把Diff设置成3了。

这样一来一个稀疏的数组可以只用“Diff:Value”的键值对来保存了。Diff我们刚才用了Filler Zero的技巧,可以强行限制了3bit的大小,Value就直接保留就可以了。而且0这种值保存的时候是完全不需要用32bit的,因此这个部分的压缩是可以压缩到

以下的。

这就是剪枝的内容概要。

(2)量化与权值共享

使用量化和共享权值的功能可以进一步压缩这个已剪枝网络的。量化和共享权值通常是一起来讨论的。

量化的意思就是把这些连续的权值进一步稀疏化、离散化。离散化之后原来连续的稠密的值就可以用离散的值来表示了,比如4.01和3.99都用4.0来表示,然后偏移量的部分用0.01来表示。如果这种方式成行,那么肯定是可以减小模型尺寸的,为什么呢?我们这么想一下看:

例如,现在有256个值,是从0到255的整数,那么可以看出这一组数字从统计上来看熵是非常大的,因为分布非常均匀。你是很难对这样的数字表示进行压缩的,要想表示出它们当中的每一个,你必须用8bit的数据来表示。可是,如果这些数字集中在某些数字周围呢?比如256个值里面有56个是8,100个是7,100个9,情况会有什么不同吗?

从直观感觉上了看,熵肯定是要小很多的,因为确定性高了很多。那么我们如果用3bit来表示它的中心位置8,再用2bit表示偏移量——1表示+1,0表示无偏移,-1表示-1。那么数据的存储空间又有很大的节省。原来的256个值,每个是8bit,那么一共需要2048个字节才能把数据全都记下来。而用了新方法后,每个值都可以表示为3bit的中心点和2bit的偏移量的大小,那么就变成了5bit来表示一个数字,一共需要1280的字节就够了。

把所有的权值尝试着聚拢到一起,就是尝试找到多个簇,并找的各簇的中心点,在这个图上示意是找到了4个不同的中心点,然后用2bit的信息来表示中心点的编号。然后每个中心点的具体位置具体在列表中标出来(centroids),就是2.00,1.50,0.00和-1.00这几个值。这样记录中心点的矩阵就会小很多,这个过程就叫量化。

然后在最后一轮训练中,把各个簇中的点的梯度加和,乘以学习率后从各簇中心点中减去。这个过程叫fine-tune(通常这里不翻译成调优)。

在这个例子中一个4*4的16个权值的矩阵被压缩到只需要一个4*1矩阵表示其中心值,再加一个4*4的矩阵,但是每个元素都表示簇编号的矩阵就可以表示了。簇的数量还是要比原来稀疏的连续实数少得多。

想要找到聚类的中心并不难,在无监督学习的章节我们已经学过k-means这种算法,现在就可以派上用场了。现在的任务就是把整个网络剩下的这些权重w的集合(假设是n个权重),分成k个簇,当然n是远远大于k的。这个我们也是有办法的,从表达式上看也就是优化这个表达式(其中ci表示每个簇的中心点):

关于共享权值,文章对比了3种方法。

一种叫Forgy,就是随机从众多的权重中挑选k个作为初始化的点,也就是图中黄色点的位置;

一种叫Density-based,也就是基于密度的方法,图中蓝色点的位置,可以看到权值集中的位置采样就多一些;

还有一种叫Linear,也就是线性方法,图中红色点的位置,是非常非常均匀的覆盖了所有的曲直空间。

从分布来看,0附近权值的都已经被消掉了,目测大部分都集中在-0.15附近和+0.15附近的感觉。

关于如何调整这些中心点的位置,论文里给了一个公式,也很好理解:

白话解释就是,现在整个网络产生的误差就是由每个簇中心的Ck点导致的,那么现在的就是要挪每个Ck,让它向着减少网络误差的方向移动。

一个完整的VGG-16网络在训练后有误差,还有大量的约等于0的值,这说明网络还是“虚胖”。现在砍掉了它们,精度可能会略有下降,所以要进行这种微调来把权值调整到合适的位置尽可能保证误差不要降低太多。但是精度升高,或者说误差比原来做量化和权值共享之前的精度还高这应该没有可能。

(3) 哈夫曼编码

最后在保存网络的时候还是要使用哈夫曼编码进行进一步的压缩。

哈夫曼编码的原理相信读者朋友在学习《数据结构》的时候已经有了比较深的理解,数据重复越多,熵越低,哈夫曼编码就越能用短的码值来表示更多的数字,编码的效率就越高,压缩的效果就越好。在这个环节中,哈夫曼编码做的是同样的事情。

从图上来看,权值的分布还是很集中的,这非常好。采用哈夫曼编码的效果后,比起用了量化与共享权值又能进一步减少20%到30%的存储空间。

原文发布于微信公众号 - 智能工场AIWorkshop(qddata)

原文发表时间:2019-06-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券