PPQ中bestPractice使用demo

# 调动PPQ的各项功能
import torch
from ppq import *
from ppq.api import *
import torchvision
# 加载校准集与模型
BATCHSIZE = 32
INPUT_SHAPE = [BATCHSIZE,3,224,224]
DEVICE = 'cuda'
PLATFORM = TargetPlatform.TRT_INT8
CALIBRATION = [torch.rand(size=INPUT_SHAPE) for _ in range (32)]
QS = QuantizationSettingFactory.default_setting()
def collate_fn(batch: torch.Tensor) -> torch.Tensor:
	return batch.to(DEVICE)
model = torchvision.models.mobilenet.mobilenet_v2(pretrained=True)
model = model.to(DEVICE)
# 执行首次量化,完成逐层误差分析
with ENABLE_CUDA_KERNEL():
	quantized = quantize_torch_model(
		model=model,calib_dataloader=CALIBRATION,
		calib_steps=32,input_shape=INPUT_SHAPE,
		collate_fn=collate_fn,platform=PLATFORM,setting=QS,
		onnx_export_file='Output/onnx.model',device=DEVICE,verbose=0)
	reports = layerwise_error_analyse(
		graph=quantized,running_device=DEVICE,
		collate_fn=collate_fn,dataloader=CALIBRATION)
# 根据逐层误差情况,将部分难以量化的层直接送到非量化平台
# 在此例子中,解除前十个误差最大的层的量化
# 为了确保量化的精度达到较高的水准,通常只有个别层需要解除量化
# 不要妄图单纯使用调度解决所有精度问题,调度体现了运行效率和网络精度之间的权衡
# 后续还可以通过调解校准算法与量化参数来提升精度
#从小到大排序单层误差
sensitivity = [(op_name,error) for op_name,error in reports.items()]
# sorted() 函数: sorted(iterable, key=None, reverse=False) 
# iterable 表示指定的序列,key 参数可以自定义排序规则;reverse 参数指定以升序(False,默认)还是降序(True)进行排序
sensitivity = sorted(sensitivity,key=lambda x: x[1],reverse=True)
# 将前十个误差最大的层送上FP32
for op_name, _ in sensitivity[ : 10]:
	QS.dispatching_table.append(operation=op_name,platform=TargetPlatform.FP32)
# 选择一个合适的校准算法,最小化量化误差
# 这一过程需要手动调整QS中的校准算法,需要反复执行和对比
# 这里只推荐更改激活值的校准算法,对于参数而言
# 在INT8的量化场景中,minmax往往都是最好的校准策略
# 在这个场景下,不能使用isotone策略
for calib_algo in {'minmax','percentile','kl','mse'}:
	QS.quantize_activation_setting.calib_algorithm = calib_algo
	with ENABLE_CUDA_KERNEL():
		quantized = quantize_torch_model(
			model=model,calib_dataloader=CALIBRATION,
			calib_steps=32,input_shape=INPUT_SHAPE,
			collate_fn=collate_fn,platform=PLATFORM,setting=QS,
			onnx_export_file='Output/onnx.model',device=DEVICE,verbose=0)
		print(f'Error Report of Algorithm {calib_algo}: ')
		reports = graphwise_error_analyse(
			graph=quantized,running_device=DEVICE,
			collate_fn=collate_fn,dataloader=CALIBRATION)
# 在确定一种校准算法之后,你还可以修改ppq.core.common中的相关属性来取得更优结果
# 下列参数将影响校准效果:
	# Observer中hist箱子个数
	# OBSERVER_KL_HIST_BINS=4096
	# Observer中percentile的参数
	# OBSERVER_PERCENTILE = 0.9999
	# Observer中mse校准方法hist箱子的个数
	# OBSERVER_MSE_HIST_BINS = 2048
	# Observer中mse计算的间隔,间隔越小,所需时间越长
	# OBSERVER_MSE_COMPUTE_INTERVAL = 8
# 在完成测试之后,选择一种最为合适的校准算法,此处以percentile为例
QS.quantize_activation_setting.calib_algorithm = 'percentile'
# 再次检查量化误差,如果与预期仍有差距,可以进一步优化过程调节参数
with ENABLE_CUDA_KERNEL():
	# 首先调节equalization算法(分层量化)的参数
	# 调节时关闭lsq_optimization以缩短流程执行时间
	QS.equalization = True # 试试True或False
	# Equalization优化级别,如果选成 1 则不进行多分支拉平,如果选成 2,则进行跨越 add, sub 的多分支拉平;不一定哪一个好,需要自己试试
	QS.equalization_setting.opt_level = 1 # 试试1或2
	#  Equalization 迭代次数,试试 1,2,3,10,100
	QS.equalization_setting.iterations = 10 # 试试3,10,100
	# Equalization 权重阈值,试试 0.5, 2。这是个十分重要的属性,所有小于该值的权重不会参与运算
	QS.equalization_setting.value_threshold = 0.5 # 试试0,0.5,2
	# 之后调节lsq算法(网络微调)的参数
	# 应该只包含计算操作,如果给定,interested_layers中的操作将被检查,如果条件满足,权重、权重比例、激活比例将被训练成mse优化目标。
	# 如果没有给定,每个满足条件的计算操作将被优化。
	QS.lsq_optimization = True # 试试True或False
	# 图分割的块大小
	QS.lsq_optimization_setting.block_size = 4 # 试试1,4,6
	# 对于块输入输出的校验设备
	QS.lsq_optimization_setting.collecting_device = 'cuda' # 如果显存不够就写cpu
	QS.lsq_optimization_setting.is_scale_trainable = True # 试试True或者False
	QS.lsq_optimization_setting.lr = 1e-5 # 试试1e-5,3e-5,1e-4 # 初始化学习率,默认是adam优化器和具有0.1衰减的multistep scheduler
	QS.lsq_optimization_setting.steps = 500 # 试试300,500,2000 # 训练steps
	quantized = quantize_torch_model(
		model=model,calib_dataloader=CALIBRATION,
		calib_steps=32,input_shape=INPUT_SHAPE,
		collate_fn=collate_fn,platform=PLATFORM,setting=QS,
		onnx_export_file='Output/onnx.model',device=DEVICE,verbose=0)
	reports = graphwise_error_analyse(
		graph=quantized,running_device=DEVICE,
		collate_fn=collate_fn,dataloader=CALIBRATION)
# 最后导出模型,并在onnxruntime上完成校验
# TorchExecutor:在ppq/executor/torch.py中
ONNX_OUTPUT_PATH = 'Output/model.onnx'
# 初始化执行引擎
executor,reference_outputs = TorchExecutor(quantized),[ ]
for sample in CALIBRATION:
	# 获取图的执行结果
	reference_outputs.append(executor.forward(collate_fn(sample)))
export_ppq_graph(
	graph=quantized,platform=TargetPlatform.ONNXRUNTIME,
	graph_save_to=ONNX_OUTPUT_PATH)
try:
	import onnxruntime
except ImportError as e:
	raise Exception('Onnxruntime is not installed.')
# onnxruntime.InferenceSession:用于获取一个ONNX Runtime推理器,其参数是用于推理的onnx模型文件。
# 推理器的run方法用于模型推理,其第一个参数为输出张量名的列表,第二个参数为输入值的字典。
# 其中输入值字典的key为张量名,value为numpy类型的张量值
sess = onnxruntime.InferenceSession(ONNX_OUTPUT_PATH,providers=['CUDAExecutionProvider'])
onnxruntime_outputs = [ ]
for sample in CALIBRATION:
	onnxruntime_outputs.append(sess.run(
		output_names=[name for name in quantized.outputs],
		input_feed = {'input.1': convert_any_to_numpy(sample)}))
name_of_output = [name for name in quantized.outputs]
for oidx,output in enumerate(name_of_output):
	y_pred,y_real = [ ], [ ]
	for reference_output,onnxruntime_output in zip(reference_outputs,onnxruntime_outputs):
		y_pred.append(convert_any_to_torch_tensor(reference_output[oidx], device='cpu').unsqueeze(0))
		y_real.append(convert_any_to_torch_tensor(onnxruntime_output[oidx],device='cpu').unsqueeze(0))
	y_pred = torch.cat(y_pred,dim=0)
	y_real = torch.cat(y_real,dim=0)
    # torch_snr_error:在ppq/quantization/measure/norm.py中
	#计算y_pred(tensor)和y_real(tensor)之间的信噪比
	# SNR(pred, real) = (pred - real) ^ 2 / (real) ^ 2
	# 如果x和y是矩阵,矩阵的信噪比误差应该是所有元素的信噪比误差的平均值
	# SNR(pred, real) = mean((pred - real) ^ 2 / (real) ^ 2)
	print(f'Simulating Error For {output}: {torch_snr_error(y_pred=y_pred,y_real=y_real).item() :.4f}')

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值