人工智能AutoML机器学习深度学习模型压缩特征工程【免费下载链接】nniAn open source AutoML toolkit for automate machine learning lifecycle, including feature engineering, neural architecture search, model compression and hyper-parameter tuning.项目地址https://gitcode.com/gh_mirrors/nn/nni点击查看免费下载本指南聚焦 NNI 压缩套件中的ModelSpeedup工具介绍如何把剪枝产生的掩码mask真正转化为模型结构的压缩与推理加速。读完本文你将掌握ModelSpeedup的完整参数语义、从掩码到紧凑模型的端到端操作流程、其基于 torch.fx 的两阶段实现原理以及自定义掩码更新器MaskUpdater与替换器Replacer进行深度定制的实战方法。模型剪枝通常以权重掩码的形式模拟真实剪枝掩码可以用于评估某一剪枝比例下模型的精度但掩码本身并不能带来真实的推理加速。剪枝的最终目标是让模型真正变小、跑得更快NNI 为此提供了nni.compression.speedup.ModelSpeedup——一个独立于剪枝算法的工具它依据用户提供的掩码将模型转换成更小的稠密dense模型从而兑现剪枝带来的加速收益。其 API 文档见 docs/source/reference/compression/pruning_speedup.rst完整实现位于 nni/compression/speedup/配套的可运行教程源码为 examples/tutorials/pruning_speedup.py。细粒度剪枝与粗粒度剪枝加速策略的分野在讨论工具之前需要先明确两类剪枝及其加速路径的区别细粒度剪枝fine-grained pruning权重矩阵中的单个元素被随机置零权重形状以及输入/输出张量的形状都不会改变。要使其加速必须依赖稀疏内核sparse kernel在底层跳过零值计算。粗粒度剪枝coarse-grained pruning以通道channel、滤波器filter等结构为单位进行剪枝权重形状以及输入/输出张量的形状通常会随之改变。这类剪枝无需稀疏内核只需将剪枝后的层替换为一个更小的稠密层即可实现加速。由于社区对稀疏内核的支持有限NNI 的ModelSpeedup只支持粗粒度剪枝的加速细粒度剪枝的加速留待后续支持。这一点在 examples/tutorials/pruning_speedup.py 的教程引言中有明确说明。ModelSpeedup 的两阶段设计与实现原理加速一个模型本质上是替换被剪枝的层粗粒度掩码对应替换为更小的层细粒度掩码对应替换为稀疏内核。粗粒度掩码通常会引起权重或输入/输出张量形状变化因此除了被直接剪枝的层还需要通过形状推断确认是否有其他未被剪枝的层也因形状变化而必须一并替换。据此ModelSpeedup的实现分为两个主步骤形状推断找出所有需要被替换的模块模块替换将这些模块替换为更小的等价模块。第一步需要模型的计算图拓扑NNI 使用基于torch.fx的 concrete trace 获取 PyTorch 模型的计算图模块的新形状由 NNI 自动推断——前向传播中输出保持不变的部分、反向传播中输入保持不变的部分被标记出来用于推导可删除的结构维度针对每种模块类型仓库提供了对应的替换函数返回一个更小的新模块。从源码结构看nni/compression/speedup/replacement.py 中维护了一张模块类型到替换函数的映射表replace_moduledict涵盖Conv1d/Conv2d/ConvTranspose2d、Linear、BatchNorm1d/2d、LayerNorm、GroupNorm、Embedding、PixelShuffle等需要真正压缩的模块而ReLU、MaxPool2d、Dropout等不改变张量形状的模块则映射到no_replace。从源码看完整加速流水线ModelSpeedup.speedup_model()nni/compression/speedup/model_speedup.py的完整流程如下保存原始权重先把graph_module的 state_dict 存入临时文件加速完成后恢复保证不因推断过程污染权重掩码冲突修复fix_mask_conflict依次调用fix_group_mask_conflict、fix_channel_mask_conflict、fix_weight_sharing_mask_conflict定义于 nni/compression/speedup/mask_conflict.py解决分组卷积、通道共享与权重共享导致的掩码不一致问题前向传播propagate_originally按图节点顺序正常执行一次记录每个节点的原始输出output_origin与原地操作后的输出output_inplace为后续推断准备中间张量的形状、dtype 等信息直接稀疏度推断update_direct_sparsity沿前向方向通过比较批量维度上输出张量的统计特征如标准差判断哪些通道恒为常数而被剪掉间接稀疏度推断update_indirect_sparsity沿反向方向以输出掩码作为梯度做一次 backward梯度恒为零的参数通道同样标记为可剪再次修复掩码冲突随后替换压缩模块replace_compressed_modules最后恢复原始权重与训练/评估模式。如果某个call_module节点没有对应的替换函数代码会记录 error 日志并提示可通过customized_replacers自行实现替换逻辑。快速上手10 行代码完成模型压缩先看一个最小可用示例完整代码见 examples/tutorials/pruning_speedup.py。该示例使用仓库内置的 MNIST 模型 nni_assets/compression/mnist_model.py结构为Conv2d(1,6) - Conv2d(6,16) - Linear(256,120) - Linear(120,84) - Linear(84,10)。第一步构造掩码通常掩码由 NNI 的剪枝器Pruner生成但ModelSpeedup是相对独立的工具可以脱离剪枝器单独使用。掩码格式为{layer_name: {weight: weight_mask, bias: bias_mask}}import torch from nni_assets.compression.mnist_model import TorchModel, device model TorchModel().to(device) # masks {layer_name: {weight: weight_mask, bias: bias_mask}} conv1_mask torch.ones_like(model.conv1.weight.data) # 将 conv1 的前三个输出通道掩码置零 conv1_mask[0: 3] 0 masks {conv1: {weight: conv1_mask}}第二步查看原始模型结构与推理耗时print(model) import time start time.time() model(torch.rand(128, 1, 28, 28).to(device)) print(Original Model - Elapsed Time : , time.time() - start)原始模型结构为TorchModel( (conv1): Conv2d(1, 6, kernel_size(5, 5), stride(1, 1)) (conv2): Conv2d(6, 16, kernel_size(5, 5), stride(1, 1)) (fc1): Linear(in_features256, out_features120, biasTrue) (fc2): Linear(in_features120, out_features84, biasTrue) (fc3): Linear(in_features84, out_features10, biasTrue) (relu1): ReLU() (relu2): ReLU() (relu3): ReLU() (relu4): ReLU() (pool1): MaxPool2d(kernel_size(2, 2), stride(2, 2), padding0, dilation1, ceil_modeFalse) (pool2): MaxPool2d(kernel_size(2, 2), stride(2, 2), padding0, dilation1, ceil_modeFalse) )第三步执行加速from nni.compression.speedup import ModelSpeedup ModelSpeedup(model, torch.rand(10, 1, 28, 28).to(device), masks).speedup_model() print(model)加速后的模型结构中conv1的输出通道从 6 变为 3被掩码的三个输出通道被结构性移除conv2的输入通道也随之从 6 变为 3充分体现了形状推断的级联效应TorchModel( (conv1): Conv2d(1, 3, kernel_size(5, 5), stride(1, 1)) (conv2): Conv2d(3, 16, kernel_size(5, 5), stride(1, 1)) (fc1): Linear(in_features256, out_features120, biasTrue) (fc2): Linear(in_features120, out_features84, biasTrue) (fc3): Linear(in_features84, out_features10, biasTrue) ... )第四步验证加速效果start time.time() model(torch.rand(128, 1, 28, 28).to(device)) print(Speedup Model - Elapsed Time : , time.time() - start)教程示例中掩码模型Mask Latency与加速后模型Speedup Latency的延迟对比如下表测试环境为单块 V100 GPUSimulatedAnnealing 示例为 ResNet18 CIFAR10 场景Pruner输入张量说明slim prunertorch.randn(64, 3, 32, 32)V100 GPUfpgm prunertorch.randn(64, 1, 28, 28)CPU方差较大l1filter prunertorch.randn(64, 3, 32, 32)V100 GPUAPoZ prunertorch.randn(64, 3, 32, 32)V100 GPUslim pruner 示例的逐批延迟单位秒TimesMask LatencySpeedup Latency10.011970.00510720.020190.00876940.027330.01480980.043100.027441160.077310.05008320.144640.10027l1filter pruner 示例的逐批延迟单位秒TimesMask LatencySpeedup Latency10.010260.00367720.016570.00816140.024580.02001880.034980.025504160.067570.047523320.104870.086442APoZ pruner 示例的逐批延迟单位秒TimesMask LatencySpeedup Latency10.013890.00420820.016280.00831040.025210.01400880.033860.023923160.060420.046183320.124210.087113这些数据来自旧版剪枝框架下的评测仅用于说明掩码模拟 vs. 真实加速的延迟差异趋势具体数值随硬件、PyTorch 版本与模型结构波动请以实际运行为准。构造参数详解ModelSpeedup的完整签名与参数语义如下源自 nni/compression/speedup/model_speedup.py 的类文档参数类型说明modeltorch.nn.Module待加速的模型加速完成后模型会被原地替换为紧凑版本dummy_inputtensor 或 tuple用于执行模型的虚拟输入传入单个 tensor、list 或 dict 时会被自动归一化为 tuplemasks_or_filedict 或 str/Path用户提供的掩码对象或掩码文件的路径传入文件路径时内部通过torch.load加载因此需保证 PyTorch 能安全加载该文件map_locationAny掩码所在设备语义与torch.load的map_location相同batch_dimint默认0dummy_input 中批量维度的下标稀疏度推断以该维度为统计轴batch_sizeint默认8稀疏度推断的批量系数实际用作 dummy_input 的 batch size源码中张量元素数大于batch_size且能被batch_size整除时才被视为可传播中间张量customized_mask_updatersList[MaskUpdater]自定义掩码更新器列表。NNI 默认依据前向/反向的数据分布自动推断稀疏度若某些特殊算子导致自动推断出错可传入自定义更新规则customized_replacersList[Replacer]自定义替换器列表用于把原始模块替换为压缩模块这些自定义替换逻辑会在 NNI 内置替换逻辑之前顺序执行graph_moduletorch.fx.GraphModule若默认的 concrete trace 无法满足需求如模型含随机结构可直接传入用户预先 trace 好的 GraphModulegarbage_collect_valuesbool默认True为True时NNI 会在缓存不再被使用时及时删除以降低峰值内存loggerlogging.Logger自定义日志器为None时使用默认 loggerINFO 级别两个关键点值得注意concrete trace 的失败风险ModelSpeedup基于torch.fx的 concrete trace 获取计算图若模型内部存在随机结构stochastic structuretrace 可能失败此时应改用graph_module参数直接传入手动构造的 GraphModule。掩码加载当masks_or_file为路径时要求文件存在若既不是存在的路径也不是 dict会抛出Exception(Please provide the mask or the path of the mask file.)。稀疏度推断与掩码传播机制ModelSpeedup如何知道除了显式掩码的层之外还有哪些层需要一并压缩答案隐藏在 nni/compression/speedup/mask_updater.py 的掩码传播机制中。核心思想是逐节点维护一张输出掩码output_mask并让掩码沿计算图流动。直接稀疏度推断direct sparsity在前向方向上每个节点的输出掩码由direct_calc_mask计算对输出张量沿 batch 维做统计若某位置的标准差低于阈值STD_DELTA 1e-6说明该位置在所有样本上恒为常数即被剪掉对应掩码位置 0对于整型张量则直接比较各样本取值是否完全相同。DefaultMaskUpdater.direct_update_process会先把输入张量与输入掩码逐元素相乘再执行节点得到输出从而让稀疏信息自然传播。间接稀疏度推断indirect sparsity某些算子无法仅凭前向输出判断通道是否冗余例如 concat、add 等汇合型算子此时使用反向传播indirect_calc_mask对输出梯度沿 batch 维求和取绝对值梯度恒为零的位置说明该通道对最终输出没有贡献对应掩码位置 0随后以该掩码作为梯度对输出做 backwardindirect_backward梯度会沿计算图传播回前驱节点与模块参数LeafModuleMaskUpdater.indirect_update_process中v.grad.data 0的参数通道被置零。免计算掩码的特殊算子为提升效率并保证正确性mask_updater.py内置了三个特化的掩码更新器NoChangeMaskUpdater处理掩码传播中输出掩码 输入掩码的算子包括各类激活函数ReLU、Sigmoid、GELU、Softmax、LayerNorm 等见no_change_act_func/no_change_act_module元组、getitem切片、以及clone/detach方法。源码注释指出对 softmax/log_softmax 使用默认流程会得到错误的掩码必须直接从输入复制掩码。NoMaskUpdater处理len、is、is not、contains、dim、size等不产生掩码的算子。LeafModuleMaskUpdater所有call_module节点的默认更新器负责为模块参数补齐默认全 1 掩码并在推断过程中随机化参数以检验哪些通道真正恒定。四个更新器按[customized, NoChange, NoMask, LeafModule, Default]的顺序依次detect先命中者接管该节点的掩码更新逻辑见ModelSpeedup.__init__。这解释了customized_mask_updaters为何能在内置规则之前介入。模块替换机制与自定义 Replacer内置替换逻辑替换阶段由Replacer完成。默认使用DefaultReplacernni/compression/speedup/replacer.py它遍历计算图中所有call_module节点根据模块类型名从replace_module_func_dict中取出替换函数以(in_masks, output_masks, param_masks)作为输入调用replace_function(module, masks)生成压缩模块并通过set_nested_attr原位挂回模型。Nni/compression/speedup/replacement.py 内置的replace_module字典目前支持以下压缩型模块Conv1d、Conv2d、ConvTranspose2d、Linear、BatchNorm1d、BatchNorm2d、InstanceNorm2d、LayerNorm、GroupNorm、Embedding、PixelShuffle、PReLUMaxPool2d、AvgPool2d、AdaptiveAvgPool2d、ZeroPad2d、各类激活与Dropout、Upsample、Flatten等模块形状不变映射到no_replace。以convert_to_coarse_mask为例替换函数会先把细粒度掩码按指定维度求和归约若某通道在其余所有维度上的掩码求和为零说明该通道可整体删除indexes其余通道保留remained_indexesconvert_dense_shape据此得到压缩后的张量形状再创建参数形状匹配的新模块并继承权重。这一步源码明确说明了掩码必须是结构性的这一前提只有整体为 0 的通道才会被移除。自定义替换器如果模型包含 NNI 尚未内置支持的模块类型替换阶段会打印 error 日志并跳过该模块提示使用customized_replacers实现替换。自定义 Replacer 需要继承 nni/compression/speedup/replacer.py 中的Replacer基类并实现replace_modules(self, speedup)方法或在DefaultReplacer基础上补充replace_module_func_dict。DefaultReplacer的类文档给出了替换函数签名约定# (original_module, (input_mask, output_mask, weight_mask)) - compressed_module def fake_module_replace(ori_module, masks): in_mask, out_mask, weight_mask masks # 依据掩码将 ori_module 剪成更小的新模块 return new_small_module replace_module_func_dict {FakeModule: fake_module_replace}ModelSpeedup.__init__中customized_replacers会被追加到内置DefaultReplacer之前因此自定义替换逻辑总是优先执行。与剪枝器的组合使用与注意事项组合流程ModelSpeedup与 NNI 剪枝器的典型组合方式先用剪枝器如 Slim、FPGM、L1Filter、APoZ、SimulatedAnnealing 等生成掩码再将掩码传入ModelSpeedup完成真实压缩。详细的分步教程可参考 examples/tutorials/pruning_quick_start.py 及其渲染文档 docs/source/tutorials/pruning_quick_start.rst。NNI 的测试套件中也有大量组合验证用例例如 test/algo/compression/pruning/test_speedup_torchvision.py 验证了 torchvision 模型加速后的输出一致性test/algo/compression/pruning/test_speedup_softmax.py 专门覆盖 softmax 场景的掩码传播正确性test/algo/compression/pruning/test_speedup_init_mask.py 则覆盖了初始化掩码的各类边界情况可作为自定义场景的参考。限制与规避版本要求当前实现要求 PyTorch 1.3.1 或更新版本。仅能替换模块对于 PyTorchModelSpeedup只能替换模块module如果forward中需要被替换的是函数func当前实现无法工作——源码注释明确指出func类型无法替换因为它不是模块。规避方法把该函数封装成一个 PyTorch 模块再参与加速。随机结构 trace 失败模型含随机结构时 concrete trace 可能失败可通过graph_module参数直接传入手动 trace 的 GraphModule。未支持的模块类型会记录 error 日志并跳过替换需通过customized_replacers补齐替换逻辑。性能观察掩码模拟与真实加速教程中 SimulatedAnnealing pruner 的实验在 CIFAR10 上剪枝 ResNet18分别测量不同稀疏度下的延迟与精度V100 GPU输入torch.randn(128, 3, 32, 32)相关可视化见 docs/img/SA_latency_accuracy.png。多组示例的共同规律是掩码模型Mask Latency与加速后模型Speedup Latency的延迟都随 batch 增大而增长但加速后模型的延迟几乎始终低于掩码模型——这正说明移除结构维度、缩小稠密层尺寸能带来不依赖稀疏内核的真实计算收益。需要强调的是这些数字来自特定历史版本的旧剪枝框架且 fpgm 示例在 CPU 上测量时方差较大。在实际项目中应结合自身硬件、模型与剪枝器重新基准测试并把精度恢复如微调纳入剪枝-加速的整体流程。赞分享人工智能AutoML机器学习深度学习模型压缩特征工程【免费下载链接】nniAn open source AutoML toolkit for automate machine learning lifecycle, including feature engineering, neural architecture search, model compression and hyper-parameter tuning.项目地址https://gitcode.com/gh_mirrors/nn/nni点击查看免费下载相关推荐如何用 AnimateAnyone 让一张人物图做出动作连贯的动画视频新手完整指南如何用 AnimateAnyone 让一张人物图做出动作连贯的动画视频新手完整指南 想让一张静态的角色照片动起来直接丢给通用视频生成模型往往会让角色变人工智能AutoML机器学习深度学习模型压缩特征工程图生 3D 只要几分钟Hunyuan3D-2 从参考图到带纹理网格的最短路径图生 3D 只要几分钟Hunyuan3D 2 从参考图到带纹理网格的最短路径 手搓高模排期排不上、外包报价又贵的时候图生 3D 是你最快能落地的替代方案。H人工智能AutoML机器学习深度学习模型压缩特征工程Microsoft NNI模型剪枝快速入门指南Microsoft NNI模型剪枝快速入门指南 什么是模型剪枝 模型剪枝是一种通过减少模型权重或中间状态大小来降低模型计算量和存储需求的技术。在深度神经网络中人工智能AutoML机器学习深度学习模型压缩特征工程上一篇Docker环境下部署Anemometer简单高效的容器化方案下一篇Redo与Shell集成终极指南如何将生成函数无缝融入日常开发流程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
