Pruning 例子

LeNet( (conv1): Conv2d(1, 6, kernel_size=(3, 3), stride=(1, 1)) (conv2): Conv2d(6, 16, kernel_size=(3, 3), stride=(1, 1)) (fc1): Linear(in_features=400, out_features=120, bias=True) (fc2): Linear(in_features=120, out_features=84, bias=True) (fc3): Linear(in_features=84, out_features=10, bias=True) ) 在pytorch里面, prune通过对权重进行掩码来完成. 这个如何理解? 首先, 我们打印一下原始的conv1的权重看看: module = model.conv1 print(list(module.named_modules())) print(list(module.named_buffers())) print(list(module.named_parameters())) 这里列举了后面可能会用到三个方法, 这个可以查看当前的module到底是一个啥情况。 着重观察 named_parameters 因为参数都保存在这里, 打印完了之后可以看到: [('weight', Parameter containing: tensor([[[[-0.2312, 0.2133, -0.1313], [-0.2980, -0.1838, -0.2902], [-0.3006, 0.1338, -0.0980]]], [[[-0.1239, 0.1060, 0.3271], [-0.0301, -0.0245, 0.0493], [-0.0160, 0.0397, -0.1242]]]], device='cuda:0', requires_grad=True)), ('bias', Parameter containing: tensor([-0.2593, -0.0520, 0.0303, 0.0382, -0.0468, -0.1053], device='cuda:0', requires_grad=True))] 它有一个weight和一个bias, 这没错, 合乎常理. 我们甚至可以看看weights的尺寸是多少. ...

2025-02-11 · 1 min · CodeMist

Pruning 后的工作

如何将这些模型保存下来. 换句话说, 你剪枝完了之后, 你得到了一个mask的掩码, 不管是结构化还是非结构化, 这个新的模型你要怎么样才能导出呢? 试想一下, 如果只是得到这么一个掩码, 你在推理的时候, 只是这一部分权重为零而已, 你推理的长宽高啥的都么有改变, so, 速度会变得更快吗? 这位老哥发现, 他尝试不管是结构化剪枝还是非结构化剪枝, 速度和模型的大小都没有变化. 然后另外一个老哥就告诉他真像了: 重要的是要了解非结构化修剪和结构化修剪之间的区别。 结构化修剪:通过删除张量的整行/列来减小重量张量的尺寸。这转化为去除神经元及其所有传入和传出连接(在密集层中)或整个卷积过滤器(在卷积层中)。 非结构化修剪:单个权重可以“去除”(归零),而不受最终张量形状的限制。这意味着删除神经元之间的单个连接(在密集层中)或删除卷积过滤器的单个权重(在卷积层中)。 请注意,生成的重量张量可以稀疏,但保持其原始形状。目前,torch.nn.utils.prune仅支持非结构化修剪,这几乎无法帮助降低推理成本,因为==GPU并未针对稀疏矩阵乘法进行优化==。虽然您可能希望减小权重张量的尺寸以减少浮点运算的数量,但非结构化修剪会产生带有许多零的权重张量,但不会自动减小此类张量的大小。仅当去除许多负担时,非结构化修剪才能帮助提高性能。在这种情况下,您可以依靠PyTorch稀疏操作,也可以尝试查找包含全零的行/列,因此可以将其删除。相反,如果要研究结构化修剪,可以看一下TorchPruner,这是我为研究目的而自行开发的一个库,它提供了实用程序来查找最不重要的神经元并相应地对重量张量进行切片。 所以, 这个部分我们也应该知道, 事实上pytorch的prune里面做的事情, 只是找到哪些层是可以置零的而已, 请注意, 这里的层指的不是某个layer, 而是一个卷积层矩阵里面的某一整行或者某一整列. 事实上pytorch的prune里面做的事情, 只是找到哪些层是可以置零的而已, 请注意, 这里的层指的不是某个layer, 而是一个卷积层矩阵里面的某一整行或者某一整列. 那么问题来了, 听起来我们要保存模型, 要得到速度加快, 模型变小的模型, 还需要再每一个prune的层里面找到某一被全置零的链接, 然后再拿来做推理. 可是这个怎么听起来这么复杂呢? 事实上在实际应用中应该怎么做呢? 这就引入了下一篇教程: 使用Distiller的工业应用模型剪枝教程.

2025-02-11 · 1 min · CodeMist

模型剪枝 Pruning

为什么需要剪枝? 过参数化主要是指在训练阶段,在数学上需要进行大量的微分求解,去捕抓数据中的微小变化信息,一旦完成迭代式的训练之后,网络模型推理的时候就不需要这么多参数。而剪枝算法正是基于过参数化的理论基础而提出的。 核心思想:减少网络模型中参数量和计算量,同时尽量保证模型的性能不受影响。 剪枝算法分类 1)Drop Out:随机的将一些神经元的输出置零,称之为神经元剪枝。【这个一眼看到就感觉会有问题】 2)Drop Connect:随机将部分神经元间的连接Connect置零,使得权重连接矩阵变得稀疏。【这个还可以】 结构化剪枝 非结构化剪枝 1) 细粒度剪枝(fine-grained):即对连接或者神经元进行剪枝,是粒度最小的剪枝,上面Drop Out和Drop Connect都是属于细粒度剪枝。 2) 向量剪枝(vector-level):它相对于细粒度剪枝粒度稍大,属于对卷积核内部(intra-kernel) 的剪枝。 3) 核剪枝(kernel-level):即去除某个卷积核,丢弃对输入通道中对应卷积核的计算。 4) 滤波器剪枝(Filter-level):对整个卷积核组进行剪枝,==推理过程中输出特征通道数会改变==。 细粒度剪枝、向量剪枝、核剪枝在参数量与模型性能之间取得了一定的平衡,但是网络模型单层的神经元之间的组合结构发生了变化,需要专门的算法或者硬件结构来支持稀疏的运算,这种叫做非结构化剪枝(Unstructured Pruning)。 其中,非结构化剪枝能够实现更高的压缩率,同时保持较高的模型性能,然而会带来网络模型稀疏化,其稀疏结构对于硬件加速计算并不友好,除非底层硬件和计算加速库对稀疏计算有比较好的支持,否则剪枝后很难获得实质的性能提升。 滤波器剪枝(Filter-level) 主要改变网络中的滤波器组和特征通道数目,所获得的模型不需要专门的算法和硬件就能够运行,被称为结构化剪枝(Structured Pruning)。结构化剪枝又可进一步细分:可以是channel-wise,也可以是filter-wise,还可以是在shape-wise。 结构化剪枝与非结构化剪枝恰恰相反,可以方便改变网络模型的结构特征,从而达到压缩模型的效果,例如知识蒸馏中的student网络模型、NAS搜索或者如VGG19和VGG16这种裁剪模型,也可以看做变相的结构化剪枝行为。 剪枝算法流程 虽然剪枝算法的分类看上去很多,但是核心思想还是对神经网络模型进行剪枝,目前剪枝算法的总体流程大同小异,可以归结为三种:标准剪枝、基于子模型采样的剪枝、以及基于搜索的剪枝,如下图所示。 标准剪枝算法流程 标准剪枝是目前最流行的剪枝流程,在Tensorflow、Pytroch都有标准的接口。主要包含三个部分:训练、剪枝、以及微调。 1)训练:首先是对网络模型进行训练。在剪枝流程中,训练部分主要指预训练,训练的目的是为剪枝算法获得在特定基础SOTA任务上训练好的原始模型。 2)剪枝:在这里面可以进行如细粒度剪枝、向量剪枝、核剪枝、滤波器剪枝等各种不同的剪枝算法。其中很重要的就一点,就是在剪枝之后,==对网络模型结构进行评估==。确定一个需要剪枝的层,设定一个裁剪阈值或者比例。实现上,通过修改代码加入一个与参数矩阵尺寸一致的==Mask矩阵==。Mask矩阵中只有0和1,实际上是用于微调网络。 3) 微调:微调是恢复被剪枝操作影响的模型表达能力的必要步骤。结构化模型剪枝会对原始模型结构进行调整,因此剪枝后的模型参数虽然保留了原始的模型参数,但是==由于模型结构的改变,剪枝后模型的表达能力会受到一定程度的影响==。实现上,微调网络模型,参数在计算的时候先乘以该Mask,Mask为1的参数值将继续训练通过BP调整梯度,而Mask为0的部分因为输出始终为0则不对后续部分产生影响。 4)再剪枝:再剪枝过程将微调之后的网络模型再送到剪枝模块中,再次进行模型结构评估和执行剪枝算法。目的是使得每次剪枝都在性能更优的模型上面进行,不断迭代式地进行优化剪枝模型,直到模型能够满足剪枝目标需求。 最后输出模型参数储存的时候,因为有==大量的稀疏==,所以可以重新定义储存的数据结构, ==仅储存非零值以及其矩阵位置==。重新读取模型参数的时候,就可以还原矩阵。 基于子模型采样流程 除标准剪枝之外,基于子模型采样的剪枝《EagleEye: Fast sub-net evaluation for efficient neural network pruning》最近也表现出比较好的剪枝效果。得到训练好的模型之后,进行子模型采样过程。一次子模型采样过程为: 1)对训练好的原模型中可修剪的网络结构,按照剪枝目标进行采样,采样过程可以是随机的,也可以按照网络结构的重要性或者通过KL散度计算进行概率采样。 2)对采样后的网络结构进行剪枝,得到采样子模型。子模型采样过程通常进行多次,得到多个子模型(n≥1)。这些子模型是原模型的不同简化版本,它们的网络结构和参数数量都有所减少。 基于搜索的剪枝流程 基于搜索的剪枝主要依靠强化学习等一系列无监督学习或者半监督学习算法,也可以是神经网络结构搜索相关理论。 给定剪枝目标之后,基于搜索的剪枝在网络结构中搜索较优的子结构,这个搜索过程往往伴随着网络参数的学习过程,因此一些基于搜索的剪枝算法在剪枝结束后不需要再进行微调。

2025-02-11 · 1 min · CodeMist