(转)几种模型压缩技术(Model Compression)
原文:https://zhuanlan.zhihu.com/p/519826477
前言
1.为什么要进行模型压缩?
在一些有资源限制的设备上不方便使用层数、参数太多的大模型(如手机、手表、飞行器、智能眼镜等),主要是两类限制:
存储空间限制(Limited memory space)
算力限制(Limited computing power)
2. 模型压缩的方式
网络剪枝(Network Pruning)
知识蒸馏(Knowledge Distillation)
参数量化(Parameter Quantization)
架构设计(Architecture Design)
动态计算(Dynamic Computation)
1.网络剪枝(Network Pruning)
为什么网络可以被剪枝?-- 因为网络通常是over-parameterized的,很多参数是没有太大用处的,这些参数可以被移除。
步骤:

1.首先有一个训好的大模型
2.评估权重(weight)和神经元(neuron)的重要性
一个weight重要程度:如果weight数值接近0,可能就是一个不太重要的weight;如果weight很正或者很负,可能就是一个比较重要的weight;可以计算一个weight的L1或L2的数值,来评估它是否重要。
一个neuron重要程度:给定一个数据集,如果某个neuron的输出几乎都是0,那么这可能就是一个不重要的neuron。
(还有很多其他的重要性评估方法,可以去查阅一些相关资料)
3.根据重要性进行排序,移除不重要的weight或neuron
4.剪枝后的网络可能准确率有所下降,需要把剪枝后的网络在数据集上重新Fine-tune一下
通常不会一下remove掉很多参数,否则很难通过Fine-tune recover回来,通常是先remove一小部分,fine-tune看下效果,如果效果ok,再接着remove,如此循环,直到达到满意的参数量和模型效果。
【为什么不直接训小网络呢?】
----network比较大的时候更好optimize,更容易找到最优解 (youtube链接)
【一些实际操作起来的问题:剪枝weight和剪枝neuron哪个更好呢?】
- 剪枝weight:去掉一些weight后,网络变的不规则,在同一层里面,有的neuron有2个input,有的有3个input,实际代码比较难实现,GPU也不好加速这种不规则的网络计算,因此实际操作的时候,并不是真的把这些weight去掉,而是把这些weight设成0.

- 剪枝neuron:更容易实现和加速

2.知识蒸馏(Knowledge Distillation)
思路:用一个小的student网络去学习大的teacher网络的输出。
问题:为什么student学teacher要比直接学原始样本数据效果更好呢?
----teacher教的不仅仅是正确答案是什么,也告诉了student哪些答案是比较相似的,比如图中,teacher不只是教这张图片是“1”,也教了“1”和“7”和“9”是相似的,这样即使模型没有见过7或者9,可能见到7或9的时候也能把它分辨出来。

(Tips:student模型还可以学多个模型ensemble的结果,得到更好的效果。)
为了使student模型学到更多的信息(比如“1”和“7”很相似),那么就不希望teacher模型的输出和one-hot一样(这样就和直接学原始数据没太大区别了),但是teacher模型的logits经过softmax后很可能会变的比较接近one-hot(如图左下角所示);因此需要除以参数Temperature控制一下,使得输出的prob差距不要太大(如图右下角所示)。

3.参数量化(Parameter Quantization)
1.用更少的bit来存值:比如之前用32bit存参数,改用16bit存参数,这样网络大小直接就缩减了一半
2.Weight clustering:
对参数进行聚类,同一类的只用一个id表示,再维护一个id对应参数值(参数平均值)的表

3.Huffman encoding:,常出现的clusters用更少的bit来编码
4.最极致的情况:一个weight只用+1或-1来存储
随便初始化一组实值参数(可以是真实value的参数),计算和它最接近的一组只用+1 -1存储的参数,用这组参数的梯度来更新实值参数,再计算更新后的实值参数和哪组+1-1参数最接近,计算梯度,更新实值参数,如此循环,直至想要停止的时候
4.架构设计(Architecture Design)
(1)全连接神经网络
加一个维度比较小的隐层,如图所示

原来参数shape是M*N,加了一层linear后,参数变成M*K+K*N
(2)CNN
输入:2个6*6的矩阵(2个channel)
输出:4个4*4的矩阵
参数量:3*3*2*4=72
filter是有深度的,同时对2个channel的18个单元进行卷积

变换卷积方式:
先用2个filter进行depthwise的卷积,filter没有深度,每个filter只进行自己channel的卷积,得到2个4*4的矩阵
再用有深度的的filter(1*1*2)进行pointwise的卷积,最终得到4个4*4的矩阵
参数量:3*3*2 + 1*1*2*4 = 26

5.动态计算(Dynamic Computation)
背景:手机电量比较充足时,可能希望得到最精确的计算,在电量不太够时,希望先有计算结果,再考虑计算精度。因此希望可以动态控制,在特殊情况下可以不使用那么多层网络进行推理。
思路:在训练时为中间层也加入classfier
缺陷:浅层的网络推理能力较差;并且会降低整体的预测精度,原因是本来浅层应该学简单的知识,加入classfier后会强迫网络在浅层也学到比较复杂的pattern,破坏了网络结构原本由浅入深的特性。
改进:具体可查看MSDNet的paper

未完待续...目前主要是根据李宏毅老师的课程做的一些记录,后续会继续学习相关内容,加入一些论文代码的介绍等...
【参考资料】
[1]. 李宏毅老师的深度学习课程——Network Compression部分
还了解了下TF中有个专门的tfmot库来支持压缩。