OpenPPL PPQ量化(2):离线静态量化源码剖析

news2024/11/15 22:25:59

目录

模型支持

量化onnx原生模型:quantize_onnx_model

输入输出

执行流程

ONNX格式解析

后记

模型支持

openppl支持了三种模型:onnx、caffe、pytorch,其中pytorch和caffe是通过quantize_torch_model和quantize_caffe_model,先将模型转换成onnx模型,再调用quantize_onnx_model来实现量化的。

@ empty_ppq_cache
def quantize_torch_model(
    model: torch.nn.Module,
    calib_dataloader: DataLoader,
    calib_steps: int,
    input_shape: List[int],
    platform: TargetPlatform,
    input_dtype: torch.dtype = torch.float,
    setting: QuantizationSetting = None,
    collate_fn: Callable = None,
    inputs: List[Any] = None,
    do_quantize: bool = True,
    onnx_export_file: str = 'onnx.model',
    device: str = 'cuda',
    verbose: int = 0,
    ) -> BaseGraph:
   
    # dump pytorch model to onnx
    dump_torch_to_onnx(model=model, onnx_export_file=onnx_export_file,
        input_shape=input_shape, input_dtype=input_dtype,
        inputs=inputs, device=device)

    return quantize_onnx_model(onnx_import_file=onnx_export_file,
        calib_dataloader=calib_dataloader, calib_steps=calib_steps, collate_fn=collate_fn,
        input_shape=input_shape, input_dtype=input_dtype, inputs=inputs, setting=setting,
        platform=platform, device=device, verbose=verbose, do_quantize=do_quantize)
@ empty_ppq_cache
def quantize_caffe_model(
    caffe_proto_file: str,
    caffe_model_file: str,
    calib_dataloader: DataLoader,
    calib_steps: int,
    input_shape: List[int],
    platform: TargetPlatform,
    input_dtype: torch.dtype = torch.float,
    setting: QuantizationSetting = None,
    collate_fn: Callable = None,
    inputs: List[Any] = None,
    do_quantize: bool = True,
    device: str = 'cuda',
    verbose: int = 0,
) -> BaseGraph:
    if do_quantize:
        if calib_dataloader is None or calib_steps is None:
            raise TypeError('Quantization needs a valid calib_dataloader and calib_steps setting.')

    if setting is None:
        setting = QuantizationSettingFactory.default_setting()

    ppq_ir = load_graph(file_path=caffe_proto_file,
                        caffemodel_path=caffe_model_file,
                        from_framework=NetworkFramework.CAFFE)

    ppq_ir = format_graph(ppq_ir)
    ppq_ir = dispatch_graph(ppq_ir, platform, 
                            dispatcher=setting.dispatcher, 
                            dispatching_table=setting.dispatching_table)

    if inputs is None:
        dummy_input = torch.zeros(size=input_shape, device=device, dtype=input_dtype)
    else: dummy_input = inputs

    quantizer = PFL.Quantizer(platform=platform, graph=ppq_ir)
    executor = TorchExecutor(graph=quantizer._graph, device=device)
    executor.tracing_operation_meta(inputs=dummy_input)

    if do_quantize:
        quantizer.quantize(
            inputs=dummy_input,
            calib_dataloader=calib_dataloader,
            executor=executor,
            setting=setting,
            calib_steps=calib_steps,
            collate_fn=collate_fn
        )
        if verbose: quantizer.report()
        return quantizer._graph
    else:
        return quantizer._graph

所以我们接下来看看quantize_onnx_model是怎么实现的。

量化onnx原生模型:quantize_onnx_model

输入输出

        onnx_import_file (str): 被量化的 onnx 模型文件路径 onnx model location

        calib_dataloader (DataLoader): 校准数据集 calibration data loader

        calib_steps (int): 校准步数 calibration steps

        collate_fn (Callable): 校准数据的预处理函数 batch collate func for preprocessing

        input_shape (List[int]): 模型输入尺寸,用于执行 jit.trace,对于动态尺寸的模型,输入一个模型可接受的尺寸即可。
            如果模型存在多个输入,则需要使用 inputs 变量进行传参,此项设置为 None
                                a list of ints indicating size of input, for multiple inputs, please use
                                keyword arg inputs for direct parameter passing and this should be set to None

        input_dtype (torch.dtype): 模型输入数据类型,如果模型存在多个输入,则需要使用 inputs 变量进行传参,此项设置为 None
                                the torch datatype of input, for multiple inputs, please use keyword arg inputs
                                for direct parameter passing and this should be set to None

        inputs (List[Any], optional): 对于存在多个输入的模型,在Inputs中直接指定一个输入List,从而完成模型的tracing。
                                for multiple inputs, please give the specified inputs directly in the form of
                                a list of arrays

        setting (OptimSetting): 量化配置信息,用于配置量化的各项参数,设置为 None 时加载默认参数。
                                Quantization setting, default setting will be used when set None

        do_quantize (Bool, optional): 是否执行量化 whether to quantize the model, defaults to True.


        platform (TargetPlatform, optional): 量化的目标平台 target backend platform, defaults to TargetPlatform.DSP_INT8.

        device (str, optional): 量化过程的执行设备 execution device, defaults to 'cuda'.

        verbose (int, optional): 是否打印详细信息 whether to print details, defaults to 0.

执行流程

我们首先要加载计算图:

    ppq_ir = load_onnx_graph(onnx_import_file=onnx_import_file)

此处加载的计算图是原始的,尚未被调度,也就是所有算子都被认为是可量化的。

然后我们需要执行图的切分与调度,不同算子会被执行不同的调度:

    ppq_ir = dispatch_graph(graph=ppq_ir, platform=platform, 
                            dispatcher=setting.dispatcher, 
                            dispatching_table=setting.dispatching_table)

所有对计算图执行的操作,最后都会返回BaseGraph类,这个类是PPQ内部专门为模型量化准备的计算图,除了保存一般计算图的必要信息之外,还存储了所有量化信息。后面在写博客解析这个量化计算图的设计。

然后根据指定的平台platform确定指定的量化类:

    quantizer = PFL.Quantizer(platform, ppq_ir)

所有的平台类型写在ppq/lib/common.py文件中:

这些具体量化方法写在quantizer文件夹中,传入量化计算图是因为这些量化类需要计算图进行初始化:

因为我们已经初始化了量化类,所以后面表示计算图不再使用ppq_ir,直接用quantizer._graph表示。我们继续要用量化图初始化执行引擎,这个引擎TorchExecutor能执行onnx的推理,由于不同平台的推理细节是不同的,所以这里的实现有点复杂,大致的流程如下:

详细的解析后面专门再写博客讲吧。

好了继续回到我们的主逻辑中,最后一步是执行量化,返回量化后的量化计算图,搞定~

    if do_quantize:
        quantizer.quantize(
            inputs=dummy_input,
            calib_dataloader=calib_dataloader,
            executor=executor,
            setting=setting,
            calib_steps=calib_steps,
            collate_fn=collate_fn
        )
        if verbose: quantizer.report()
        return quantizer._graph
    else:
        executor = TorchExecutor(graph=ppq_ir, device=device)
        executor.tracing_operation_meta(inputs=

最后注意这里如果不需要执行量化,我们用没有原始载入的计算图执行一遍推理,然后返回即可。

ONNX格式解析

如果不了解ONNX格式,前面从ONNX解析出计算图部分会比较难理解,有一篇写的很棒的博客,我摘抄了一部分帮助理解:ONNX学习笔记 - 知乎

这一节我们来分析一下ONNX的组织格式,上面提到ONNX中最核心的部分就是onnx.protohttps://github.com/onnx/onnx/blob/master/onnx/onnx.proto)这个文件了,它定义了ONNX这个数据协议的规则和一些其它信息。现在是2021年1月,这个文件有700多行,我们没有必要把这个文件里面的每一行都贴出来,我们只要搞清楚里面的核心部分即可。在这个文件里面以message关键字开头的对象是我们需要关心的。我们列一下最核心的几个对象并解释一下它们之间的关系。

  • ModelProto
  • GraphProto
  • NodeProto
  • ValueInfoProto
  • TensorProto
  • AttributeProto

当我们加载了一个ONNX之后,我们获得的就是一个ModelProto,它包含了一些版本信息,生产者信息和一个GraphProto。在GraphProto里面又包含了四个repeated数组,它们分别是node(NodeProto类型),input(ValueInfoProto类型),output(ValueInfoProto类型)和initializer(TensorProto类型),其中node中存放了模型中所有的计算节点,input存放了模型的输入节点,output存放了模型中所有的输出节点,initializer存放了模型的所有权重参数。

我们知道要完整的表达一个神经网络,不仅仅要知道网络的各个节点信息,还要知道它们的拓扑关系。这个拓扑关系在ONNX中是如何表示的呢?ONNX的每个计算节点都会有inputoutput两个数组,这两个数组是string类型,通过inputoutput的指向关系,我们就可以利用上述信息快速构建出一个深度学习模型的拓扑图。这里要注意一下,GraphProto中的input数组不仅包含我们一般理解中的图片输入的那个节点,还包含了模型中所有的权重。例如,Conv层里面的W权重实体是保存在initializer中的,那么相应的会有一个同名的输入在input中,其背后的逻辑应该是把权重也看成模型的输入,并通过initializer中的权重实体来对这个输入做初始化,即一个赋值的过程。

最后,每个计算节点中还包含了一个AttributeProto数组,用来描述该节点的属性,比如Conv节点或者说卷积层的属性包含grouppadstrides等等,每一个计算节点的属性,输入输出信息都详细记录在https://github.com/onnx/onnx/blob/master/docs/Operators.md

后记

关于如何做量化校准?如果使用校准数据?如何配置量化设置?具体的量化过程是如何?如何选择需要量化的算子?……

还有很多问题没有讲明白,这个系列很长,我们一一探索!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/193057.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Elasticsearch:Terms set 查询

什么是 terms set 查询? Terms set 查询根据匹配给定字段的精确术语的最少数量返回文档。 terms set 查询与 term 查询有何不同? Terms set query 和 Terms query 之间的唯一区别是你可以提供必须匹配的最少数量的术语才能检索特定文档。 什么是 minim…

【Ansys Meshing】Fluent Meshing和Ansys Meshing在划分边界层网格能力上的对比

一、几何模型展示 如下图所示,一端的圆柱是流体入口,另一端的圆柱是流体出口,中间都是导热管。 二、在spaceclaim中进行切割实体 2.1 切割手段 切割平面的位置如图两根线所示,最终得到左右两边两个有圆柱的大块,以…

【SAP Abap】X档案:SAP 快速提供基础数据给第三方系统访问的几种方法(附常用基础数据)

SAP 快速提供基础数据给第三方系统访问的几种方法1、数据封装2、开放RFC访问3、开放接口服务4、开放DB访问5、常用基础数据1、数据封装 在企业信息系统建设过程中,少不了的就是系统集成数据对接。 尤其是SAP系统中大量的基础数据集成,如各种字段值域&am…

C语言:操作符详解

往期文章 C语言:初识C语言C语言:分支语句和循环语句C语言:函数C语言:数组 目录往期文章前言1. 操作符分类2. 算术操作符3. 移位操作符4. 位操作符5. 赋值操作符6. 符合赋值符7. 单目操作符8. 关系操作符9. 逻辑操作符10. 条件操作…

Java 中的基本设计模式

设计模式是针对常见软件设计问题的可重用解决方案。它们提供了一种以一致且高效的方式组织和构建代码的方法。一些常见的设计模式包括:工厂模式是一种创建型设计模式,它提供用于在超类中创建对象的接口,但允许子类更改将要创建的对象的类型。…

【数据结构趣味多】优先级队列——堆

1. 优先级队列 概念: 队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列,该中场景下,使用队列显然不合适,比…

前端打包后生成的dist 或 build目录,如何在本地启动服务运行

前端打包后生成的dist/build目录,如何在本地启动服务运行 运行npn run build,会打包后会产生 dist 或 build 目录 一般情况下,直接打开dist 或 build 目录下的 index.html会在浏览器看到内容。 然而发现网页一片空白,打开了控制台…

C++基础入门(引用补充)

1、使用场景做参数void Swap(int& left, int& right) {int temp left;left right;right temp; }做返回值int& Count() {static int n 0;n;// ...return n; }c语言内,出了count函数,n被销毁,会创建临时变量存储其值&#xff0…

DETR——使用Transformer进行端到端目标检测的开端之作

深度学习知识点总结 专栏链接: https://blog.csdn.net/qq_39707285/article/details/124005405 此专栏主要总结深度学习中的知识点,从各大数据集比赛开始,介绍历年冠军算法;同时总结深度学习中重要的知识点,包括损失函数、优化器…

寻根究底,为什么Docker中的Alpine Linux镜像能这么小

去年我发表了文章对Docker基础镜像的思考,该不该选择alpine,其中对于Alpine Linux镜像如此之小的原因我解释为它使用了musl而不是glibc 有人发现并指出了我的这个错误,说musl与glibc的大小差别不足以造成如此大的差距,应该别有原…

C++-静态局部变量

作者:翟天保Steven 版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处 特征: 1.需添加关键字static。 2.在静态存储区分配内存,始终存在直到程序结束。 3.作用域为局部&#…

阿⾥云Apsara Clouder云计算专项技能认证:云服务器ECS⼊门【 个⼈所得税年度应纳税额抵扣 3600(0成本)】

文章目录 引言I 云服务器ECS⼊门II 考试III 个⼈所得税app填写专项附加扣除引言 适合⼈群:需要交个⼈所得税的上班族,有⼀定计算机基础结果:⼯资年收⼊10-20万的,能省下360元(3600*10%)I 云服务器ECS⼊门 云服务器(Elastic Compute Service, 简称ECS),是一种简单高效,…

PDPS教程:机器人气动点焊焊枪大开与小开运动状态自动切换设置

目录 概述 气动点焊焊枪运动状态设置 机器人气动点焊焊枪工具类型定义 气动点焊焊枪运动状态切换原理 气动点焊焊枪大开与小开状态切换设置 机器人仿真运行 概述 工业机器人点焊焊接过程中,为了提高焊接效率、优化焊接节拍、降低能源消耗,通常会在…

2022尚硅谷SSM框架跟学(八)Spring MVC基础三

2022尚硅谷SSM框架跟学 八 Spring MVC基础三8.RESTful案例8.1准备工作8.2功能清单8.3.具体功能:访问首页(1).配置view-controller(2).创建页面8.4具体功能:查询所有员工数据(1).控制器方法(2).创建employee_list.html8.5具体功能:删除(1).创建…

企业电子招投标采购系统源码之功能模块功能描述

​ 功能模块: 待办消息,招标公告,中标公告,信息发布 描述: 全过程数字化采购管理,打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力,为外…

激光焊接/点焊的特性及优势分析?

目前应用于生产的点焊方式大多为电阻点焊、电弧点焊、激光点焊和胶接点焊等多种点焊方法。其中激光焊接是激光材料加工技术应用的重要方面之一。 激光点焊机主要由激光器、电源及控制、冷却机、导光及调焦、双目体视显微观察几部分构成,结构紧凑,体积小…

Android中对图片的操作,移动、缩放、涂鸦和保存到图库

一、实现方法 监听用户手势,提取用户操作 (1)移动: 分别计算X,Y轴的结束与初始之间移动偏移的量 (2)缩放:(结束两指间距离伸缩比例)/ 初始两指间距离,scaleX…

云计算|OpenStack|社区版OpenStack安装部署文档(五 --- 计算服务nova安装部署---Rocky版)

前言: nova服务是openstack最重要的一个组件,没有之一,该组件是云计算的计算核心,大体组件如下: OpenStack Docs: Compute service overview 挑些重点,nova-api,libvirt,nova-pla…

最小生成树与最短路径

目录 一.最小生成树 1.1概念 1.2Kruskal算法 1.3Prim算法 二.最短路径 2.11单源最短路径--Dijkstra算法 2.1.2单源最短路径--Bellman-Ford算法 一.最小生成树 1.1概念 连通图中的每一棵生成树,都是原图的一个极大无环子图,即:从其中删去…

虹科分享|论企业网络安全的重要性

拥有有效的企业网络安全不仅仅是让你的员工创建一个不是他们宠物名字的密码--除非他们的猫的名字至少有12个字符长,由大小写字母和符号组成。无论是经过充分研究的鱼叉式钓鱼尝试,还是绕过MFA,威胁者都变得更加大胆。随着全球各行业数据泄露事…