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官网)相关推荐

  1. 王者荣耀服务器维修多久,王者荣耀服务器正在维护中,具体维护时间及更新内容可留意官网更新公告_游戏吧...

    王者荣耀经常会进行更新,但是有事明明是不停机更新,却出现了王者荣耀服务器正在维护中,具体维护时间及更新内容可留意官网更新公告,很多玩家都不知道到底是怎么回事.下面游戏小编就为各位玩家带来了解决方法. ...

  2. 验证Xcode真伪的方法,来自苹果官网

    验证Xcode真伪的方法,来自苹果官网 Xcode的验证你的版本 2015年9月22日  注意:中文为有道翻译,看下验证方法即可. 我们最近将应用程序从应用程序商店,还建有Xcode的假冒版本有可能对 ...

  3. Python爬取网页所需内容+王者荣耀官网

    目标: 完成对王者荣耀游戏的所有英雄头像.皮肤等数据的内容爬取及图片下载,所涉及到的模块内容有requests.json.lxml.selenium.os等.王者荣耀英雄官网地址如下:https:// ...

  4. 资源站上新与更新内容!/苹果官网偷跑4.7寸新机

    反反复复在爆料中出现的所谓"iPhone 9/SE 2",最终在苹果官网意外偷跑. 网友意外发现,苹果Apple Store美国官网于当地时间本周四晚悄然更新了贝尔金牌iPhone ...

  5. android 读取资源中的文件下载,android – 从APK扩展文件中读取内容(来自obb文件)...

    我已经实现了APK扩展文件下载服务,全部来自 http://developer.android.com/google/play/expansion-files.html 我可以下载APK扩展文件,我可 ...

  6. Building a Restful Web Service(最好的Spring入门教程 --来自Spring官网的Guides)

    搭建一个Restful Web服务 本文档将引导你用Spring搭建一个"Hello,World!"Restful Web服务. 你要搭建的是什么? 你将搭建一个Web服务,这个服 ...

  7. java基础恶补1(基础)(内容来自牛客网Java宝典)

    1.java代码为什么可以实现一次编写,到处运行 参考答案: JVM(java虚拟机)是java跨平台的关键. 在程序运行前,java源代码(.java)需要经过编译器编译成字节码(.class).在 ...

  8. 来自 Qt 官网的呐喊

    星标/置顶 公众号

  9. 2020中国全国各省市,三级联动数据,数据机构(数据来自国家统计局官网)

    联动的数据结构,可以参考 // A code blocklist: [{value: 1,label: '中国',children: [{value: 2,label: '广东',children: ...

最新文章

  1. 第六十二课、单例类模板
  2. 万字大白话带你重拾JVM
  3. 我这样理解技术人的成长过程
  4. Ubuntu9.10 server 安装配置 vsftpd2.2.0 ftp服务器 并且 解决 putty 登陆 sshd 显示中文乱码
  5. 【APP】centos 5.X安装网络和系统监控管理平台--OpenNMS
  6. php打印负载函数、Linux awk打印负载
  7. nginx转发websocket
  8. 一个程序猿如何自学双截棍,避免各种编程职业病的发生?
  9. 地理文本处理技术在高德的演进(上)+
  10. 24种设计模式--命令模式【Command Pattern】
  11. Objective-c 中 nil, Nil, NULL和NSNull的区别
  12. [框架][MyBatis]MyBatis集锦
  13. 优秀的程序员是这样的
  14. Ubuntu16.04+NVIDIA显卡驱动安装步骤
  15. php5.6软件下载,【PHP下载】PHP for Linux 5.6.6-ZOL软件下载
  16. centos7校正系统时间
  17. mysql salve从库设置read only 属性
  18. OD CE找数据总结(上)
  19. h5调用指纹识别_基于HTML Canvas实现“指纹识别”技术,canvas指纹
  20. 模拟器也可以使用摄像头,不用真机也可以测试摄像头程序 ,

热门文章

  1. 一个简单的各异向性滤波实现和应用
  2. 【Altium Designer】PCB设计中利用board cutout做板子开孔开槽
  3. 华为p50 pro 鸿蒙,华为P50 Pro外观基本确认:居中开孔全面屏首发鸿蒙操作系统
  4. LITS数据集预处理(二)
  5. 英特尔“闪电”换帅,新任掌门下月接棒(附内部信)
  6. [转]陌陌不再寂寞:上线1年用户突破1000万
  7. 《快活帮》第四次作业:项目需求调研与分析
  8. html中添加空格站位样式
  9. HDU 2201 JAVA
  10. 7个Linux手机平台比较 (4):多媒体架构的异同