首页 > 其他分享 >05Nvidia剪枝方案介绍

05Nvidia剪枝方案介绍

时间:2023-07-06 16:13:21浏览次数:42  
标签:剪枝 name 方案 torch mask module 05Nvidia model cls

Nvidia剪枝方案介绍

目前大多数的剪枝研究处于以下两个方面

  1. 绝大多数剪枝是非结构化的,属于细粒度稀疏。而细粒度稀疏其实没有那么好的加速效果
  2. Coarse-grained sparsity的稀疏效果有限

("Coarse-grained sparsity"是一种稀疏性类型,它指的是在较大的数据块或数据结构中存在稀疏性,而不是在单个元素级别。在深度学习和神经网络中,这通常意味着在层级别或通道级别进行稀疏化,而不是在单个权重或神经元级别。

例如,对于卷积神经网络,粗粒度稀疏性可能意味着整个过滤器或通道被置零或被剪枝,而不是单个权重。这种稀疏性类型的一个优点是,它可以更容易地利用硬件加速器的并行性,因为整个数据块可以一次性地被加载、处理或跳过。

相反,"fine-grained sparsity"则是指在单个元素级别存在稀疏性,例如单个权重或神经元被置零或被剪枝。这种稀疏性类型可能更难以优化,因为它可能需要更复杂的索引和数据管理策略。

"coarse-grained sparsity"是一种在更大的数据结构级别实现稀疏性的策略,它可以更容易地与硬件优化相结合。)

面临的挑战

  1. 精度丢失
  2. 没有一个通用的剪枝方案去针对不同的网络
  3. Lack of speedup(由于剪完之后结构发生了改变,可能无法使用矩阵加速,可能无法利用内存加速,存储开销变大)

    下面是一个demo,流程是加载预训练模型 -> 测试预训练模型 -> 剪枝 ->测试剪枝后的模型 -> 再训练剪枝后的模型 -> 测试再训练后的模型 -> 保存剪枝和再训练后的模型
torch.manual_seed(42)
get_model("./model.pt")
# get_model("None")
print("-------orig---------")
test()
print(model[2].state_dict())
ASP.prune_trained_model(model, optimizer)
print("-------pruned---------")
test()
print(model[2].state_dict())
train()
print("-------retrain---------")
test()
print(model[2].state_dict())
torch.save(model, "./model_sparse.pt")

构建加载模型的函数get_model()

#如果有则直接加载模型和优化器,没有则构建一个简单的模型并train一下然后保存下来
def get_model(f):
    global model, optimizer
    if os.path.exists(f):
        model = torch.load(f).cuda()
        optimizer = optim.Adam(model.parameters(), lr=0.01)
    else:
        model = nn.Sequential(
            nn.Linear(8, 16),
            nn.PReLU(),
            nn.Linear(16, 8),
        ).cuda()
        optimizer = optim.Adam(model.parameters(), lr=0.01)
        train()
        torch.save(model, f)

ASP( Automatic Sparsity Pruning)复现,该方法是Nvidia在2020年提出并首次引入Nvidia的Ampere架构中。在这种方法中,权重的重要性是通过一种称为 "mask" 的机制来确定的。这些 mask 是在训练过程中学习的,并且在训练结束时,权重被乘以相应的 mask。这样,不重要的权重(即,对应于 mask 中的零的权重)就被剪枝掉了。

class ASP:
    model = None
    verbosity = 0
    optimizer = None
    sparse_parameters = []
    calculate_mask = None

    @classmethod
    def init_model_for_pruning(
        cls,
        model,
        mask_calculator="m4n2_1d",
        verbosity=3,
        whitelist=[torch.nn.Linear, torch.nn.Conv1d, torch.nn.Conv2d],
        custom_layer_dict={},
    ):
        assert cls.model is None, "ASP has been initialized already."
        cls.model = model
        cls.verbosity = verbosity

        if isinstance(mask_calculator, str):
            def create_mask_from_pattern(param):
                return create_mask(param, mask_calculator).bool()

            cls.calculate_mask = create_mask_from_pattern

        # function to extract variables that will be sparsified.
        # idea is that you will add one of these functions for each module type that can be sparsified.

        sparse_parameter_list = {
            torch.nn.Linear: ["weight"],
            torch.nn.Conv1d: ["weight"],
            torch.nn.Conv2d: ["weight"],
        }
        if (
            custom_layer_dict
        ):  # Update default list to include user supplied custom (layer type : parameter tensor), make sure this tensor type is something ASP knows how to prune
            sparse_parameter_list.update(custom_layer_dict)
            whitelist += list(custom_layer_dict.keys())

        for module_type in whitelist:
            assert module_type in sparse_parameter_list, (
                "Module %s :: Don't know how to sparsify module." % module.dtype()
            )

        # find all sparse modules, extract sparse parameters and decorate
        def add_sparse_attributes(module_name, module):
            sparse_parameters = sparse_parameter_list[type(module)]
            for p_name, p in module.named_parameters():
                if p_name in sparse_parameters and p.requires_grad:
                    # check for NVIDIA's TC compatibility: we check along the horizontal direction
                    if p.dtype == torch.float32 and (
                        (p.size()[0] % 8) != 0 or (p.size()[1] % 16) != 0
                    ):  # User defines FP32 and APEX internally uses FP16 math
                        print(
                            "[ASP] Auto skipping pruning %s::%s of size=%s and type=%s for sparsity"
                            % (module_name, p_name, str(p.size()), str(p.dtype))
                        )
                        continue
                    if p.dtype == torch.float16 and (
                        (p.size()[0] % 8) != 0 or (p.size()[1] % 16) != 0
                    ):  # For Conv2d dim= K x CRS; we prune along C
                        print(
                            "[ASP] Auto skipping pruning %s::%s of size=%s and type=%s for sparsity"
                            % (module_name, p_name, str(p.size()), str(p.dtype))
                        )
                        continue

                    if cls.verbosity >= 3:
                        print(
                            "[ASP] Sparsifying %s::%s of size=%s and type=%s for sparsity"
                            % (module_name, p_name, str(p.size()), str(p.dtype))
                        )

                    mask = torch.ones_like(p).bool()
                    buffname = p_name.split(".")[-1]  # buffer names cannot contain "."
                    module.register_buffer("__%s_mma_mask" % buffname, mask)
                    cls.sparse_parameters.append(
                        (module_name, module, p_name, p, mask)
                    )
                else:
                    if cls.verbosity >= 3:
                        print(
                            "[ASP] Not sparsifying %s::%s of size=%s and type=%s"
                            % (module_name, p_name, str(p.size()), str(p.dtype))
                        )

        for name, sparse_module in eligible_modules(
            model, tuple(whitelist)
        ):
            add_sparse_attributes(name, sparse_module)

    @classmethod
    def init_optimizer_for_pruning(cls, optimizer):
        assert cls.optimizer is None, "ASP has initialized optimizer already."
        assert (
            cls.calculate_mask is not None
        ), "Called ASP.init_optimizer_for_pruning before ASP.init_model_for_pruning."

        # store pointer to original optimizer step method
        cls.optimizer = optimizer
        cls.optimizer.__step = optimizer.step

        def __step(opt_self, *args, **kwargs):
            # prune gradients before step method
            with torch.no_grad():
                for (
                    module_name,
                    module,
                    p_name,
                    p,
                    mask,
                ) in cls.sparse_parameters:
                    if p.grad is not None:  # thx pjudd
                        p.grad.mul_(mask)
            # call original optimizer step method
            rval = opt_self.__step(*args, **kwargs)
            # prune parameters after step method
            with torch.no_grad():
                for (
                    module_name,
                    module,
                    p_name,
                    p,
                    mask,
                ) in cls.sparse_parameters:
                    p.mul_(mask)
            return rval

        cls.optimizer.step = types.MethodType(__step, cls.optimizer)

    @classmethod
    def compute_sparse_masks(cls): #!aaaa
        with torch.no_grad():
            for module_name, module, p_name, p, mask in cls.sparse_parameters:
                mask.set_(cls.calculate_mask(p)) # torch.Size([8, 16]) # mask = cls.calculate_mask(p) # in place op
                p.mul_(
                    mask
                )  # in-place multiplication, so pruned weights are 0-values, hence checkpoint will have 0s for pruned weights

    @classmethod
    def prune_trained_model(cls, model, optimizer):
        # add mask buffers to model (init_model_for_pruning), augment optimizer (init_optimizer_for_pruning) and compute masks (compute_sparse_masks)
        cls.init_model_for_pruning(
            model,
            mask_calculator="m4n2_1d",
            verbosity=2,
            whitelist=[torch.nn.Linear, torch.nn.Conv2d],
        )
        cls.init_optimizer_for_pruning(optimizer)
        cls.compute_sparse_masks()

构建mask

def create_mask(tensor, pattern="m4n2_1d", density=0.5): #! 0
    # Reshape tensor and mask.
    shape = tensor.shape
    ttype = tensor.type()
    t = tensor.float().contiguous()

    # len(shape) == 2:
    t = t.view(shape[0], shape[1])
    func = getattr(sys.modules[__name__], pattern, None) # getattr() asks for the name of a thing we're looking for (like a function or an attribute in a module), and if it finds it, we can use it later in our code.
    mask = func(t, density) # func here is m4n2_1d func
    return mask.view(shape).type(ttype)
param = torch.randn(8, 16).to("cuda:0")

    def create_mask_from_pattern(param):
        return create_mask(param, "m4n2_1d").bool() #工厂模式

    mask = create_mask_from_pattern(param)

首先是取到权重矩阵,然后分割成每4个一组,然后乘以01的全排列(m个位置里选出n个1),假设是4,则有6种排列,那么结果是n*6的矩阵,然后在每一个维度上取一个最大值

#从m个位置里选出n个位置为1并生成所有的排列
def compute_valid_1d_patterns(m, n): 
    patterns = torch.zeros(m) # [0,0,0,0]
    patterns[:n] = 1
    valid_patterns = torch.Tensor(list(set(permutations(patterns.tolist()))))
    return valid_patterns
def mn_1d_best(matrix, m, n): 
    patterns = compute_valid_1d_patterns(m, n).cuda()
    #首先把权重矩阵复制出来,全部填上1,并更改为4个一组
    mask = torch.cuda.IntTensor(matrix.shape).fill_(1).view(-1, m)
    mat, shape = reshape_1d(matrix, m) # matrix: [8, 16] ==>  mat[32, 4]
    #做矩阵乘法,并对每一行取一个最大值的索引
    #在PyTorch中,torch.argmax()函数返回输入张量中沿指定维度最大值的索引。dim参数就是用来指定这个维度的。
    pmax = torch.argmax(torch.matmul(mat.abs(), patterns.t()), dim=1) # 32x4@4x6=32x6
    #pmax是索引,根据索引把对应01排列取出来
    mask[:] = patterns[pmax[:]]
    #然后将mask还原成matrix的形状
    mask = mask.view(matrix.shape)
    return mask

标签:剪枝,name,方案,torch,mask,module,05Nvidia,model,cls
From: https://www.cnblogs.com/125418a/p/17532451.html

相关文章

  • 构建数字化警务移动平台所面临的难题与技术应对方案
    当谈及数字警务时,它被认为是一种较为创新的新型警务模式,由于其本身的便捷性以及来自社会的广泛认可,逐渐得到公安机关的高度关注和广泛应用。与传统警务相比,数字警务不仅是简单的技术升级,更重要的是通过技术创新和工作模式的创新来全面提升警务工作的效率和质量,实现警务工作的全面......
  • 岩土工程中振弦类采集仪的完整解决方案:从仪器选型到结果解释
    岩土工程中振弦类采集仪的完整解决方案:从仪器选型到结果解释岩土工程中,振弦类采集仪是一种常用的工具,用于测量土壤中的弹性波速度、土层的物理性质和地下水位等参数。它可以通过振动和接收地震波来获取这些数据,具有精度高、数据处理方便等优点。 振弦类采集仪的完整解决方案......
  • Bootstrap的模态框无法弹出解决方案
     Bootstrap的模态框无法弹出的问题 今天在使用Bootstrap官网所提供的模态框插件时候发现其中的可选尺寸模态框无法弹出在模态框前使用过其他Bootstrap的js插件,可以正常使用,说明所需依赖js文件已经正常引用注意:jquery.min.js与bootstrap.min.js文件引入......
  • umi + antd pro代码 在编辑器报警的解决方案
    前言:umi+antdpro工程,创建后,使用起来的时候毛病多。 2,在webstorm编辑器报错TS2686:'React'referstoaUMDglobal,butthecurrentfileisamodule.Consideraddinganimportinstead.查资料说配置:WhatDanwroteiscorrectyoucanchangeTS config.json://ts......
  • Unreal Engine4 GPU崩溃或3D设备丢失的解决方案
    起因:UnrealEngine4在渲染时报错GPU崩溃或3D设备丢失解决办法:regedit 打开注册表在以下2个路径下新建 DWORD(32-bit)Value命名为  TdrDelay,并修改数值为:60(十进制)Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\GraphicsDriversComput......
  • im私有化部署:保障企业内部数据安全与隐私的首选方案
    随着信息技术的迅猛发展,即时通讯(IM)已成为人们生活和工作中不可或缺的一部分。然而,随之而来的数据安全和隐私问题也日益突出。为了应对这一挑战,越来越多的组织和企业开始采用IM私有化部署方案,以保障数据安全与隐私。  IM私有化部署是指将即时通讯系统部署在企业自有的服务器......
  • DevExpress WinForms日程/日历组件,可轻松创建信息管理解决方案!(二)
    在上文中(点击回顾>>),我们主要介绍了DevExpressWinFormsScheduler组件的日历视图、议程和时间表视图、出色的高DPI渲染等,本文将继续介绍该组件的UI自定义等功能。PS:DevExpressWinForm拥有180+组件和UI库,能为WindowsForms平台创建具有影响力的业务解决方案。DevExpressWinForm......
  • 教学软件设计方案及程序
    很多人在教学行业使用久远,在教学道路上缺少很多多媒体工具:例如计算器,多媒体助手等,今天推荐一款软件,作者原创开发,C#编译,可教学使用,不可商用!软件下载(推荐,下载不限速):https://www.123pan.com/s/e7LDVv-ImFmH.htmlgithub:https://github.com/da0505/C-sharp软件代码(本代码已经过测......
  • 模型剪枝:给模型剪个头发
    ​本文来自公众号“AI大道理”。 深度学习网络模型从卷积层到全连接层存在着大量冗余的参数,大量神经元激活值趋近于0,将这些神经元去除后可以表现出同样的模型表达能力,这种情况被称为过参数化,而对应的技术则被称为模型剪枝。网络一般是over-parameterized参数过多的......
  • 模型剪枝:Network Slimming剪枝实战
    ​本文来自公众号“AI大道理”​NetworkSlimming剪枝是比较广泛的一种模型剪枝方法,作者来自清华大学、英特尔中国实验室、复旦大学和科内尔大学。 ​ 添加图片注释,不超过140字(可选)​1、NetworkSlimming剪枝理论NetworkSlimming剪枝是结......