当前位置: 首页 > news >正文

模型轻量化1--模型剪枝

毕业论文需要,所以近期在学习剪枝的东西,简单学了一下基础的东西,担心以后忘记,便进行简单的记录,以下是剪枝的主要流程:

(1)模型的稀疏训练:这是剪枝之前非常重要的一步,在BN层中添加L1正则项可以使模型在训练过程中带来稀疏性。

def updateBN():for m in model.modules():if isinstance(m, nn.BatchNorm2d):# sign是符号函数m.weight.grad.data.add_(args.s*torch.sign(m.weight.data))  # L1

(2)模型的剪枝:

          简单的用四步遍历来进行流程上的讲解吧:

        ①第一步遍历,记录原始模型BN层的通道总数;(便于后面计算模型要裁剪的总数);

        ②第二步遍历,先用上一步的通道数来构建一个空列表bn,用来存放所有的BN层的权重参数,之后对这些权重参数进行排序,然后根据剪枝率计算要裁剪的阈值;

        ③第三次遍历,利用上一次遍历计算出来的阈值来构建一个BN层的mask(大于阈值为1,小于阈值为0),并用此来获取剪枝后模型的配置(cfg)。此时只要将原始权重 * mask,这样模型就是增加了mask的模型了(需要注意的是,效果上和剪枝之后是一样的,但是参数量并没有变少,只是设置为0了);
        ④第四次遍历,这一次是对原始模型和新模型同时进行遍历:当遍历到需要剪枝的模块的时候(卷积层,BN层,Linear层),对其进行相应的操作即可:

             a)卷积层:weights:(out_channels, in_channels, kernel_size, kernel_size);

                需要获取两个mask,一个start_mask对应着剪枝后这层的输入通道,另一个是end_mask对应着剪枝后这层的输出通道,在获得两个mask之后,将原始模型的权重参数赋值到新的模型上。

        elif isinstance(m0, nn.Conv2d):# 输入通道idx0 = np.squeeze(np.argwhere(np.asarray(start_mask.cpu().numpy())))# 输出通道:将mask转换成ndarray,维度为57,对应位置为要保留的通道    idx1 = np.squeeze(np.argwhere(np.asarray(end_mask.cpu().numpy())))      print('In shape: {:d}, Out shape {:d}.'.format(idx0.size, idx1.size))# todo:对输入的通道进行剪枝w1 = m0.weight.data[:, idx0.tolist(), :, :].clone()# todo:对输出的通道进行剪枝  64,3,3,3 => 57,3,3,3     w1 = w1[idx1.tolist(), :, :, :].clone()# todo:将剪枝后的通道进行赋值                 m1.weight.data = w1.clone()# todo:只需要对输出通道进行剪枝,并且bias的维度是只有1个维度的!!!!!                             b1 = m0.bias.data[idx1.tolist()].clone()   m1.bias.data = b1.clone()  # todo:将剪枝后的通道进行赋值

                b)BN层:

                BN层的时候,需要获取一个mask,这个mask可以直接取上一个卷积层的end_mask可。(BN层的输入和输出是一样的)

        if isinstance(m0, nn.BatchNorm2d):# 计算当前BN层要保留权重的索引idx1 = np.squeeze(np.argwhere(np.asarray(end_mask.cpu().numpy())))# 计算当前层的权重,并将要保留的通道权重进行复制m1.weight.data = m0.weight.data[idx1.tolist()].clone()# 计算当前层的bias,并将要保留的通道权重进行复制m1.bias.data = m0.bias.data[idx1.tolist()].clone()# 计算当前层的running_mean,并将其进行复制 m1.running_mean = m0.running_mean[idx1.tolist()].clone()# 计算当前层的running_var,并将其进行复制                 m1.running_var = m0.running_var[idx1.tolist()].clone()# 将层的cfg索引 +1 以便于更新start_mask和end_masklayer_id_in_cfg += 1start_mask = end_mask.clone()   # 下一层的start_mask,就是上一层的end_maskif layer_id_in_cfg < len(cfg_mask):# todo:除全连接层之外的层的 end_mask 是索引为[layer_id_in_cfg]的cfg_maskend_mask = cfg_mask[layer_id_in_cfg]

                c)Linear层:线性层也是如同卷积层一样获取start_maskend_mask,值得注意的是Linear层需要对bias进行剪枝,并且bias的剪枝的时候是直接复制的bias的权重,因为bias的权重只与输出的通道有关,所以如果有两层线性层的话,Linearbias是要剪枝成end_mask的形状的。(下面的实例代码是只有一层线性层,并且10个分类的分类任务,所以最后已成的end_mask不需要剪枝,故直接clone( )即可)。

        elif isinstance(m0, nn.Linear):idx0 = np.squeeze(np.argwhere(np.asarray(start_mask.cpu().numpy())))m1.weight.data = m0.weight.data[:, idx0].clone()m1.bias.data   = m0.bias.data.clone()

(3)剪枝完成之后的模型微调训练:

        最后读取剪枝的 cfg 配置构建模型,并且读取模型的权重之后进行正常的微调训练即可。
 


http://www.mrgr.cn/news/54226.html

相关文章:

  • [笔记] 关于CreateProcessWithLogonW函数创建进程
  • vue 页面导出gif图片 img 导出gif 超简单~
  • Unity中面试遇到的问题--C#--dynamic
  • Springboot项目
  • OCR经典神经网络(三)LayoutLM v2算法原理及其在发票数据集上的应用(NER及RE)
  • Linux下进程通信原理图(详细)总结附实例代码快速掌握
  • AI周报(10.13-10.19)
  • 把自己写的文章发布在各大媒体网站上难不难?
  • 【每日一题】【算法双周赛】【第 20 场 小白入门赛评价/分享】赛后另类AI写题分析分享
  • 2025年天津仁爱学院专升本动画化学工程与工艺专业对应专业限制
  • 《嵌入式最全面试题-Offer直通车》目录
  • Lua字符串
  • JDK 1.6主要特性
  • 我的JAVA项目构建
  • 怎么修改编辑PDF的内容,有这4个工具就行了。
  • MySQL-20.多表设计-一对一多对多
  • 解锁A/B测试:如何用数据驱动的实验提升你的网站和应用
  • 速盾:为什么高防cdn比普通cdn效果更好?
  • 利士策分享,财富与福报,有没有内在联系?
  • 【Macbook air 2017 升级换硬盘遇到的问题】
  • Thread类的介绍
  • 简历怎么写?怎么准备面试?怎么让面试官感兴趣?
  • 快速查看平台信息脚本(完善中...)
  • Javaweb基础-vue
  • 1024程序员节 我们在 上海-RAG学习
  • Lua数字