FPGM-Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration学习笔记

个人看法

关于作者提出的norm-based的方法的依赖条件,我认为通过简单的修改就能解决,并且在自己的yolov3的项目中也成功应用,剪枝率大于90%。但是在Paddle Slim中看到百度似乎用了这个方法做模型剪枝,就学习一下。

1、范数的标准差足够大,才能有足够的搜索空间,容易找到划分的阈值?
其实也可以按照比例裁剪,不需要固定的阈值。

2、范数的最小值接近于0?
这个其实是稀疏化效果的好坏,需要进行多次参数调节。

3、FPGM无法裁剪掉完整的层
模型剪枝之后,通道数减少,计算量下降。如果跑在GPU上,随之而来的问题是模型会有计算力瓶颈转为访存瓶颈,必须多batch size才能”屏蔽“访存时间,但是在实际生产环境中往往batch size 为1。但基于norm-based的方法可以剪掉整个层,降低网络的宽度,速度收益更明显。自己的实际实验也验证了这一点。

4、FPGM比较通用
无需依赖任何东西,只需要有filter就行。

5、FPGM的剪枝质量
FPGM假设每一层都分配到一个欧几里得空间,按照固定的剪枝率对每一层进行剪枝,但是每一层的重要性是不一样的,所以这个剪枝率也应该是不一样的。如何得到每一层合适的剪枝率,也是一个需要解决的问题。

作者的知乎解答:https://zhuanlan.zhihu.com/p/67175001

源码:https://github.com/he-y/filter-pruning-geometric-median

一、摘要

之前的通道剪枝都是利用“smaller-norm-less-important”原则:通过一个范数去衡量一个filter的重要性。作者指出了这种方法并不是一直有效的,依赖于两个条件:

  • 1、范数的标准差足够大(足够的搜索空间,容易找到划分的阈值)
  • 2、范数的最小值接近于0(相对贡献小)
    针对以上两点不足,作者提出了FPGM(Filter Pruning via Geometric Median),通过几何中位数进行模型压缩,从相对小的贡献度的思路转换到贡献最可被替代。

二、简介

网络剪枝是网络的压缩和加速中一个重要的方向,自1989年LeCun提出以来,得到了迅速发展。现在主要分为2种方向:1)权重剪枝;2)滤波器剪枝。滤波器剪枝相对于权重剪枝有一系列优点,包括它可以得到规则的模型,由此减少内存消耗,并且加速网络的推断。
对于典型的滤波器剪枝的流程,现有的方法认为滤波器的范数(p-norm)越小,相对应的特征图越接近于0,于是对网络对贡献越小,那么这些滤波器可以去掉而不会严重影响网络的性能。于是我们用滤波器的p-norm进行重要性排序,并且删除p-norm小的滤波器。在自己的yolov3项目中也使用了基于L1范数进行剪枝,剪枝率可以达到90%以上。

image.png
  • 1、对于以上的图(a),绿色越深表示相应的范数值越大,之前的方法假设范数值低的filter重要度低,可以移除。而FPGM则是使用了几何中位数,根据网络的冗余度进行剪枝。
  • 2、对于以上的图(b),蓝色的曲线是理想的范数值分布,最大值是v1,最小值是v2,有一个比较大的标准差,以便有一个足够大的搜索空间,然后选择一个合适的值,对小于该值的filter剪枝。

针对以上的问题,提出了FPGM,从相对小的贡献度的思路转换到贡献最可被替代。通过对同一层中不同的filter计算Geometric Media(几何中位数),越靠近几何中心越容易被其他的filter表示。

三、相关工作

作者将之前的模型压缩工作分为了:

  • 1、Weight Pruning:裁剪filter的weight
  • 2、Data Dependent Filter Pruning:依赖于训练数据
  • 3、Data Independent Filter Pruning:不依赖于训练数据
    作者和《Rethinking the smaller-norm-less-informative assumption in channel pruning of convolution layers》这篇论文进行对比,主要差异如下:
  • 1、通过batch normalization中的参数加强网络的稀疏性,这对于没有BN的网络不适用
  • 2、剪枝之后需要fine-tuning
  • 3、训练的时候需要计算额外的梯度,引入了额外的计算量(其实这部分很牵强,增加的计算量九牛一毛)

四、具体方法

1、分析Norm-based的方法

作者再一次针对这个问题进行了分析,这里不再赘述。


image.png

2、关于Norm-based的实验

证明了在实际场景中并不是都能满足上述依赖的两个条件。

  • 1、范数的标准差足够大。图(a)和图(c)对比不满足。图(e)和图(g)。
  • 2、最小值接近0


    image.png

3、通过几何中位数进行剪枝

通过几何中位数进行剪枝,可以很好的解决Norm-based方法的局限性。对于一系列点:,


看懂了就很简单,几何中位数是对于欧几里得空间的点的中心的一个估计。其实就是到所有点欧式距离最小的点。越靠近球体中心的点越容易被球面上的点表示。作何认为filter也是欧氏空间中的点,于是可以根据计算GM来得到这些filter的“中心”,也就是他们的共同性质。如果某个filter接近于这个GM,可以认为这个filter的信息跟其他滤波器重合,甚至是冗余的,于是我们可以去掉这个filter而不对网络产生大的影响。去掉它后,它的功能可以被其他filter代替。
伪代码:

image.png

作者的源码实现:

def get_filter_similar(self, weight_torch, compress_rate, distance_rate, length):
    codebook = np.ones(length)
    if len(weight_torch.size()) == 4:
        filter_pruned_num = int(weight_torch.size()[0] * (1 - compress_rate))
        similar_pruned_num = int(weight_torch.size()[0] * distance_rate)
        weight_vec = weight_torch.view(weight_torch.size()[0], -1)
        # norm1 = torch.norm(weight_vec, 1, 1)
        # norm1_np = norm1.cpu().numpy()
        norm2 = torch.norm(weight_vec, 2, 1)
        norm2_np = norm2.cpu().numpy()
        filter_small_index = []
        filter_large_index = []
        filter_large_index = norm2_np.argsort()[filter_pruned_num:]
        filter_small_index = norm2_np.argsort()[:filter_pruned_num]

        # distance using numpy function
        indices = torch.LongTensor(filter_large_index).cuda()
        weight_vec_after_norm = torch.index_select(weight_vec, 0, indices).cpu().numpy()
        # for euclidean distance
        similar_matrix = distance.cdist(weight_vec_after_norm, weight_vec_after_norm, 'euclidean')
        # for cos similarity
        # similar_matrix = 1 - distance.cdist(weight_vec_after_norm, weight_vec_after_norm, 'cosine')
        similar_sum = np.sum(np.abs(similar_matrix), axis=0)

        # for distance similar: get the filter index with largest similarity == small distance
        similar_large_index = similar_sum.argsort()[similar_pruned_num:]
        similar_small_index = similar_sum.argsort()[:  similar_pruned_num]
        similar_index_for_filter = [filter_large_index[i] for i in similar_small_index]

        kernel_length = weight_torch.size()[1] * weight_torch.size()[2] * weight_torch.size()[3]
        for x in range(0, len(similar_index_for_filter)):
            codebook[
                similar_index_for_filter[x] * kernel_length: (similar_index_for_filter[x] + 1) * kernel_length] = 0
            print("similar index done")
            else:
                pass
            return codebook

4、理论加速和实际加速

这部分工作其实只具有一定的参考性吧,因为实际加速和算子的具体实现有很大关系, 不同平台、不同CUDA、CUDNN、不同的深度学习框架的实际速度都不同。


image.png

五、实验结果

1、不同方法的剪枝实验

作者在CIFAR-10和ILSVRC-2012上做了关于VGG和ResNet的实验,这里我值列举ResNet在ILSVRC-2012上的结果。ResNet的shortcut层不参与剪枝。


image.png

2、消融实验

FPGM对于剪枝的训练间隔不敏感

image.png

不同的剪枝率

在18%和40%时甚至超过了未剪枝的模型,所以FPGM有一定的正则化效果


image.png

不同的距离函数

作者验证了L1和余弦距离作为距离函数的结果。

  • 欧式距离:93.73 ± 0.23 %
  • L1距离:93.87 ± 0.22 %
  • 余弦距离: 93.56 ± 0.13

FPGM和 Norm-based结合

在CIFAR-10上可以有略微的提升,但是在ILSVRC-2012没有。作者认为是在ILSVRC-2012没有满足上述提到的Norm-based的两个依赖条件。

3、特征图可视化

红色的是被剪枝掉的通道特征,分别为:(7,23,27,46,56,58)。可以被剩下的(5,12,16,18,22, et al.)所替代。(0,4,33,34,47, et al.) 包含了大熊猫的轮廓。


image.png

你可能感兴趣的:(FPGM-Filter Pruning via Geometric Median for Deep Convolutional Neural Networks Acceleration学习笔记)