PPQ中dispatch使用demo

# 该脚本展示如何自由调度算子,实现混合精度推理
import torch
import torchvision
from ppq import *
from ppq.api import *
# 在开始之前,首先设计一个支持混合精度的量化器
# BaseQuantizer在ppq/quantization/quantizer/base.py中
class MyQuantizer(BaseQuantizer):
	# quant_operation_types是一个类型枚举,在这里需要写下所有该量化器需要所需要量化的算子
	@ property
	def quant_operation_types(self) -> set:
		return {'Conv'}
	# 一旦确定了哪些算子需要量化,则需要在init_quantize_config为他们初始化量化信息
	# 对于一个Onnx算子,总会有几个输入和输出Variable,需要为每一个相连的Variable初始化量化信息
	# 麻烦的问题是:对于很多onnx算子而言,他们的部分输入是不需要量化的
	# 如clip算子的三个输入value,min,max,大部分框架不要求量化min,max
	# 如reshape算子的两个输入value,shape,其中shape不能够被量化
	# PPQ的算子接线器中记录了这些信息
	# 算子接线器中记录了所有标注onnx的默认量化策略
	# 该函数将使用预定义的算子量化策略初始化量化信息	
	# 然而需要注意的是,由于手动调度的存在,用户可以强制调度一个类型不在quant_operation_types中的算子来到量化平台
	# 建议针对这种情况进行回应。或者,在探测到算子类型并非可量化后进行报错
	def init_quantize_config(self,operation: Operation) -> OperationQuantizationConfig:
		# 为卷积算子初始化量化信息,只量化卷积算子的输入(input & weight),bias不被量化
		if operation.type == 'Conv':
			config = self.create_defualt_quant_config(
				op = operation,
				num_of_bits = 4,
				quant_max = 15,
				quant_min = -16,
				observer_algorithm = 'percentile',
				# 量化策略:QuantizationProperty.PER_TENSOR->以tensor为单位完成量化,每个tensor使用一个scale和offset信息
				# QuantizationProperty.LINEAR:线性量化,通常INT8,INT16皆属于线性量化,在线性量化的表示中不存在指数位
				# QuantizationProperty.SYMMETRICAL:对称量化,在量化计算中不启用offset
				policy = QuantizationPolicy(
					QuantizationProperty.PER_TENSOR + 
					QuantizationProperty.LINEAR + 
					QuantizationProperty.SYMMETRICAL),
				rounding = RoundingPolicy.ROUND_HALF_EVEN)
			# 关闭所有输出量化,状态设置为fp32
			for  tensor_quant_config in config.output_quantization_config:
				#  QuantizationStates.FP32:表示这一路输入不量化
				tensor_quant_config.state = QuantizationStates.FP32
			# 关闭bias量化,状态设置为fp32
			if operation.num_of_input == 3:
				config.input_quantization_config[-1].state = QuantizationStates.FP32
			# 如果算子调度到INT8平台上,执行INT8量化
			if operation.platform == TargetPlatform.ACADEMIC_INT8:
				print('f{operation.name} has been dispatched to INT8')
				config.input_quantization_config[0].num_of_bits = 8
				# 获取TQC最大量化值
				config.input_quantization_config[0].quant_max = 127
				# # 获取TQC最小量化值
				config.input_quantization_config[0].quant_min = -128
				config.input_quantization_config[1].num_of_bits = 8
				config.input_quantization_config[1].quant_max = 127
				config.input_quantization_config[1].quant_min = -128
			return config
		else:
			raise TypeError(f'Unsupported Op Type: {operation.type}')
		# 当前量化器进行量化的算子都将被发往一个指定的目标平台
		# 这里选择TargetPlatform.ACADEMIC_INT4作为目标平台
		@ property
		def target_platform(self) -> TargetPlatform:
			return  TargetPlatform.ACADEMIC_INT4
#注册量化器
register_network_quantizer(MyQuantizer,platform=TargetPlatform.ACADEMIC_INT4)
# 下面展示PPQ手动调度逻辑
# 以Mobilenetv2举例,展示如何完成混合精度调度
BATCHSIZE = 32
INPUT_SHAPE = [BATCHSIZE,3,224,224]
DEVICE = 'cuda'
PLATFORM = TargetPlatform.ACADEMIC_INT4
CALIBRATION = [torch.rand(size=INPUT_SHAPE) for _ in range(32)]
def collate_fn(batch: torch.Tensor) -> torch.Tensor: return batch.to(DEVICE)
model = torchvision.models.mobilenet.mobilenet_v2(pretrained=True)
model = model.to(DEVICE)
# 为了执行手动调度,必须首先创建QuantizationSetting
# 使用QuantizationSetting.dispatch_table属性来传递调度方案
# 大多数预制量化器没有针对INT4写过量化方案,因此只支持FP32-INT8的相互调度
QS = QuantizationSettingFactory.default_setting()
# 示例语句:下面的语句把Op1调度到FP32平台
# QS.dispatching_table.append(operation='Op1',platform=TargetPlatform.FP32)
# 我如何知道调度哪些层到高精度会得到最优的性能表现?
# layerwise_error_analyse函数正是为此设计,调用该方法
# 选择那些误差较高的层调度到高精度平台
# 首先将所有层送往INT4,然后执行误差分析
with ENABLE_CUDA_KERNEL:
	dump_torch_to_onnx(model=model,onnx_export_file='Output/model.onnx',
			input_shape=INPUT_SHAPE,input_dtype=torch.float32)
	graph = load_onnx_graph(onnx_import_file='Output/model.onnx')
	quantized = quantize_native_model(
		model=graph,calib_dataloader=CALIBRATION,
		calib_steps=32,input_shape=INPUT_SHAPE,
		collate_fn=collate_fn,platform=PLATFORM,
		device=DEVICE,verbose=0,setting=QS)
	# 使用graphwise_analyse衡量调度前的量化误差
	reports = graphwise_error_analyse(
		graph=quantized,running_device=DEVICE,collate_fn=collate_fn,
		dataloader=CALIBRATION)
	# 执行逐层分析,结果是一个字典,该字典内写入了所有算子的单层量化误差
	reports = layerwise_error_analyse(
		graph=quantized,running_device=DEVICE,collate_fn=collate_fn,
		dataloader=CALIBRATION,verbose=False)
	# 从小到大排序单层误差
	sensitivity = [(op_name,error) for op_name,error in reports.items()]
	sensitivity = sorted(sensitivity,key=lambda x: x[1], reverse=True)
	# 将前十个误差最大的层送上INT8,并重新量化
	for op_name, _ in sensitivity[: 10]:
		QS.dispatching_table.append(operation=op_name,platform=TargetPlatform.ACADEMIC_INT8)
	graph = load_onnx_graph(onnx_import_file='Output/model.onnx')
	quantized = quantize_native_model(
		model=graph,calib_dataloader=CALIBRATION,
		calib_steps=32,input_shape=INPUT_SHAPE,
		collate_fn=collate_fn,platform=PLATFORM,
		device=DEVICE,verbose=0,setting=QS)
	# 使用graphwise_analyse衡量最终的量化误差
	reports = graphwise_error_analyse(
		graph=quantized,running_device=DEVICE,collate_fn=collate_fn,
		dataloader=CALIBRATION)

这个demo在注册量化器这行代码报错,现在还没有找到原因,如果有知道原因的朋友希望可以解答一下,感谢!、

更换platform之后demo可以运行,且在官方platform中没有找到ACADEMIC_INT4或者ACADEMIC_INT8。猜测是这个patform的原因。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值