Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to export onnx and save quantized onnx model? #68

Closed
lucasjinreal opened this issue Apr 20, 2022 · 54 comments
Closed

how to export onnx and save quantized onnx model? #68

lucasjinreal opened this issue Apr 20, 2022 · 54 comments

Comments

@lucasjinreal
Copy link

Does there any runnable example?

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 20, 2022

哦豁,你需要注意到大部分后端其实不需要一个quantized模型,大部分后端只需要我们提供量化参数。也就是通过量化过程将生成一个浮点权重的模型文件+一个量化参数文件,你的后端框架需要这两个东西作为输入。当然有一些后端把量化参数写在模型里面。

如果你已经看了教学视频,那么你会发现我们使用Targetplatform枚举来确定导出平台,请知悉:
如果你选择tensorRT作为导出平台,则我们将直接导出量化后的engine文件,tensorRT可以直接运行;
如果你选择ppl系列作为导出平台,则我们将优化后的onnx文件以及json格式的量化参数,ppl需要这两个东西以运行量化模型;
如果你选择snpe作为导出平台,量化参数将被写入caffe proto文件,snpe好像...可以直接运行;
如果你选择metax作为导出平台,他们还在写后端框架所以短时间内可能不能执行;
如果你选择tengine作为导出平台,他们还在写接口来读取ppq的输入,所以好像也不能执行;
如果你选择nxp作为导出格式,量化参数将被写入onnx,nxp可以直接运行;
如果你选择onnxruntime作为导出格式,我们将在网络中插入quant以及dequant节点,onnxruntime可以直接运行。
如果你选择onnxruntime OOS作为导出格式,我们将在网络中插入quant以及dequant节点,并且置换原有计算节点,onnxruntime可以直接运行。
如果你选择onnx作为导出格式,我们将导出一个ppq原生的格式,这个格式只是用来debug的。

如果你想最快速的看到结果,选择onnxruntime作为导出格式即可,你就可以在导出的onnx中看到量化结果。

所有导出平台被列举在ppq.api.文件中:
EXPORTERS = {
TargetPlatform.PPL_DSP_INT8: PPLDSPCaffeExporter,
TargetPlatform.PPL_DSP_TI_IN8: PPLDSPTICaffeExporter,
TargetPlatform.PPL_CUDA_INT8: PPLBackendExporter,
TargetPlatform.SNPE_INT8: SNPECaffeExporter,
TargetPlatform.NXP_INT8: NxpExporter,
TargetPlatform.ONNX: OnnxExporter,
TargetPlatform.ONNXRUNTIME: ONNXRUNTIMExporter,
TargetPlatform.CAFFE: CaffeExporter,
TargetPlatform.NATIVE: NativeExporter,
TargetPlatform.EXTENSION: ExtensionExporter,
TargetPlatform.ORT_OOS_INT8: ORTOOSExporter,
TargetPlatform.METAX_INT8_C: ONNXRUNTIMExporter,
TargetPlatform.METAX_INT8_T: ONNXRUNTIMExporter,
}

@ZhangZhiPku
Copy link
Collaborator

在你完成量化后,你会获得一个获得量化好的计算图,此时调用函数

export(working_directory=WORKING_DIRECTORY, quantized=quantized, platform=PLATFORM)

或者

export_ppq_graph(graph=quantized, platform=PLATFORM, graph_save_to='Output/quantized(onnx).onnx', config_save_to='Output/quantized(onnx).json')

均可以完成图的导出工作,注意写一个正确的PLATFORM,ppq会根据你的平台确定导出方案。
如果你正工作在我们目前不支持的平台上,你可以使用TargetPlatform.ONNXRUNTIME来导出一个标准的ONNX量化模型,或者TargetPlatform.ONNX来导出标准的量化参数文件,而后在思考向后端框架传递量化信息。或者你也可以去覆写TargetPlatform.EXTENSION的导出逻辑,从而实现特定结构的导出。

@lucasjinreal
Copy link
Author

lucasjinreal commented Apr 21, 2022

@ZhangZhiPku 感谢大佬的回复。我向请教几个弱智的问题阿:

  1. 我主要关心两个target,ORT和TensorRT,ORT支持保存为QOperator的格式吗(即直接替换成量化算子,模型体积会减小)?支持QDQ格式吗?如果支持QDQ格式,貌似TensorRT可以直接读取QDQ.
  2. 就ORT的后端而言,跟ONNXRuntime里面的quantization有什么区别吗?我尝试在ONNXRuntime上量化一些稍微复杂的模型,例如一个transformer的实例分割,float32的onnx很正常,ORT的量化会离奇的分割我的图导致无法inference. 不知道pqq是否有这个问题?(对复杂图的支持情况)
  3. 是否支持pytorch1.11?

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 21, 2022

  1. 如果你要保存成qopertor的格式,这个你需要选择平台ORT_OOS_INT8,这样里面的算子会被替换成QOperator。不过你可以试一下即使保存成ONNXRUNTIME,不替换里面的算子它体积还是能变小.. 这两种格式都是QDQ格式,然而tensorRT需要一种很特殊的QDQ格式,当然我们已经帮你写好了,直接使用TensorRT作为导出平台即可,会输出特定的QDQ格式并且帮你转换engine(如果你装了tensorRT的话),你可以在TRTExporter里面看到相关内容。
  2. 复杂图需要图调度需要后端框架和量化工具共同支持,我不知道Onnxruntime里面量化咋写的,对于复杂的图而言,支持情况比较好的后端框架包括TensorRT, OpenVINO, OpenPPL,如果你的模型很复杂,你可能最终只能在这三个平台上完成部署,只能在cpu, gpu上运行。在量化工具方面,ppq支持能力很强,也应该是唯一能够处理复杂图调度的量化工具,我们的下一期视频也会介绍这方面的内容,希望能够更好地解答你的问题...
  3. 好像我们torch 1.5~torch 1.11 都用过,低版本的torch可能会有一些函数写法不兼容的问题,高版本好像一般没啥问题。我们主要使用torch里面的函数执行算子计算,我们不依赖于torch的计算图,量化的计算也不依赖于torch,如果你开了USING_CUDA_KERNELS,这些函数都是我们手写的,它不受torch版本影响,而且它比torch里面的还快不少。

@lucasjinreal
Copy link
Author

@ZhangZhiPku 牛阿,大佬。这么看来应该是开源的支持度最广的量化框架了。我之所以问最后一个问题,是因为不同版本的pytorch导出onnx的逻辑都不一样,如果我们依赖于onnx去做中间件基本上很难做到多版本都能跑,可能在torch1.10没问题,到torch1.11就崩了,我目前魔改的mqbench就是这么个蛋疼的状况,不知道ppq是否也是走的这么个路子?如果是是如何规避这个问题的?

另外我放眼望去,没有看到任何demo 阿,不知道能否给个能跑的简单模型和复杂模型例子? 例如一个resnet18,和一个检测模型的例子?

照着这个例子我想是一下实例分割(sparseinst)一个相对来说比较友好的简单的实例分割,也好给你们反馈一个复杂模型下的ppq使用案例。

@ZhangZhiPku
Copy link
Collaborator

哦豁,我不清楚你遇到了什么问题,你可以具体说下torch1.10跟torch1.11的差别?
我们支持pytorch的时候也是通过pytorch自己的导出接口导出onnx,不过我们还没遇到过什么版本方面的问题,感觉导出的onnx都差不多。(请注意我们支持opset 11的方式导出,如果你是opset 13的网络,我们可能要添加一些支持;如果你是更低版本的opset,大部分算子低版本算子ppq内部会直接完成转化)

哦豁,你需要demo的话,首先量化这里复杂模型和简单模型都差不多的处理方式,你可以用我们的菜鸡版接口,然后直接把数据放在对应文件夹下面,单击运行就量化完了。如果你的模型输入很复杂,比如有多个输入什么的,你需要使用我们的高级版接口

但是一个检测模型完成部署你还需要弄他的后处理什么的,这部分你打算如何处理?是要一份python的代码参考还是一份c++的?

@lucasjinreal
Copy link
Author

lucasjinreal commented Apr 21, 2022

@ZhangZhiPku 牛阿,这个非🥬🐣版接口,貌似是直接丢进区onnx做量化?不需要pytorch定义的模型? 那这个不就跟ort如出一辙了阿? 我尝试一下我的复杂模型,我感觉他有救了.不能放弃治疗希望可以起死回生

这个其实就是我想问得问题.目前检测量化方案,比较合理的方案是沙?

torch.8 vs torch1.11 , 例如, 大部分fx接口都诺到了 torch.ao.quantization当中, 一些方法被删除. 另外你自己定义的onnx op,在trace的时候,一些值会被诺到 attributes里面去, 这些东西在随后quantize weights的时候处理方式就存在较大差异. 不知道ppq是符合处理的? 例如 我定义的一个 symbolic:

class AtomFixedPerchannelAffine(torch.autograd.Function):
    @staticmethod
    def forward(ctx, x, scale, zero_point, ch_axis, quant_min, quant_max):
        return torch.fake_quantize_per_channel_affine(
            x, scale, zero_point, ch_axis, quant_min, quant_max
        )

    @staticmethod
    def symbolic(g, x, scale, zero_point, ch_axis, quant_min, quant_max):
        logger.debug("symbolic for: AtomFixedPerchannelAffine")
        return g.op(
            "::AtomFixedPerchannelAffine",
            x,
            scale,
            zero_point,
            # ch_axis, # why it can not be here?
            quant_min_i=quant_min,
            quant_max_i=quant_max,
        )

这个就存在不同版本的torch tace的node不同的问题. (不过这些都已经解决了). 不知道ppq里面是否用的更为高级的方法来插入量化节点?

@ZhangZhiPku
Copy link
Collaborator

这玩意mmdetection那边有两套方案,一个就是从head开始后面的网络全切,然后你检测网络最后就输出好几个tensor,然后所有后面的事情自己写c++实现吧。还有一个就是整个网络全部导出到Onnx里面,后面只有检测框变换大小是c++实现的。
ppq里面不调用torchfx,我们有自己的ir和图融合方式,是完全静态的方式处理的,ppq里面也没有量化节点的概念,我们使用Tensor量化配置结构体。所以torch.fx和torch.quantization跟我们没啥关系...

关于fake quant point这个做法,我们好像的确高级不少...

容找两个模型给你看一下...

@ZhangZhiPku
Copy link
Collaborator

image
这个是yolo5,就是head后面的全切,只有backbone,然后c++自己写其他东西吧。

@ZhangZhiPku
Copy link
Collaborator

image
这个是retinenet,含有所有算子,输出直接是检测框和分类概率,nms也已经包含在onnx里面了。

@lucasjinreal
Copy link
Author

后面这个retinanet,被ppq量化之后的onnx是什么样的?

我们有自己的ir和图融合方式,是完全静态的方式处理的,ppq里面也没有量化节点的概念

那你们在导入一个torch定义的模型的时候,是怎么拿到里面的结构图的?

另外向请问一下,如果为要成把量化后的权重保存为onnx, PLATFORM = TargetPlatform.ORT_OOS_INT8 # identify a target platform for your network.
是设置这个Target吗

@ZhangZhiPku
Copy link
Collaborator

这个东西ppq会自动追踪出里面要量化的节点,这个网络里大概2000个算子,能量化的有300个左右,不能量化的1700个左右。
虽然我好像暂时不能给你跑结果出来,我pytorch升级到1.11之后好像mmcv出了点问题执行不了这个网络了。

我们量化torch模型的时候,是直接用torch.onnx.export()导出onnx模型,然后我们处理的是onnx模型,跟torch没啥关系...
如果你要保存量化后的权重,TargetPlatform.ORT_OOS_INT8,TargetPlatform.ONNXRUNTIME都可以,你可以看看他们的区别,以及TargetPlatform.TRT_INT8

@lucasjinreal
Copy link
Author

lucasjinreal commented Apr 21, 2022

@ZhangZhiPku 大佬,求支招阿:

ppq/executor/torch.py", line 331, in __forward
    raise NotImplementedError(
NotImplementedError: Graph op: Mod_385(Mod) has no backend implementation on target platform TargetPlatform.SHAPE_OR_INDEX


我是了下 resnet18,很香。丝滑般量化体验。但是我的实力分割模型遇到个算子不支持,杂规避。我里面有 AdaptiveAvgPool可能导致的 (为了能导出我加了ceil)

另外你提到,是在 torch.onnx.export导出之后处理onnx模型,那是先插入量化节点->onnx 还是先导出onnx ->处理onnx呢?
如果是后者,那其不是,和onnxruntime一毛一样?不同之处在那里? 如果是前者,那你replace算子的时候,是怎么的一套逻辑阿,感觉在导出的onnx图里面去replace量化算子,是一个很难保证结果一定正确的事情。

PS:

我的模型算子集合:

 ./weights/sparse_inst_r50_giam_aug_2b7d68_sim.onnx
╭─────────────────── sparse_inst_r50_giam_aug_2b7d68_sim.onnx Summary ───────────────────╮
│ IR Version: 6                                                                          │
│ Opset Version: 11,                                                                     │
│ Doc:                                                                                   │
│ Producer Name: pytorch                                                                 │
│ All Ops: Transpose,Sub,Div,Shape,Gather,Conv,Relu,MaxPool,Add,AveragePool,Unsqueeze,Sl │
│ ice,Concat,Resize,Cast,Range,Reshape,Expand,ConstantOfShape,Mul,Equal,Where,Sigmoid,Ma │
│ tMul,ReduceSum,Clip,Less,Xor,Mod,Not,And,Sqrt,Split,Squeeze,ReduceMax,ArgMax,TopK,Grea │
│ ter                                                                                    │
╰────────────────────────────────────────────────────────────────────────────────────────╯
                     sparse_inst_r50_giam_aug_2b7d68_sim.onnx Detail                      
╭─────────────┬────────────────────────────────────┬──────────────────────┬──────────────╮
│ Name        │ Shape                              │ Input/Output         │ Dtype        │
├─────────────┼────────────────────────────────────┼──────────────────────┼──────────────┤
│ images      │ [-1, 640, 640, 3]                  │ input                │ float32      │
│ masks       │ [-1, -1, -1, -1, -1]               │ output               │ int64        │
│ scores      │ [-1, -1]                           │ output               │ float32      │
│ labels      │ [-1, -1]                           │ output               │ int64        │
╰─────────────┴────────────────────────────────────┴──────────────────────┴──────────────╯
                             Table generated by onnxexplorer                              

@ZhangZhiPku
Copy link
Collaborator

AdaptiveAvgPool,AdaptiveMaxPool这两个算子torch导不出来的,具体你可以看torch的symbolic_opset_9里面的实现,他们会被替换成别的,而且不一定能导出。

当然你这个好像只是有个Mod算子我们没有实现它的计算方式,老实说你的问题跟这个老哥的问题一致,这个Mod算子很简单,我可以很简单的给你加个实现,很快就能搞定。

你可以很轻松地度过ppq这关,这种奇奇怪怪的算子很影响你网络的部署能力,很多后端平台它根本运行不了这个算子。(当然TensorRT和ONNXRUNTIME基本都支持)。我建议你在设计网络的时候尽可能避开这些奇奇怪怪的算子,大道至简,重剑无锋。

在你的网络中奇奇怪怪的算子包括:ConstantOfShape,Equal,Where,Less,Xor,Mod,Not,And,ArgMax,TopK
我们大部分都已经支持了,但是后端框架真的未必支持你这些。

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 21, 2022

关于你所提到的结果是否正确的问题,这个问题刚好是我们这些量化工具的核心问题,我将花不小的篇幅向你阐述我的观点:

首先,我们的核心问题是一个网络拿来量化,首先要保证我的量化计算过程能跟硬件上真实的过程尽可能对齐,这样我才能算出正确的量化信息。你也看到了mqbench或者pytorch里面引入了一些叫做fake quant point东西来模拟硬件上的实际量化过程。很遗憾的是,这些fake quant point过于简单,表达能力十分有限,对于很多硬件而言都是无法正确描述量化的。

你可以看一眼我们的量化计算原理(Part 1)量化计算原理(Part 2),在那里我向你简要介绍了硬件执行量化的真实过程,从而对比pytorch的相关实现。

当我们知晓了不同硬件上的量化异同点,我们就可以开始着手设计量化工具,也就诞生了PPQ对于量化的基本逻辑抽象:Tensor量化配置结构体 以及 算子量化配置结构体。在每一个类似的结构体中,都有着7种取整方式;12种量化策略;12种量化状态;6个量化参数以及一个树形父子链接。我向你保证这是世界上最先进的量化表示。

我在做设计的时候其实考虑的很多——我们希望构建一种统一的抽象表达尽可能多的后端量化细节,并且尽可能做到无误差模拟。我们希望你不论在什么平台上去部署,ppq的量化信息永远都是语义上足够充分的,为此我们需要描述每一个数据的量化过程,以及可替换的算子计算逻辑,而非简单地几个fake quant point那么简单。这一点你可以导出Targetplatform.ONNX来观察我们的量化配置json文件,同时你也能够理解为什么我们的框架规模大概是mqbench的十倍。

在这一步的基础上,我们才有能力针对不同的后端做出相应的修改,这些抽象量化配置信息为我们留下了足够的空间让我们进行这样的操作。当然修改过程可能并不像你想的那么高端有效,对于开源后端平台,我们会直接去看它的代码实现来设计量化策略,或者直接拉一个对方的开发者过来问问;对于不开源后端平台,我们会拿网络上去试它的量化策略。因此我必须向你澄清,在我们目前所支持的平台中,支持情况大概是这样的:

TargetPlatform.PPL_DSP_INT8: 这是我们自己的平台
TargetPlatform.PPL_DSP_TI_IN8: 这是我们自己的平台
TargetPlatform.PPL_CUDA_INT8: 这是我们自己的平台
TargetPlatform.SNPE_INT8: 这是我们经常使用的平台,好像误差不大
TargetPlatform.NXP_INT8: 零误差模拟
TargetPlatform.ONNXRUNTIME: 模拟误差极小
TargetPlatform.ORT_OOS_INT8: 这是其他开源开发者贡献的内容,我不知道它的模拟误差是多少
TargetPlatform.METAX_INT8_C: 与对方开发者联合开发
TargetPlatform.METAX_INT8_T: 与对方开发者联合开发
TargetPlatform.Tengine_INT8: 与对方开发者联合开发
TargetPlatform.TensorRT: 这是PPQ 0.6.4新引入的导出平台,我也不知道它模拟误差是多少,应该不大吧...

哦对了,如果你要弄到fpga上去,我曾在xilinx实习,跟他们也挺熟的...

那我们最后来回答你的问题,我们究竟是怎么弄出来的onnxruntime模型。首先我们有一个更加复杂的量化表示,在导出的过程中我们做的事情实际就是化简和翻译——把ppq的量化信息翻译到onnxruntime当中去,这玩意之所以能对齐,其根本原因是我们量化描述的足够灵活全面,直接原因是因为我们帮你测过了...

当然我们最近在不断修改这个onnxruntime的导出,它那一天突然对不上了也是有可能的。

@lucasjinreal
Copy link
Author

@ZhangZhiPku 大佬,我吧Mod拿掉了。
现在遇到这么个错误:

ppq/ppq/executor/torch.py", line 22, in build_meta
    raise TypeError(f'Can not tracing meta for given value(type: {type(value)}), check your graph again.')
TypeError: Can not tracing meta for given value(type: <class 'NoneType'>), check your graph again.

During handling of the above exception, another exception occurred:

/executor/torch.py", line 422, in tracing_operation_meta
    self.__forward(
  File "/mq/ppq/executor/torch.py", line 387, in __forward
    raise RuntimeError(f'Error happens when dealing with operation {str(operation)}')
RuntimeError: Error happens when dealing with operation Clip_357(TargetPlatform.FP32) - inputs:['761', '1087_0', ''], outputs:['766']

我的onnx模型在这里,可以看看如何规避吗。

https://drive.google.com/file/d/1vp6IcNNponowYJWarYafwYxak2mP71-i/view?usp=sharing

我完全不知道这个错误从何而来,讲道理onnxruntime都已经load完了

@ZhangZhiPku
Copy link
Collaborator

这个也简单...你看到你的clip_357算子的最后一个输入是 '',其实这玩意就是onnx里面的空输入,我们之前的模型都没见过这玩意,所以ppq里没有能够处理它的逻辑。我也是最近才看见有这么个东西,在下一个patch里面就会修正这个问题,你可以直接改代码把这个clip的第三个输入弄出来,这个就是你只clip了下限,没有上限...

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 21, 2022

你的模型我看了,改了clip应该就能跑,有几个问题值得你注意,你的onnx一开始有数据预处理操作,你需要把他们弄到网络模型外面,我理解这部分操作应该不是由模型完成的。

与此同时在这个模型里面你有不少后处理操作,特别注意你的matmul_373和add_374其实是由一个gemm拆分出来的算子,这个需要合并,我不知道你为什么导出成了这样...,torch的导出它应该不会拆出来的,目前的情况是如果你不把他合成gemm,ppq的量化是不会处理这个matmul的,会有逻辑上的不便之处,请参考这个pr

当你把它弄成gemm之后,你需要考虑清楚是否需要量化它,这玩意是后处理算子,量化了容易起飞;根据你的上下文我不建议你量化它,因为它后面还有非线性激活sigmoid和sqrt,为此你可能需要将调度逻辑切换到pplnn。不过或许你可以直接不去合并这些gemm,这样似乎也没什么问题... ppq默认不会量化matmul。

@lucasjinreal
Copy link
Author

lucasjinreal commented Apr 22, 2022

@ZhangZhiPku 怎么改阿?你那边跑起来了吗?可不可以指定sigmoid之后的层就不量化.

但是我也不确定为什么会有Clip,Onnxruntime可以推理。
image

ppq是不是可以忽略max加一个default value?

@ZhangZhiPku
Copy link
Collaborator

莫慌,容我到公司给你这个模型手动加上一个 max value 就行了...

@lucasjinreal
Copy link
Author

@ZhangZhiPku 我加上了,但是接下来又有算子报错了,

/torch/nn/functional.py", line 3920, in interpolate
    raise NotImplementedError("Got 4D input, but linear mode needs 3D input")
NotImplementedError: Got 4D input, but linear mode needs 3D input

ppq/ppq/executor/torch.py", line 387, in __forward
    raise RuntimeError(f'Error happens when dealing with operation {str(operation)}')
RuntimeError: Error happens when dealing with operation Resize_414(TargetPlatform.FP32) - inputs:['pred_masks', 'onnx::Resize_517_14', 'onnx::Resize_1033_2'], outputs:['onnx::Sigmoid_836']

image

报错的这个resize,离输出已经很近了。

但是为不明白,我这附近没有上采样阿,哪儿来的这个东西。

你如果跑我之前的onnx,可能会遇到Split的错误,你那边可以解决吗。

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 22, 2022

我没有遇到split的问题,我加上了之后也是这个resize炸了,我也不知道你为什么有这个玩意,而且这个不是一个torch能算的东西。看起来你必须把它弄走...,它的输入是四维的,torch不支持linear模式下面算这个四维的resize,你可以检查你在sigmoid前面搞啥了,把它缩成三维或许是可行的。这东西如果只是一个上采样,或许我也可以手动把它变成三维的,它现在的scale是 1,1,2,2

你这模型该不会是tensorflow导出来的吧...

@lucasjinreal
Copy link
Author

@ZhangZhiPku 不满你说,这是来自于yolov7里面的一个抄袭模型。https://github.com/jinfagang/yolov7/blob/main/yolov7/modeling/meta_arch/sparseinst.py
这个onnx,我在ORT下面推理是正常的:
image

我怀疑是这行代码造成的: https://github.com/jinfagang/yolov7/blob/58848221007eaa773d10767afb5026afd602b9a7/yolov7/modeling/transcoders/decoder_sparseinst.py#L226

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 22, 2022

你这竟然还是个torch的模型...按理说torch导出来的不太可能它自己执行不了....不过既然问题已经定位,我这边应该能够很快解决。
不妨将最新的模型发给我,这样我就可以在上面小修小补了。

@lucasjinreal
Copy link
Author

更正一下,问题可能是这段代码:

# mask_features: BxCxHxW
        B, C, H, W = mask_features.shape
        pred_masks = torch.bmm(pred_kernel, mask_features.view(B, C, H * W)).view(
            B, N, H, W
        )

        pred_masks = F.interpolate(
            pred_masks,
            scale_factor=self.scale_factor,
            mode="bilinear",
            align_corners=False,
        )

但是不太对阿,这里是bileanr。而Interpolate 应该是可以支持4D输入的阿/。 杂个解决

@ZhangZhiPku
Copy link
Collaborator

反正这个算子不就是好像拉了一下h跟w变成两倍了..那我给你手动改个算子呗...

@lucasjinreal
Copy link
Author

最新的模型: https://drive.google.com/file/d/1vp6IcNNponowYJWarYafwYxak2mP71-i/view?usp=sharing
就是这个Resize的问题了

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 22, 2022

奇怪...你这里写的不是blinear吗...
这个要是bilinear是可以做四维的,那我可直接给他改成bilinear了...

@lucasjinreal
Copy link
Author

我也很奇怪,貌似billinear 被导出成了 onnx::Resize的linear。 这不科学阿

@ZhangZhiPku
Copy link
Collaborator

链接:https://pan.baidu.com/s/1dm1tVVjKVoQhgPkiZ-7yEA?pwd=jf2e
提取码:jf2e
--来自百度网盘超级会员V4的分享

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 22, 2022

哦..我看了onnx那边的解释,好像onnx里面这个linear就是包括了bilinear的意思,那还是我们写的不符合onnx规范,让我改一改...
所以我改成bilinear之后,它很可能不能上onnxruntime运行。

当然你可以在导出之前写一段话给他改回去:
graph.operations['Resize_414'].attributes['mode'] = 'linear'

@lucasjinreal
Copy link
Author

@ZhangZhiPku 这是int8吗?怎么比原来差不多大。大佬该完了测试一下阿,看看年不能生成一个 ONNX_OSS_INT8格式的量化权重的模型。

@ZhangZhiPku
Copy link
Collaborator

我没有数据不能给你做量化,我只是给你改了一下bilinear然后它就能跑了...

@lucasjinreal
Copy link
Author

@ZhangZhiPku 不用真量化,就看看能不能跑,能不能的导出图。怎么该的 阿, push到了ppq吗

@ZhangZhiPku
Copy link
Collaborator

没有,我就只是改了一下你得图..

@ZhangZhiPku
Copy link
Collaborator

这里我放了几个量化好的yolo5和你的yolo7, onnx_oos那个我暂时导不出来,在我这个开发机上还在改那个exporter。
链接:https://pan.baidu.com/s/1fHxIakGaMwOo0UwbY2a0kg?pwd=rgkd
提取码:rgkd
--来自百度网盘超级会员V4的分享

@lucasjinreal
Copy link
Author

lucasjinreal commented Apr 22, 2022

@ZhangZhiPku 好的感谢,如果有可能,传文件用googledrive方便写阿,百度下载不动么的会员。
你怎么改的我的图阿? export 改完就可以保存onnx_oos了吗?大概沙时候我这边也可以跑起来阿

我的目的是要用QDQ的格式或者QOperator的格式,可以直接用onnxruntime 推理的

@ZhangZhiPku
Copy link
Collaborator

容我提一个pr,应该很快就能搞定那个resize了

@lucasjinreal
Copy link
Author

@ZhangZhiPku 好的,大佬,跪等

@ZhangZhiPku
Copy link
Collaborator

resize的问题已经在这个更新中修正 #73

@lucasjinreal
Copy link
Author

@ZhangZhiPku 大佬,Resize的问题pass了。眼看就要成功,结果,尼采怎么着。遇到个恶心的问题:

/ppq/ppq/parser/onnxruntime_oos_exporter.py", line 423, in transform_qlinear_joint_op
    graph.variables[
KeyError: 'onnx::Concat_720_scale'

这个是不是意味着,这个concat并没有被量化?现在无法保存模型,全部输出如下:

19:21:35 04.22 INFO dataloader.py:66]: creating calibration dataloader for coco, onnx input_name: images
[19:21:37] PPQ Layerwise Equalization Pass Running ... 40 equalization pair(s) was found, ready to run optimization.
Layerwise Equalization: 100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 10/10 [00:00<00:00, 114.37it/s]
Finished.
/home/envs/torch1.10/lib/python3.9/site-packages/torch/nn/functional.py:3771: UserWarning: Default upsampling behavior when mode=bilinear is changed to align_corners=False since 0.4.0. Please specify align_corners=True if the old behavior is desired. See the documentation of nn.Upsample for details.
  warnings.warn(
[19:21:37] PPQ Quantization Config Refine Pass Running ... Finished.
[19:21:37] PPQ Quantization Fusion Pass Running ...        Finished.
[19:21:37] PPQ Quantize Point Reduce Pass Running ...      Finished.
[19:21:37] PPQ Parameter Quantization Pass Running ...     Finished.
Calibration Progress(Phase 1): 100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 32/32 [00:07<00:00,  4.43it/s]
Finished.
[19:21:45] PPQ Quantization Alignment Pass Running ...     Finished.
[19:21:45] PPQ Passive Parameter Quantization Running ...  Finished.
[19:21:45] PPQ Parameter Baking Pass Running ...           Finished.
--------- Network Snapshot ---------
Num of Op:                    [353]
Num of Quantized Op:          [162]
Num of Variable:              [608]
Num of Quantized Var:         [324]
------- Quantization Snapshot ------
Num of Quant Config:          [500]
BAKED:                        [75]
OVERLAPPED:                   [200]
SLAVE:                        [51]
ACTIVATED:                    [99]
PASSIVE_BAKED:                [75]
Network Quantization Finished.
File Output/quantized(onnx).onnx.onnx has already exist, ppq exporter will overwrite it.
File Output/quantized(onnx).json has already exist, ppq exporter will overwrite it.

然后我得到的json,是没有这个key,杂症阿。

image

@lucasjinreal
Copy link
Author

这个算子是不是被漏掉了?
image

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 22, 2022

巧了,这个是onnx oos exporter的bug,这个exporter是并非是我们写的,而是其他开发者贡献的,我猜他没有考虑到你的concat是个不可量化的concat,所以导致了这样的问题,不过在新的版本中,我们正在着手重写这个exporter。目前你可以很简单的,在ppq.parser.onnxruntime_oos_exporter.py中的第528行加上过滤条件 if not isinstance(operation, QuantableOperation): return,从而过滤掉所有非量化算子。

如你所见,你的图中的concat实际上接收的数据流来自于shape算子,这意味着它是一个shape的concat,这种concat不能被量化,否则你的网络将执行失败。因为量化操作会改变shape的值,这样未来这个shape再去做其他算子的参数的时候,你会发现形状不对。

还记得我们之前提到的ppq图调度与数值追踪,解决的就是上述问题...

@lucasjinreal
Copy link
Author

lucasjinreal commented Apr 22, 2022

@ZhangZhiPku 直接过滤之后,最终保存的模型,是正确的吗? 我看到又两个, onnxruntime_export onnxruntime_oss_export,前者是官方维护的吗?

@ZhangZhiPku
Copy link
Collaborator

onnxruntime_export 是我们在维护的,它可以正常导出你的模型。

@lucasjinreal
Copy link
Author

那我直接用你们的onnxruntime_export不就的了吗?
哪一个可以保存我想要的int8模型阿

image

@ZhangZhiPku
Copy link
Collaborator

这两个出来的模型格式是不一样的...但都是Int8的...

@lucasjinreal
Copy link
Author

ppq/ppq/api/interface.py", line 243, in quantize_onnx_model
    raise ValueError(f'Target Platform {platform} is an non-quantable platform.')
ValueError: Target Platform TargetPlatform.ONNXRUNTIME is an non-quantable platform.

我慌了

@ZhangZhiPku
Copy link
Collaborator

ZhangZhiPku commented Apr 22, 2022

你写错了吧...量化平台不能选这个,只有导出平台可以选这个...
量化平台是用来确定量化规则的,onnxruntime这玩意没有具体的量化规则...你必须要指明你的网络准备在那里运行,如果是gpu,请选择tensorRT或者ppl_cuda_int8...

@lucasjinreal
Copy link
Author

ARM64 M1应该选那个?x86呢?

另外为加了这个return,但是还是报错:

image

@ZhangZhiPku
Copy link
Collaborator

不过你可以偷偷加我一个微信,我感觉你好像要搞出一些奇怪的东西:
Phoenixz2014

@lucasjinreal
Copy link
Author

已加,可删,感谢大佬

@hshen14
Copy link

hshen14 commented Apr 24, 2022

Hi @ZhangZhiPku 想了解一下是否有计划支持PyTorch FX INT8 model -> ONNX model? Thx.

@Feiyuyu0503
Copy link

关于你所提到的结果是否正确的问题,这个问题刚好是我们这些量化工具的核心问题,我将花不小的篇幅向你阐述我的观点:

首先,我们的核心问题是一个网络拿来量化,首先要保证我的量化计算过程能跟硬件上真实的过程尽可能对齐,这样我才能算出正确的量化信息。你也看到了mqbench或者pytorch里面引入了一些叫做fake quant point东西来模拟硬件上的实际量化过程。很遗憾的是,这些fake quant point过于简单,表达能力十分有限,对于很多硬件而言都是无法正确描述量化的。

你可以看一眼我们的量化计算原理(Part 1)量化计算原理(Part 2),在那里我向你简要介绍了硬件执行量化的真实过程,从而对比pytorch的相关实现。

当我们知晓了不同硬件上的量化异同点,我们就可以开始着手设计量化工具,也就诞生了PPQ对于量化的基本逻辑抽象:Tensor量化配置结构体 以及 算子量化配置结构体。在每一个类似的结构体中,都有着7种取整方式;12种量化策略;12种量化状态;6个量化参数以及一个树形父子链接。我向你保证这是世界上最先进的量化表示。

我在做设计的时候其实考虑的很多——我们希望构建一种统一的抽象表达尽可能多的后端量化细节,并且尽可能做到无误差模拟。我们希望你不论在什么平台上去部署,ppq的量化信息永远都是语义上足够充分的,为此我们需要描述每一个数据的量化过程,以及可替换的算子计算逻辑,而非简单地几个fake quant point那么简单。这一点你可以导出Targetplatform.ONNX来观察我们的量化配置json文件,同时你也能够理解为什么我们的框架规模大概是mqbench的十倍。

在这一步的基础上,我们才有能力针对不同的后端做出相应的修改,这些抽象量化配置信息为我们留下了足够的空间让我们进行这样的操作。当然修改过程可能并不像你想的那么高端有效,对于开源后端平台,我们会直接去看它的代码实现来设计量化策略,或者直接拉一个对方的开发者过来问问;对于不开源后端平台,我们会拿网络上去试它的量化策略。因此我必须向你澄清,在我们目前所支持的平台中,支持情况大概是这样的:

TargetPlatform.PPL_DSP_INT8: 这是我们自己的平台 TargetPlatform.PPL_DSP_TI_IN8: 这是我们自己的平台 TargetPlatform.PPL_CUDA_INT8: 这是我们自己的平台 TargetPlatform.SNPE_INT8: 这是我们经常使用的平台,好像误差不大 TargetPlatform.NXP_INT8: 零误差模拟 TargetPlatform.ONNXRUNTIME: 模拟误差极小 TargetPlatform.ORT_OOS_INT8: 这是其他开源开发者贡献的内容,我不知道它的模拟误差是多少 TargetPlatform.METAX_INT8_C: 与对方开发者联合开发 TargetPlatform.METAX_INT8_T: 与对方开发者联合开发 TargetPlatform.Tengine_INT8: 与对方开发者联合开发 TargetPlatform.TensorRT: 这是PPQ 0.6.4新引入的导出平台,我也不知道它模拟误差是多少,应该不大吧...

哦对了,如果你要弄到fpga上去,我曾在xilinx实习,跟他们也挺熟的...

那我们最后来回答你的问题,我们究竟是怎么弄出来的onnxruntime模型。首先我们有一个更加复杂的量化表示,在导出的过程中我们做的事情实际就是化简和翻译——把ppq的量化信息翻译到onnxruntime当中去,这玩意之所以能对齐,其根本原因是我们量化描述的足够灵活全面,直接原因是因为我们帮你测过了...

当然我们最近在不断修改这个onnxruntime的导出,它那一天突然对不上了也是有可能的。

志佬,PPQ支持xilinx的FPGA平台吗

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants