环境要求
- python3
-
总体使用流程
通过本工具得到训练好的float onnx模型,以及MNN模型压缩参数文件
通过MNN转换工具,输入这两个文件,得到最终的MNN稀疏模型(如果直接部署稀疏的float模型,而不叠加量化,那么转换时需要使用MNNConvert的 —weightQuantBits 8 参数进行转换,才会进行稀疏编码,否则模型大小将不变)
支持的op,使用建议
目前支持torch.nn.Conv2d,torch.nn.Linear
优化器超参使用模型收敛阶段的超参,学习率可以在收敛阶段学习率的基础上再调小
支持的剪枝算法
SNIPLevelPruner:最细粒度的随机剪枝算法,稀疏单位为单个权值。一般需要剪枝比例达到80%以上才有加速,此方法主要进行压缩。
- SIMDOCPruner:稀疏单位为1*4的剪枝算法,一般需要30%以上剪枝比例才能加速,精度比通道剪枝好。
TaylorFOChannelPruner:通道剪枝算法,此算法是将conv2d的一整个filter剪掉,因此此filter对应的输出通道会失效,剪枝完模型是一个规整的模型,不需要后端进行特殊加速,但为了将剪掉的filter从模型中真正去掉,需要使用MNNConverter进行转换,转换过程中会分析剪枝通道之间的依赖关系,并完成最终的转换。
使用方法
建议从训练好的float模型进行finetune
- 创建Pruner对象,对原始模型进行转换,然后用转换之后的模型进行训练
- 在train过程中,调用pruner的do_pruning方法进行剪枝
- 导出MNN模型压缩参数文件,示例代码如下(关注其中pruner的用法): ```python from mnncompress.pytorch.SNIP_level_pruner import SNIPLevelPruner from mnncompress.pytorch.SIMD_OC_pruner import SIMDOCPruner Pruner = SIMDOCPruner
你的模型代码
class Net(nn.Module): pass
model = Net()
加载已经训练好的模型
model.load_state_dict(torch.load(“ori_model.pt”)) model.to(device)
将模型进行转换,并使用转换后的模型进行训练,测试
更多配置请看API部分
pruner = SIMDOCPruner(model, total_pruning_iterations=1, sparsity=0.6, debug_info=False)
def train(model, data, optimizer, pruner) model.train() for d, t in data: optimizer.zero_grad() output = model(d) loss = F.nll_loss(output, t) loss.backward() optimizer.step()
# step之后调用pruner的剪枝方法
pruner.do_pruning()
# 获取当前剪枝比例
print(pruner.current_prune_ratios())
for epoch in range(1, epochs + 1): train(model, data, optimizer, pruner) test(model, data)
保存模型
model.eval() torch.save(model.state_dict(), “pruned_model.pt”) x = torch.randn(input_shape).to(device) torch.onnx.export(model, x, “pruned_model.onnx”)
保存MNN模型压缩参数文件,应在剪枝完毕之后进行,建议在保存模型时调用
pruner.save_compress_params(“compress_params.bin”, append=False)
5. 模型稀疏之后,可以进一步使用PyTorch训练量化工具进行量化,得到稀疏量化模型。也可以直接使用此float稀疏的模型进行推理,需要在MNN模型转换时指定剪枝得到的MNN模型压缩参数文件,并进行权值量化(才会进行稀疏编码):
```bash
mnnconvert --modelFile pruned_model.onnx --MNNModel pruned_model.mnn --framework ONNX --bizCode MNNTest --compressionParamsFile compress_params.bin --weightQuantBits 8
相关API
SNIPLevelPruner / SIMDOCPruner / TaylorFOChannelPruner
Pruner(model, sparsity=0.5, total_pruning_iterations=1, config_file=None, debug_info= False,
prune_finetune_iterations=0, max_prune_ratio=0.99,
align_channels=4 # only for 'TaylorFOChannelPruner'
):
参数
model:Module,pytorch模型
sparsity:float,0~1,模型总体剪枝比例
total_pruning_iterations:int,指定多少个iteration将指定比例的连接剪掉
config_file:str,可先运行一次Pruner的do_pruning方法,得到搜索出来的剪枝比例yml文件之后,
然后微调此yml文件中的各层剪枝比例,将修改后的文件名通过此参数传入,可控制各层剪枝比例
debug_info:是否输出debug信息
prune_finetune_iterations: int, 指定剪枝进行一步,finetune多少步,如指定为99,则剪枝一次,
finetune99次,在finetune的99次过程中剪枝mask不变
max_prune_ratio: float, 指定各层最大剪枝比例
align_channels: int, 仅通道剪枝算法TaylorFOChannelPruner有此参数,用于指定剪枝之后对齐的通道数
方法和属性
do_pruning(result_file = "found_prune_ratios.yml"):进行一次剪枝,在训练optimizer.step之后调用,搜索到的各层剪枝比例将保存到此文件中
current_prune_ratios():返回当前各层剪枝比例
save_compress_params(filename, append=False): 保存MNN模型压缩参数到filename文件
append 表示是否将剪枝参数信息append到传入的文件中,如果为false,则将新建或覆盖该文件,
append=True 表示剪枝之前还做了其他模型压缩操作,filename文件中已存在相关压缩信息