pytorch中的模型剪枝

简介: pytorch中的模型剪枝

剪枝是一种常用的模型压缩策略。通过将模型中不重要的连接失效,实现模型瘦身的效果,并减少计算量。PyTorch中实现的剪枝方式有三种:

  • 局部剪枝
  • 全局剪枝
  • 自定义剪枝

Pytorch中与剪枝有关的接口封装在torch.nn.utils.prune中。下面开始演示三种剪枝在LeNet网络中的应用效果,首先给出LeNet网络结构。

import torch
from torch import nn
class LeNet(nn.Module):
    def __init__(self):
        super(LeNet, self).__init__()
        # 1: 图像的输入通道(1是黑白图像), 6: 输出通道, 3x3: 卷积核的尺寸
        self.conv1 = nn.Conv2d(1, 6, 3)
        self.conv2 = nn.Conv2d(6, 16, 3)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)  # 5x5 是经历卷积操作后的图片尺寸
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = nn.Linear(84, 10)
    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, int(x.nelement() / x.shape[0]))
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x
局部剪枝

局部剪枝实验,假定对模型的第一个卷积层中的权重进行剪枝

model_1 = LeNet()
module = model_1.conv1
# 剪枝前
print(list(module.named_parameters()))
print(list(module.named_buffers()))
prune.random_unstructured(module, name="weight", amount=0.3)
# 剪枝后
print(list(module.named_parameters()))
print(list(module.named_buffers()))

运行结果

## 剪枝前
[('weight', Parameter containing:
tensor([[[[ 0.1729, -0.0109, -0.1399],
          [ 0.1019,  0.1883,  0.0054],
          [-0.0790, -0.1790, -0.0792]]],
    ...
        [[[ 0.2465,  0.2114,  0.3208],
          [-0.2067, -0.2097, -0.0431],
          [ 0.3005, -0.2022,  0.1341]]]], requires_grad=True)), ('bias', Parameter containing:
tensor([-0.1437,  0.0605,  0.1427, -0.3111, -0.2476,  0.1901],
       requires_grad=True))]
[]
## 剪枝后
[('bias', Parameter containing:
tensor([-0.1437,  0.0605,  0.1427, -0.3111, -0.2476,  0.1901],
       requires_grad=True)), ('weight_orig', Parameter containing:
tensor([[[[ 0.1729, -0.0109, -0.1399],
          [ 0.1019,  0.1883,  0.0054],
          [-0.0790, -0.1790, -0.0792]]],
    ...
        [[[ 0.2465,  0.2114,  0.3208],
          [-0.2067, -0.2097, -0.0431],
          [ 0.3005, -0.2022,  0.1341]]]], requires_grad=True))]
[('weight_mask', tensor([[[[1., 1., 1.],
          [1., 1., 1.],
          [1., 1., 1.]]],
        [[[0., 1., 0.],
          [0., 1., 1.],
          [1., 0., 1.]]],
        [[[0., 1., 1.],
          [1., 0., 1.],
          [1., 0., 1.]]],
        [[[1., 1., 1.],
          [1., 0., 1.],
          [0., 1., 0.]]],
        [[[0., 0., 1.],
          [0., 1., 1.],
          [1., 1., 1.]]],
        [[[0., 1., 1.],
          [0., 1., 0.],
          [1., 1., 1.]]]]))]

模型经历剪枝操作后, 原始的权重矩阵weight参数不见了,变成了weight_orig。 并且剪枝前打印为空列表的module.named_buffers(),此时拥有了一个weight_mask参数。经过剪枝操作后的模型,原始的参数存放在了weight_orig中,对应的剪枝矩阵存放在weight_mask中, 而将weight_mask视作掩码张量,再和weight_orig相乘的结果就存放在了weight中。

全局剪枝

局部剪枝只能以部分网络模块为单位进行剪枝,更广泛的剪枝策略是采用全局剪枝(global pruning),比如在整体网络的视角下剪枝掉20%的权重参数,而不是在每一层上都剪枝掉20%的权重参数。采用全局剪枝后,不同的层被剪掉的百分比不同。

model_2 = LeNet().to(device=device)
# 首先打印初始化模型的状态字典
print(model_2.state_dict().keys())
# 构建参数集合, 决定哪些层, 哪些参数集合参与剪枝
parameters_to_prune = (
            (model_2.conv1, 'weight'),
            (model_2.conv2, 'weight'),
            (model_2.fc1, 'weight'),
            (model_2.fc2, 'weight'),
            (model_2.fc3, 'weight'))
# 调用prune中的全局剪枝函数global_unstructured执行剪枝操作, 此处针对整体模型中的20%参数量进行剪枝
prune.global_unstructured(parameters_to_prune, pruning_method=prune.L1Unstructured, amount=0.2)
# 最后打印剪枝后的模型的状态字典
print(model_2.state_dict().keys())

输出结果

odict_keys(['conv1.bias', 'conv1.weight_orig', 'conv1.weight_mask', 'conv2.bias', 'conv2.weight_orig', 'conv2.weight_mask', 'fc1.bias', 'fc1.weight_orig', 'fc1.weight_mask', 'fc2.bias', 'fc2.weight_orig', 'fc2.weight_mask', 'fc3.bias', 'fc3.weight_orig', 'fc3.weight_mask'])

当采用全局剪枝策略的时候(假定20%比例参数参与剪枝),仅保证模型总体参数量的20%被剪枝掉,具体到每一层的情况则由模型的具体参数分布情况来定。

自定义剪枝

自定义剪枝可以自定义一个子类,用来实现具体的剪枝逻辑,比如对权重矩阵进行间隔性的剪枝

class my_pruning_method(prune.BasePruningMethod):
    PRUNING_TYPE = "unstructured"
    def compute_mask(self, t, default_mask):
        mask = default_mask.clone()
        mask.view(-1)[::2] = 0
        return mask
def my_unstructured_pruning(module, name):
    my_pruning_method.apply(module, name)
    return module
model_3 = LeNet()
print(model_3)

在剪枝前查看网络结构

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)
)

采用自定义剪枝的方式对局部模块fc3进行剪枝

my_unstructured_pruning(model.fc3, name="bias")
print(model.fc3.bias_mask)

输出结果

tensor([0., 1., 0., 1., 0., 1., 0., 1., 0., 1.])

最后的剪枝效果与实现的逻辑一致。

参考文档

深度学习之模型压缩(剪枝、量化)

相关文章
|
2月前
|
机器学习/深度学习 数据采集 人工智能
PyTorch学习实战:AI从数学基础到模型优化全流程精解
本文系统讲解人工智能、机器学习与深度学习的层级关系,涵盖PyTorch环境配置、张量操作、数据预处理、神经网络基础及模型训练全流程,结合数学原理与代码实践,深入浅出地介绍激活函数、反向传播等核心概念,助力快速入门深度学习。
137 1
|
6月前
|
机器学习/深度学习 PyTorch API
PyTorch量化感知训练技术:模型压缩与高精度边缘部署实践
本文深入探讨神经网络模型量化技术,重点讲解训练后量化(PTQ)与量化感知训练(QAT)两种主流方法。PTQ通过校准数据集确定量化参数,快速实现模型压缩,但精度损失较大;QAT在训练中引入伪量化操作,使模型适应低精度环境,显著提升量化后性能。文章结合PyTorch实现细节,介绍Eager模式、FX图模式及PyTorch 2导出量化等工具,并分享大语言模型Int4/Int8混合精度实践。最后总结量化最佳策略,包括逐通道量化、混合精度设置及目标硬件适配,助力高效部署深度学习模型。
857 21
PyTorch量化感知训练技术:模型压缩与高精度边缘部署实践
|
1月前
|
边缘计算 人工智能 PyTorch
130_知识蒸馏技术:温度参数与损失函数设计 - 教师-学生模型的优化策略与PyTorch实现
随着大型语言模型(LLM)的规模不断增长,部署这些模型面临着巨大的计算和资源挑战。以DeepSeek-R1为例,其671B参数的规模即使经过INT4量化后,仍需要至少6张高端GPU才能运行,这对于大多数中小型企业和研究机构来说成本过高。知识蒸馏作为一种有效的模型压缩技术,通过将大型教师模型的知识迁移到小型学生模型中,在显著降低模型复杂度的同时保留核心性能,成为解决这一问题的关键技术之一。
|
2月前
|
机器学习/深度学习 存储 PyTorch
Neural ODE原理与PyTorch实现:深度学习模型的自适应深度调节
Neural ODE将神经网络与微分方程结合,用连续思维建模数据演化,突破传统离散层的限制,实现自适应深度与高效连续学习。
97 3
Neural ODE原理与PyTorch实现:深度学习模型的自适应深度调节
|
8月前
|
机器学习/深度学习 JavaScript PyTorch
9个主流GAN损失函数的数学原理和Pytorch代码实现:从经典模型到现代变体
生成对抗网络(GAN)的训练效果高度依赖于损失函数的选择。本文介绍了经典GAN损失函数理论,并用PyTorch实现多种变体,包括原始GAN、LS-GAN、WGAN及WGAN-GP等。通过分析其原理与优劣,如LS-GAN提升训练稳定性、WGAN-GP改善图像质量,展示了不同场景下损失函数的设计思路。代码实现覆盖生成器与判别器的核心逻辑,为实际应用提供了重要参考。未来可探索组合优化与自适应设计以提升性能。
611 7
9个主流GAN损失函数的数学原理和Pytorch代码实现:从经典模型到现代变体
|
3月前
|
PyTorch 算法框架/工具 异构计算
PyTorch 2.0性能优化实战:4种常见代码错误严重拖慢模型
我们将深入探讨图中断(graph breaks)和多图问题对性能的负面影响,并分析PyTorch模型开发中应当避免的常见错误模式。
199 9
|
5月前
|
机器学习/深度学习 存储 PyTorch
PyTorch + MLFlow 实战:从零构建可追踪的深度学习模型训练系统
本文通过使用 Kaggle 数据集训练情感分析模型的实例,详细演示了如何将 PyTorch 与 MLFlow 进行深度集成,实现完整的实验跟踪、模型记录和结果可复现性管理。文章将系统性地介绍训练代码的核心组件,展示指标和工件的记录方法,并提供 MLFlow UI 的详细界面截图。
217 2
PyTorch + MLFlow 实战:从零构建可追踪的深度学习模型训练系统
|
4月前
|
机器学习/深度学习 数据可视化 PyTorch
Flow Matching生成模型:从理论基础到Pytorch代码实现
本文将系统阐述Flow Matching的完整实现过程,包括数学理论推导、模型架构设计、训练流程构建以及速度场学习等关键组件。通过本文的学习,读者将掌握Flow Matching的核心原理,获得一个完整的PyTorch实现,并对生成模型在噪声调度和分数函数之外的发展方向有更深入的理解。
1562 0
Flow Matching生成模型:从理论基础到Pytorch代码实现
|
5月前
|
机器学习/深度学习 PyTorch 算法框架/工具
提升模型泛化能力:PyTorch的L1、L2、ElasticNet正则化技术深度解析与代码实现
本文将深入探讨L1、L2和ElasticNet正则化技术,重点关注其在PyTorch框架中的具体实现。关于这些技术的理论基础,建议读者参考相关理论文献以获得更深入的理解。
151 4
提升模型泛化能力:PyTorch的L1、L2、ElasticNet正则化技术深度解析与代码实现
|
6月前
|
机器学习/深度学习 PyTorch 编译器
深入解析torch.compile:提升PyTorch模型性能、高效解决常见问题
PyTorch 2.0推出的`torch.compile`功能为深度学习模型带来了显著的性能优化能力。本文从实用角度出发,详细介绍了`torch.compile`的核心技巧与应用场景,涵盖模型复杂度评估、可编译组件分析、系统化调试策略及性能优化高级技巧等内容。通过解决图断裂、重编译频繁等问题,并结合分布式训练和NCCL通信优化,开发者可以有效提升日常开发效率与模型性能。文章为PyTorch用户提供了全面的指导,助力充分挖掘`torch.compile`的潜力。
643 17

热门文章

最新文章

推荐镜像

更多