PPQ.executor中的内容(来自PPQ官网)
PPQ Graph Executor(PPQ执行引擎)
为了量化并优化神经网络模型,PPQ实现了基于Pytorch的执行引擎,该执行引擎能够执行Onnx与Caffe的模型文件,目前支持90余种常见Onnx算子,涵盖1d,2d,3d视觉、语音、文本模型。PPQ的执行引擎位于ppq.executor目录下,有两个主要部分组成:ppq.executor.torch.py文件中包含了执行引擎自身;PPQ.executor.op文件夹中则包含了不同后端的算子库。
PPQ Backend Functions(PPQ算子库)
核心算子库位于ppq.executor.op.torch.default文件中,该文件中包含了所有算子默认的执行逻辑。
对于一个量化算子而言,由于硬件的不同其执行逻辑也可能发生变化。例如LeakyRelu算子的负数部分在GPU上会采用x*alpha的方法完成计算,而在FPGA则会采用x=x>>3完成计算。正是因为这种差异的存在,PPQ允许相同的算子在不同的平台(TargetPlatform)上拥有不同的执行逻辑。这也意味着针对每一个平台,都将实现一个平台独特的算子库文件,这些算子库都继承于ppq.executor.op.torch.default。
def Mul_forward(op:Operation,values:List[torch.Tensor],ctx:TensorBackendContext=None,**kwargs) -> torch.Tensor:"""Performs element-wise binary multiplication(with Numpy-style broadcasting support).This oparator supports multidirectional(i.e.,Numpy-style) broadcasting;for more details please check the doc.(Opset 14 change): Extend supported types to include uint8, int8, uint16, and int16."""ASSERT_NUM_OF_INPUT(op=op,values=values,min_num_of_input=2,max_num_of_input=2)values = VALUE_TO_EXECUTING_DEVICE(op=op,ctx=ctx,values=values)multiplicand,multiplier = valuesreturn multiplicand * multiplier
上文中的内容即ppq.executor.op.torch.defualt中Mul算子的执行逻辑,在PPQ中,所有算子在执行时都将接受一系列torch.Tensor作为输入,而后调用pytorch完成算子的计算逻辑。可以打开PPQ的算子库文件查看其它算子的执行逻辑,并且PPQ也提供了register_operation_handler函数,借助该函数可以注册自定义算子的执行逻辑;或是覆盖现有算子的执行逻辑。
def register_operation_handler(handler:Callable,operation_type:str,platform:TargetPlatform):"""Register a custimized function as operation handler.Function should accept at least 3 input parameters,return one or more tensor as result:func(op:Operation,values:List[torch.Tensor],ctx:TorchBackendContext=None,**kwargs) -> torch.Tensor:If there is already another operation handler for given operation_type,new handler will replace the old one without warring."""if platform not in GLOBAL_DISPATCHING_TABLE:raise ValueError('Unknown Platform detected,Please check your platform setting.')GLOBAL_DISPATCHING_TABLE[platform][operation_type] = handler
该函数位于ppq.api,你可以使用语句from ppq.api import register_operation_handler来引入它。
PPQ Executor(PPQ执行引擎)
接下来将介绍PPQ执行引擎TorchExecutor,可以使用语句from ppq import TorchExecutor导入执行引擎。初始化执行引擎则需要传入一个PPQ计算图实例对象,在这里假设已经获取到了一个量化后的计算图对象ppq_quant_ir,并使用下面的语句初始化计算引擎:
executor = TorchExecutor(graph=ppq_quant_ir)
executor.forward(inputs=...,output_names=...,hooks=...)
使用executor.forward接口获取图的执行结果,它将可以传入三个参数:
- inputs:inputs(Union[dict,list,torch.Tensor]):[input tensors or somewhat]
- output_names(List[str],optional):output variable names.default is None.
- hooks(Dict[str,RuntimeHook],optional):A hook table for customizing operation bahaviour and collate data during executing.
当执行引擎获取到推理请求时,它将按拓扑顺序依次执行图中的算子:下面展示了一个简单的示例:
在这里,图中包含三个算子Conv,Relu,Softmax,他们将按照拓扑次序被依次执行。PPQ的执行引擎会在执行完Conv算子后,将Conv算子的结果暂存于Var1中,供Relu算子取用。而在执行完Relu算子后,PPQ执行引擎则会及时地释放Var1中暂存的数据,因为它们不会被其他算子取用,而且也不是网络的输出Variable。在每一次推理过后,PPQ还会清空网络中所有的暂存变量以释放显存。下面代码段展示了一个非量化算子的执行逻辑:
for operation in executing_order:outputs = operation_forward_func(operation,inputs,self._executing_context)outputs = outputs if isinstance(outputs,(list,tuple)) else [outputs]fp_outputs = outputsfor output_idx,output_var in enumerate(operation.outputs):output_var = operation.outputs[ouput_idx]output_var.value = outputs[output_idx]
# clear all variable(static clear).
for var in self._graph.variables.values():if not var.is_parameter:var.value = None
PPQ的执行引擎是专为量化计算图的设计执行的-接下来深入到量化算子的执行过程中去。对于一个量化算子而言,其每一个输入和输出变量都会有一个Tensor Quantization Config(TQC)控制结构体对量化过程进行描述。
对于一个量化Conv算子而言,PPQ将为它创建2-3个Input TQC,以及一个Output TQC。分别对其输入变量以及输出变量的量化行为进行描述。下面的代码展示了如何为量化算子创建特定的TQC描述量化逻辑。
if operation.type == 'Conv':config = self.create_default_quant_config(op = operation,num_of_bits = 8,quant_max = 127,quant_min = -128,observer_algorithm = 'percentile',policy = QuantizationPolicy(QuantizationProperty.PER_TENSOR + QuantizationProperty.LINEAR +QuantizationProperty.SYMMETRICAL),rounding = RoundingPolicy.ROUND_HALF_EVEN)# 关闭所有输入量化,状态设置为fp32for tensor_quant_config in config.input_quantization_config:tensor_quant_config,state = QuantizationStates.FP32operation.config = config
在下图中,展示了PPQ执行引擎对于量化算子的执行逻辑:
在PPQ中,算子的执行被分为四个过程:
- 首先PPQ将根据算子上的TQC信息量化算子的输入。量化过程并非是原地的,量化后的数据会是一个新的torch.Tensor。
- 随后PPQ在算子库中寻找算子的执行逻辑,已经提到对于每一个平台,都可以拥有自己的一套独立的算子库。PPQ将按照算子的平台找到特定的计算逻辑,并调用他们完成计算得到结果。
- PPQ将根据算子上的TQC信息量化算子的输出。同样地,输出的量化也不是原地的。
- 最后将量化好的结果写入到计算图的Variable上,从而供后续的算子取用。
对于一个非量化算子而言,上述步骤中的1,3是可以省略的。
下图展示了一个量化卷积算子与TQC之间的关系:
Quantize Delegate(量化代理函数)
PPQ允许为网络中特定的TQC注册量化代理函数。这样就可以注册自定义的量化处理逻辑,而非使用PPQLinearQuantFunction完成量化。
def register_quantize_delegate(self,config:TensorQuantizationConfig,delegator:TorchQuantizeDelegator):
使用executor.register_quantize_delegate(config,function)完成函数注册,被注册的函数必须满足TorchQuantizeDelegator所定义的接口。下面给出一个简单的量化代理函数例子:
class MyQuantDelegator(TorchQuantizeDelegator):"""Use This class to realize your quantization logicInherit class TorchQuantizeDelegate,implement interface _call_,thenregister your delegator with executor.register_quantize_delegate"""def _call_(self,tensor:torch.Tensor,config:TensorQuanttizationConfig) -> torch.Tensor:if config.policy.has_property(QuantizationProperty.ASYMMETRICAL):raise ValueError('Sorry,this delegator handles only Symmetrical Quantizations.')print('You are invoking custimized quant function now.')return torch.round(tensor / config.scale) * config.scale
在执行器遇到TQC时,将会调用executor.quantize_function执行量化,其逻辑为:
def quantize_function(self,tensor:torch.Tensor,config:TensorQuantizationConfig=None) -> torch.Tensor:if config is None or not QuantizationStates.is_activated(config.state): return tensorelif config in self._delegates: return self._delegates[config](tensor,config)else:if config.policy.has_property(QuantizationProperty.DYNAMIC):else:return self._default_quant_fn(tensor,config)
Usage(用法示例)
PPQ的执行器初始化需要一个计算图示例作为参数:
executor = TorchExecutor(graph=ppq_quant_ir)
executor.forward(inputs=...,output_names=...,hooks=...)
这一计算图可以是量化过后的,也可以是没有量化的。但PPQ希望传入的计算图经过正确的调度,传入没有调度的计算图将会触发警报:
if not graph.extension_attrib.get(IS_DISPATCHED,False):ppq_warning('Can not create executor with your graph,graph is not correctly dispatched,''use dispatch_graph(graph=ir,platform=platform,setting=setting) first.')
executor.forward需要三个参数,下面举例对其进行说明:
# 传入三个变量a,b,c作为输入
executor.forward(inputs=[a,b,c],output_names=...,hooks=...)
# 分别对图中input,var1两个变量传入a,b作为输入
executor.forward(inputs={'input':a,'var1':b},output_names=...,hooks=...)
# 传入一个完整的tensor作为输入
executor.forward(inputs=torch.zeros(shape=[1,3,224,224]),output_names=...,hooks=...)
# 要求网络输出output,Var1的值
executor.forward(inputs=...,output_names=['output1','Var1'],hooks=...)
executor.forward函数默认不需要梯度,如果希望执行带有梯度的网络,需要使用executor.forward_with_geadient函数。forward函数的返回值永远是一个torch.Tensor数组,其中元素的顺序由output_names参数决定。
Hook(执行钩子函数)
在调用executor.forward函数时可以传入hooks参数。钩子函数是注册在op上的,可以传入一个字典用来说明调用的钩子函数:字典{‘Conv1’:myhook}说明了希望在算子Conv1的执行器件调用钩子函数myhook。
钩子函数必须继承于RuntimeHook类,必须实现成员函数pre_forward_hook,post_forward_hook。在这两个函数中,可以制定特定的逻辑修改算子输入输出的值。
class RuntimeHook(metaclass=ABCMeta):"""RuntimeHook is an abstract class designed for executor customizing."""def _init_(self,operation:Operation) -> None:self._hook_to = operationdef pre_forward_hook(self,inputs:list,**kwargs) -> list:return inputsdef post_forward_hook(self,outputs:list,**kwargs) -> list:return outputs
PPQ.executor中的内容(来自PPQ官网)相关推荐
- 王者荣耀服务器维修多久,王者荣耀服务器正在维护中,具体维护时间及更新内容可留意官网更新公告_游戏吧...
王者荣耀经常会进行更新,但是有事明明是不停机更新,却出现了王者荣耀服务器正在维护中,具体维护时间及更新内容可留意官网更新公告,很多玩家都不知道到底是怎么回事.下面游戏小编就为各位玩家带来了解决方法. ...
- 验证Xcode真伪的方法,来自苹果官网
验证Xcode真伪的方法,来自苹果官网 Xcode的验证你的版本 2015年9月22日 注意:中文为有道翻译,看下验证方法即可. 我们最近将应用程序从应用程序商店,还建有Xcode的假冒版本有可能对 ...
- Python爬取网页所需内容+王者荣耀官网
目标: 完成对王者荣耀游戏的所有英雄头像.皮肤等数据的内容爬取及图片下载,所涉及到的模块内容有requests.json.lxml.selenium.os等.王者荣耀英雄官网地址如下:https:// ...
- 资源站上新与更新内容!/苹果官网偷跑4.7寸新机
反反复复在爆料中出现的所谓"iPhone 9/SE 2",最终在苹果官网意外偷跑. 网友意外发现,苹果Apple Store美国官网于当地时间本周四晚悄然更新了贝尔金牌iPhone ...
- android 读取资源中的文件下载,android – 从APK扩展文件中读取内容(来自obb文件)...
我已经实现了APK扩展文件下载服务,全部来自 http://developer.android.com/google/play/expansion-files.html 我可以下载APK扩展文件,我可 ...
- Building a Restful Web Service(最好的Spring入门教程 --来自Spring官网的Guides)
搭建一个Restful Web服务 本文档将引导你用Spring搭建一个"Hello,World!"Restful Web服务. 你要搭建的是什么? 你将搭建一个Web服务,这个服 ...
- java基础恶补1(基础)(内容来自牛客网Java宝典)
1.java代码为什么可以实现一次编写,到处运行 参考答案: JVM(java虚拟机)是java跨平台的关键. 在程序运行前,java源代码(.java)需要经过编译器编译成字节码(.class).在 ...
- 来自 Qt 官网的呐喊
星标/置顶 公众号
- 2020中国全国各省市,三级联动数据,数据机构(数据来自国家统计局官网)
联动的数据结构,可以参考 // A code blocklist: [{value: 1,label: '中国',children: [{value: 2,label: '广东',children: ...
最新文章
- 第六十二课、单例类模板
- 万字大白话带你重拾JVM
- 我这样理解技术人的成长过程
- Ubuntu9.10 server 安装配置 vsftpd2.2.0 ftp服务器 并且 解决 putty 登陆 sshd 显示中文乱码
- 【APP】centos 5.X安装网络和系统监控管理平台--OpenNMS
- php打印负载函数、Linux awk打印负载
- nginx转发websocket
- 一个程序猿如何自学双截棍,避免各种编程职业病的发生?
- 地理文本处理技术在高德的演进(上)+
- 24种设计模式--命令模式【Command Pattern】
- Objective-c 中 nil, Nil, NULL和NSNull的区别
- [框架][MyBatis]MyBatis集锦
- 优秀的程序员是这样的
- Ubuntu16.04+NVIDIA显卡驱动安装步骤
- php5.6软件下载,【PHP下载】PHP for Linux 5.6.6-ZOL软件下载
- centos7校正系统时间
- mysql salve从库设置read only 属性
- OD CE找数据总结(上)
- h5调用指纹识别_基于HTML Canvas实现“指纹识别”技术,canvas指纹
- 模拟器也可以使用摄像头,不用真机也可以测试摄像头程序 ,