微软NNI进行神经网络模型剪枝压缩的踩坑记录

微软NNI进⾏神经⽹络模型剪枝压缩的踩坑记录
微软NNI进⾏神经⽹络模型剪枝压缩的踩坑记录
最近做毕设嵌⼊式部署神经⽹络,想着对⽹络进⾏⼀下剪枝压缩加加速什么的,结果nni使⽤起来⼀脸懵逼。。。第⼀次在CSDN写⽂章,也是当作⾃⼰的学习笔记了~
NNI进⾏模型剪枝分类
第⼀眼看上去nni真的⽀持不少论⽂中的简直操作,基本都复现了⼀遍。但有些⽅法⼀调⽤就出错,还有的给了⽤法,不知道参数是个啥。。。
NNI剪枝的流程
以level为例
from nni.algorithmspression.pytorch.pruning import LevelPruner
config_list =[{'sparsity':0.8,'op_types':['default']}]
#default,需要修改的层
pruner = LevelPruner(model, config_list)
prunerpress()
os.path.periment_data_dir,'model_masked.pth'),os.path.periment_data_dir,'mask.pth'))#模型保存与模型掩码保存
m_speedup = ModelSpeedup(model, dummy_input, masks_file, device)
m_speedup.speedup_model()#⼀定要进⾏speedup才能加速
evaluation_result = evaluator(model)# 评估模型
torch.save(model.state_dict(), os.path.periment_data_dir,'model_speed_up.pth'))
最终保存模型
其中speedup最终为重要,这样才能加速。
但speedup过程真的时间很长,要进⾏掩码和model参数的计算。
NNI现有剪枝⽅法
⾸先是单⼀简单的剪枝操作:
1.Level Pruner
最简单的基本的⼀次性 Pruner:可设置⽬标稀疏度(以分数表⽰,0.6 表⽰会剪除 60%)。⾸先按照绝对值对指定层的权重排序。 然后按照所需的稀疏度,将值最⼩的权重屏蔽为 0。
from nni.algorithmspression.pytorch.pruning import LevelPruner
config_list =[{'sparsity':0.8,'op_types':['default']}]
#default,需要修改的层
pruner = LevelPruner(model, config_list)
prunerpress()
2.Slim Pruner
One-Shot Pruner,它在训练过程中对 batch normalization(BN)层的⽐例因⼦进⾏稀疏正则化,以识别不重要的通道。 ⽐例因⼦值较⼩的通道将被修剪。
from nni.algorithmspression.pytorch.pruning import SlimPruner
config_list =[{'sparsity':0.8,'op_types':['BatchNorm2d']}]
pruner = SlimPruner(model, config_list)
prunerpress()
3.FPGM Pruner
One-Shot Pruner,⽤最⼩的⼏何中值修剪卷积滤波器。 FPGM 选择最可替换的滤波器。
from nni.algorithmspression.pytorch.pruning import FPGMPruner
config_list =[{
'sparsity':0.5,
'op_types':['Conv2d']#修改所有的卷积层
}]
pruner = FPGMPruner(model, config_list)
prunerpress()
4.L1Filter Pruner
One-Shot Pruner,它修剪 卷积层 中的滤波器。L1正则化剪枝。
from nni.algorithmspression.pytorch.pruning import L1FilterPruner
config_list =[{'sparsity':0.8,'op_types':['Conv2d']}]
pruner = L1FilterPruner(model, config_list)
prunerpress()
5.L2Filter Pruner
这是⼀种结构化剪枝算法,⽤于修剪权重的最⼩ L2 规范卷积滤波器,算是⼀种⼀次性修剪器。
from nni.algorithmspression.pytorch.pruning import L2FilterPruner
config_list =[{'sparsity':0.8,'op_types':['Conv2d']}]
pruner = L2FilterPruner(model, config_list)
prunerpress()
还有⼏种剩下不常⽤的剪枝操作就暂时不贴出来了。
接下来是组合型的剪枝操作,也是坑⽐较多的操作:
6.AGP Pruner
⼀种⾃动逐步剪枝算法,在 n 个剪枝步骤中,稀疏度从初始的稀疏度(通常为 0)增加到最终的稀疏度。
from nni.algorithmspression.pytorch.pruning import AGPPruner
config_list =[{
'initial_sparsity':0,
'final_sparsity':0.8,
'start_epoch':0,
'end_epoch':10,
'frequency':1,
'op_types':['default']
}]
# 读取预训练的模型,或在使⽤ Pruner 前进⾏训练。
# model = MyModel()
# model.load_state_dict(torch.load('mycheckpoint.pth'))
# AGP Pruner 会在 optimizer. step() 上回调,在微调模型时剪枝,
# 因此,必须要有 optimizer 才能完成模型剪枝。
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9, weight_decay=1e-4)
pruner = AGPPruner(model, config_list, optimizer, pruning_algorithm='level')
prunerpress()
AGP剪枝在我使⽤过程中经常报错,⽐如我遇到过的报错有
‘0不能转换为float型’,于是我把参数改成了:
config_list =[{
'initial_sparsity':0.01,
'final_sparsity':0.8,
'start_epoch':1,
'end_epoch':10,
'frequency':1,
'op_types':['default']
视频直播技术方案}]
我是这样解决的,但确实很⽞学。。。
同时AGP Pruner 默认使⽤ LevelPruner 算法来修建权重,还可以设置 pruning_algorithm 参数来使⽤其它剪枝算法:
level: LevelPruner
slim: SlimPruner
l1: L1FilterPruner
l2: L2FilterPruner
fpgm: FPGMPruner
taylorfo: TaylorFOWeightFilterPruner
apoz: ActivationAPoZRankFilterPruner
mean_activation: ActivationMeanRankFilterPruner
7.NetAdapt Pruner
NetAdapt 在算⼒⾜够的情况下,⾃动简化预训练的⽹络。 给定整体稀疏度,NetAdapt 可通过迭代剪枝⾃动为不同层⽣成不同的稀疏分布。
from nni.algorithmspression.pytorch.pruning import NetAdaptPruner
config_list =[{
'sparsity':0.5,
'op_types':['Conv2d']
}]
pruner = NetAdaptPruner(model, config_list, short_term_fine_tuner=short_term_fine_tuner, evaluator=evaluator,base_algo='l1', experiment_data_dir='./') prunerpress()
⼤坑出现了,在GitHub官⽅⽂档⾥并没有写short_term_fine_tuner和evaluator这两个参数是啥,base涤纶编织物浸水性能试验
_algo是正则
化,experiment_data_dir则是模型保存的位置。我翻了很久的sample到了short_term_fine_tuner和evaluator这两个参数相应定义:
def evaluator(model):
return test(model, device, criterion, val_loader)
def test(model, device, criterion, val_loader):
model.eval()
test_loss =0
correct =0
_grad():
for data, target in val_loader:
data, target = (device), (device)
output = model(data)
# sum up batch loss
test_loss += criterion(output, target).item()
# get the index of the max log-probability
pred = output.argmax(dim=1, keepdim=True)
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /=len(val_loader.dataset)
accuracy = correct /len(val_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.2f}%)\n'.format(
test_loss, correct,len(val_loader.dataset),100.* accuracy))止推垫圈
return
仔细⼀看其实就是简单的返回⼀个accuracy,并且输⼊仅有model
def short_term_fine_tuner(model, epochs=1):
for epoch in range(epochs):
train(args, model, device, train_loader, criterion, optimizer, epoch)
short_term_fine_tuner是⼀个epoch的参数微调,也就是训练⼀次。
8.SimulatedAnnealing Pruner
模拟退⽕剪枝,此 Pruner 基于先验经验,实现了引导式的启发搜索⽅法,模拟退⽕(SA)算法。 增强的模拟退⽕算法基于以下理论:具有更多权重的深度神经⽹络层通常具有较⾼的可压缩度,对整体精度的影响更⼩。
from nni.algorithmspression.pytorch.pruning import SimulatedAnnealingPruner
config_list =[{
'sparsity':0.5,弹性夹头
'op_types':['Conv2d']
}]
pruner = SimulatedAnnealingPruner(model, config_list, evaluator=evaluator, base_algo='l1', cool_down_rate=0.9, experiment_data_dir='./')
prunerpress()
evaluator如上
9.AutoCompress Pruner
每⼀轮中,AutoCompressPruner 会⽤相同的稀疏度对模型进⾏剪枝,从⽽达到总体的稀疏度。
对苯树脂AutoCompress是基于模拟退⽕的算法。
from nni.algorithmspression.pytorch.pruning import AutoCompressPruner
config_list =[{
'sparsity':0.5,
'op_types':['Conv2d']
}]
pruner = AutoCompressPruner(
model, config_list, trainer=trainer, evaluator=evaluator,
dummy_input=dummy_input, num_iterations=3, optimize_mode='maximize', base_algo='l1',
cool_down_rate=0.9, admm_num_iterations=30, admm_training_epochs=5, experiment_data_dir='./')
prunerpress()
10.AMC Pruner
AMC Pruner 利⽤强化学习来提供模型压缩策略。 这种基于学习的压缩策略⽐传统的基于规则的压缩策略有更⾼的压缩⽐, 更好地保存了精度,节省了⼈⼒。
from nni.algorithmspression.pytorch.pruning import AMCPruner
config_list =[{
'op_types':['Conv2d','Linear']
}]
pruner = AMCPruner(model, config_list, evaluator, val_loader, flops_ratio=0.5)
prunerpress()
val_loader是使⽤pytorch的dataloader进⾏数据读⼊的验证集。
剩下的剪枝操作⽐较复杂还没有研究透,不过应该⼤同⼩异,有机会继续研究更新。
最后贴⼀下我的测试代码
import nni
import torch
as nn
from nnipression.pytorch import ModelSpeedup
from model_input import Model_input
import numpy as np
import data_loading
from prefetcher import data_prefetcher
from torch.utils.data import DataLoader
from PIL import Image
from torchvision import transforms
if __name__ =='__main__':
model_name ='shufflenet'
pruning_class ='SimulatedAnnealing'
model = Model_input(model_name).model_final
if model_name !='shufflenet_pruned':
dict_save_path ='model/'+ model_name +'_20210311.pkl'
model.load_state_dict(torch.load(dict_save_path, map_location="cuda:0"))
else:
dict_save_path ='model/'+ model_name +'_'+pruning_class +".pth"
model.load_state_dict(torch.load(dict_save_path, map_location="cuda:0"))
model.load_state_dict(torch.load(dict_save_path, map_location="cuda:0"))
model_final = model
def evaluator(model):
loss_fn = nn.CrossEntropyLoss()
test_dataset = data_loading.Dataset_loading('D:/Dataset_all/weld_Dataset_unlabel/al5083/test/test.json')        dataloader = DataLoader(test_dataset, shuffle=True, batch_size=32, num_workers=1, pin_memory=True)        model.cuda()
model.eval()
all_data_num =0
correct_data_num =0
loss_all =[]
corr_num_all =[]
loss =0
losses =0
prefetcher = data_prefetcher(dataloader)
_grad():
images, labels = ()
steps =0
while images is not None:
steps +=1
images, labels = images.cuda(), labels.cuda()
<output = model(images)
loss = loss_fn(output, labels)
losses += loss
loss_all.append(loss)
pred_labels = output.argmax(dim=1)
all_data_num += labels.size(0)
correct_data_num +=(pred_labels == labels).sum().item()
corr_num_all.append(correct_data_num)
images, labels = ()
acc =(correct_data_num / all_data_num)
# print('评估结果:test_loss:', np.array(losses.cpu()), 'test_acc:{:.2f}'.format(acc), '%')
# loss_record_path = 'train_record/'+ime())+'-'+str(epoch)+'-'+'loss'+'.txt'
# acc_record_path = 'train_record/' + ime()) + '-' + str(epoch) + '-'+'acc'+'.txt'
return acc
def pruner_load(pruning_class,model):
global short_term_fine_tuner,evaluator,trainer,dummy_input,val_loader,fine_tuner
if pruning_class =='level':
from nni.algorithmspression.pytorch.pruning import LevelPruner
config_list =[{'sparsity':0.8,'op_types':['default']}]
pruner = LevelPruner(model, config_list)
elif pruning_class =='slim':
from nni.algorithmspression.pytorch.pruning import SlimPruner
config_list =[{'sparsity':0.8,'op_types':['BatchNorm2d']}]
pruner = SlimPruner(model, config_list)
elif pruning_class =='FPGM':
from nni.algorithmspression.pytorch.pruning import FPGMPruner
config_list =[{
'sparsity':0.5,
'op_types':['Conv2d']}]
pruner = FPGMPruner(model, config_list)
elif pruning_class =='L1':
from nni.algorithmspression.pytorch.pruning import L1FilterPruner
config_list =[{'sparsity':0.8,'op_types':['Conv2d']}]
pruner = L1FilterPruner(model, config_list)

本文发布于:2024-09-22 13:32:04,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/1/216568.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:剪枝   模型   压缩   权重   神经   算法   保存   参数
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议