三. TensorRT基础入门-ONNX注册算子的方法

目录

    • 前言
    • 0. 简述
    • 1. 执行一下我们的python程序
    • 2.转换swin-tiny时候出现的不兼容op的例子
    • 3. 当出现导出onnx不成功的时候,我们需要考虑的事情
    • 4. unsupported asinh算子
    • 5. unsupported deformable conv算子
    • 总结
    • 参考

前言

自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》,链接。记录下个人学习笔记,仅供自己参考

本次课程我们来学习课程第三章—TensorRT 基础入门,一起来学习 ONNX 注册算子的方法

课程大纲可以看下面的思维导图

在这里插入图片描述

0. 简述

本小节目标:学习 pytorch 导出 onnx 不成功的时候如何解决(without plugin篇)

这节我们学习第三章节第六小节—ONNX 注册算子的方法,学习当 pytorch 导出 onnx 失败时的解决方法,比较常见的就是我们在使用开源代码将它导出 ONNX 时发现算子不兼容,还有导出成功但是转成 TensorRT 的 engine 时又出现算子不兼容,这里就简单过一下遇到这些情况我们应该怎么做

因为现阶段我们还没有讲如何利用 C++ 去写一个 plugin,所以我们先不考虑这个算子的 C++ 是如何实现的,我们现在主要是 pytorch 出现一些不兼容的算子导出 onnx 的时候该怎么做

1. 执行一下我们的python程序

源代码获取地址:https://github.com/kalfazed/tensorrt_starter

这个小节的案例主要是 3.4-export-unsupported-node,如下所示:

在这里插入图片描述

2.转换swin-tiny时候出现的不兼容op的例子

先给大家做一个简单的背景介绍,按照 swin-transformer 官方文档导出 onnx 时可能会出现如下问题:

在这里插入图片描述

roll 这个算子在 opset9 不支持,那我们再看看 opset12:

在这里插入图片描述

可以发现在 opset12 时依旧出现同样的问题

在这里插入图片描述

swin/models/swin_transformer.py

我们到对应的代码中可以找到 torch.roll 它在 onnx opset 中是不兼容的,那我们应该怎么办呢?我们在下一个小节会跟大家去讲

3. 当出现导出onnx不成功的时候,我们需要考虑的事情

当出现导出 onnx 不成功的时候,我们主要有以下几个方法,难易度从低到高:

  • 修改 opset 的版本
    • 查看不支持的算子在新的 opset 中是否被支持
    • 如果不考虑自己搭建 plugin 的话,也需要看看 onnx-trt 中这个算子是否被支持
    • 因为 onnx 是一种图结构表示,并不包含各个算子的实现。除非我们是要在 onnxruntime 上测试,否则我们更看重 onnx-trt 中这个算子的支持情况
    • 算子支持文档:https://github.com/onnx/onnx/blob/main/docs/Operators.md
  • 替换 pytorch 中的算子组合
    • 把某些计算替换成 onnx 可以识别的
  • 在 pytorch 登记 onnx 中某些算子
    • 有可能 onnx 中有支持,但没有被登记,比如 Asinh
  • 直接修改 onnx 创建 plugin
    • 使用 onnxsurgeon
    • 一般是用在加速某些算子上使用

这个小节主要是给大家介绍第三种方法,注册登记算子

4. unsupported asinh算子

我们先执行下 sample_asinh.py 案例代码,输出如下所示:

在这里插入图片描述

src/sample_asinh.py

可以看到导出 onnx 出错了,原因是 aten::asinh 在 ONNX opset9 是不支持的,代码如下:

import torch
import torch.onnxclass Model(torch.nn.Module):def __init__(self):super().__init__()def forward(self, x):x = torch.asinh(x)return xdef infer():input = torch.rand(1, 5)model = Model()x = model(input)print("input is: ", input.data)print("result is: ", x.data)def export_norm_onnx():input   = torch.rand(1, 5)model   = Model()model.eval()file    = "../models/sample-asinh.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 9)print("Finished normal onnx export")if __name__ == "__main__":infer()# 这里导出asinh会出现错误。# Pytorch可以支持asinh的同时,#   def asinh(input: Tensor, *, out: Optional[Tensor]=None) -> Tensor: ...# 从onnx支持的算子里面我们可以知道自从opset9开始asinh就已经被支持了#   asinh is suppored since opset9# 所以我们可以知道,问题是出现在PyTorch与onnx之间没有建立asinh的映射# 我们需要建立这个映射。这里涉及到了注册符号函数的概念,详细看PPTexport_norm_onnx()

出现导出问题我们先去寻找官方文档 https://github.com/onnx/onnx/blob/main/docs/Operators.md,如下图所示:

在这里插入图片描述

这里我们可以看到 asinh 算子在 opset9 这个版本已经开始支持了,但是导出就是不行,为什么呢?其实问题是出现在 pytorch 与 onnx 之间没有建立 asinh 的映射,我们需要自己来建立这个映射

我们可以在 torch/onnx/symbolic_opset9.py 找到 pytorch 注册到 onnx 中支持的算子,如下图所示:

在这里插入图片描述

Note:低版本 torch 下面的 symbolic_opset9.py 文件中可能并没有上图中的内容,博主 torch 版本是 2.0.1

针对不同的算子我们可以看到如下代码:

# torch/onnx/symbolic_opset9.py/L317@_onnx_symbolic("aten::_shape_as_tensor")
@_beartype.beartype
def _shape_as_tensor(g: jit_utils.GraphContext, input):return g.op("Shape", input)@_onnx_symbolic("aten::_reshape_from_tensor")
@_beartype.beartype
def _reshape_from_tensor(g: jit_utils.GraphContext, input, shape):if isinstance(shape, list):shape = g.op("Concat", *shape, axis_i=0)return reshape(g, input, shape)@_onnx_symbolic("aten::reshape")
@symbolic_helper.quantized_args(True)
@_beartype.beartype
def reshape(g: jit_utils.GraphContext, self, shape):return symbolic_helper._reshape_helper(g, self, shape)...

这些代码是 PyTorch 中用于将特定的 aten (PyTorch 的内部操作库) 操作符转换为 ONNX 操作符的符号化函数。这些符号化函数定义了如何将 PyTorch 的操作符映射到等效的 ONNX 操作符,其实它们就是做了算子注册这件事

我们以 reshape 为例简单分析下 pytorch 到 onnx 的算子注册是怎么做的:(from ChatGPT)

1. @_onnx_symbolic(“aten::reshape”)

  • 这是一个装饰器,用于将 PyTorch 的 aten::reshape 操作符映射到 ONNX 的等效操作符。
  • @_onnx_symbolic 是一个装饰器函数,用于注册和标记 PyTorch 操作符与其 ONNX 对应操作符之间的关系。
  • "aten::reshape" 指定了 PyTorch 中的 reshape 操作符。

2. @symbolic_helper.quantized_args(True)

  • 指定函数是否处理量化参数。
  • @symbolic_helper.quantized_args 是一个装饰器,用于指示该函数是否支持量化参数。
  • True 表示该函数支持量化参数。如果模型中有量化的操作,该装饰器会确保这些操作被正确处理和导出。

3. @_beartype.beartype

  • 类型检查装饰器,确保函数的输入参数和返回类型符合预期。
  • @_beartype.beartypebeartype 库提供的装饰器,用于在运行时进行类型检查,确保参数类型和返回值类型正确。

4. def reshape(g: jit_utils.GraphContext, self, shape):

  • 定义 reshape 函数。
  • g: 表示 jit_utils.GraphContext 类型的对象,代表当前的图上下文,用于在图中添加节点。
  • self: 输入的张量,代表要重塑的张量。
  • shape: 表示新的形状,可以是一个表示形状的张量或一个列表。

5. return symbolic_helper._reshape_helper(g, self, shape)

  • 调用 _reshape_helper 函数执行实际的重塑操作,并返回结果。
  • symbolic_helper._reshape_helper(g, self, shape) 是一个帮助函数,用于执行重塑逻辑。
  • g: 传递当前的图上下文。
  • self: 传递要重塑的输入张量。
  • shape: 传递目标形状。

其中的 aten::xxx 是 C++ 的一个 namespace,pytorch 的很多算子的底层都是在 aten 这个命名空间下进行实现的,而 aten(a Tensor Library)是一个实现张量运算的 C++ 库。onnx_symblic 负责绑定,使得 pytorch 中的算子与 aten 命名空间下的算子一一对应

补充g.op 函数是 PyTorch 中用于在 ONNX 计算图中添加操作节点的函数。它是 jit_utils.GraphContext 类的一个方法,用于定义和插入新的 ONNX 操作节点。其参数定义如下:

  • op_type: 操作的类型,例如 "Reshape", "Concat", "Shape" 等。这些操作类型是 ONNX 操作符集中的名称。
  • inputs: 传递给操作节点的输入张量。可以是一个或多个张量。
  • attributes: 可选参数,指定操作的属性,通常以 key=value 的形式传递。

通过这么一套操作我们就可以把 ONNX 中的某个算子和 Pytorch 底层 aten 命名空间下的算子实现给绑定起来,这就是一个算子的注册

因此之前 asinh 导出问题就是因为 pytorch 中的底层算子实现和 onnx 中的算子没有绑定,我们手动绑定下即可。所以我们来看 sample_asinh_register.py 案例代码,如下所示:

import torch
import torch.onnx
import onnxruntime
from torch.onnx import register_custom_op_symbolic# 创建一个asinh算子的symblic,符号函数,用来登记
# 符号函数内部调用g.op, 为onnx计算图添加Asinh算子
#   g: 就是graph,计算图
#   也就是说,在计算图中添加onnx算子
#   由于我们已经知道Asinh在onnx是有实现的,所以我们只要在g.op调用这个op的名字就好了
#   symblic的参数需要与Pytorch的asinh接口函数的参数对齐
#       def asinh(input: Tensor, *, out: Optional[Tensor]=None) -> Tensor: ...
def asinh_symbolic(g, input, *, out=None):return g.op("Asinh", input)# 在这里,将asinh_symbolic这个符号函数,与PyTorch的asinh算子绑定。也就是所谓的“注册算子”
# asinh是在名为aten的一个c++命名空间下进行实现的# 那么aten是什么呢?
# aten是"a Tensor Library"的缩写,是一个实现张量运算的C++库
register_custom_op_symbolic('aten::asinh', asinh_symbolic, 12)# 这里容易混淆的地方:
# 1. register_op中的第一个参数是PyTorch中的算子名字: aten::asinh
# 2. g.op中的第一个参数是onnx中的算子名字: Asinhclass Model(torch.nn.Module):def __init__(self):super().__init__()def forward(self, x):x = torch.asinh(x)return xdef validate_onnx():input = torch.rand(1, 5)# PyTorch的推理model = Model()x     = model(input)print("result from Pytorch is :", x)# onnxruntime的推理sess  = onnxruntime.InferenceSession('../models/sample-asinh.onnx')x     = sess.run(None, {'input0': input.numpy()})print("result from onnx is:    ", x)def export_norm_onnx():input   = torch.rand(1, 5)model   = Model()model.eval()file    = "../models/sample-asinh.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 12)print("Finished normal onnx export")if __name__ == "__main__":export_norm_onnx()# 自定义完onnx以后必须要进行一下验证validate_onnx()

这段代码展示了如何在 PyTorch 中创建和注册一个自定义的符号函数 (symbolic function),以便将 PyTorch 的 asinh 操作导出到 ONNX 格式,并在 ONNX Runtime 中进行推理验证

函数 register_custom_op_symbolic 是 PyTorch ONNX 导出工具中用来注册自定义算子的符号映射函数。此函数对于在 ONNX 中支持 PyTorch 中的自定义或特殊算子至关重要。下面我会逐一解释函数参数和作用:(from CHatGPT)

参数解释

  • symbolic_name (str):

    • 这是需要注册的自定义算子的名称。格式通常为 <domain>::<op>,其中 <domain> 表示算子所属的命名空间,而 <op> 是算子的名称。例如,aten::asinh 表示来自 aten 域的 asinh 算子。
  • symbolic_fn (Callable):

    • 这是一个函数,用于定义如何将 PyTorch 中的操作转换为 ONNX 图中的节点。这个函数通常会接收几个参数:ONNX 图对象、当前算子的输入参数等,并且基于这些输入构造并返回 ONNX 中相应的算子节点。

    • symbolic_fn 应返回一个或多个用于替换原有 PyTorch 算子的 ONNX 操作节点。这个函数的实现需要考虑输入输出的匹配、算子属性的转换等。

  • opset_version (int):

    • 这是指定算子应当注册到的 ONNX 操作集版本。ONNX 的操作集(opset)定义了算子的支持和行为,不同版本的 opset 可能支持不同的算子或者同一算子的不同行为。这个参数确保你的自定义算子兼容特定版本的 ONNX。

执行后输出如下图所示:

在这里插入图片描述

导出的 ONNX 如下图所示:

在这里插入图片描述

我们再来看另外一种写法,案例 sample_asinh_register2.py 代码如下所示:

import torch
import torch.onnx
import onnxruntime
import functools
from torch.onnx import register_custom_op_symbolic
from torch.onnx._internal import registration_onnx_symbolic = functools.partial(registration.onnx_symbolic, opset=9)# 另外一个写法
#    这个是类似于torch/onnx/symbolic_opset*.py中的写法
#    通过torch._internal中的registration来注册这个算子,让这个算子可以与底层C++实现的aten::asinh绑定
#    一般如果这么写的话,其实可以把这个算子直接加入到torch/onnx/symbolic_opset*.py中
@_onnx_symbolic('aten::asinh')
def asinh_symbolic(g, input, *, out=None):return g.op("Asinh", input)class Model(torch.nn.Module):def __init__(self):super().__init__()def forward(self, x):x = torch.asinh(x)return xdef validate_onnx():input = torch.rand(1, 5)# PyTorch的推理model = Model()x     = model(input)print("result from Pytorch is :", x)# onnxruntime的推理sess  = onnxruntime.InferenceSession('../models/sample-asinh2.onnx')x     = sess.run(None, {'input0': input.numpy()})print("result from onnx is:    ", x)def export_norm_onnx():input   = torch.rand(1, 5)model   = Model()model.eval()file    = "../models/sample-asinh2.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 12)print("Finished normal onnx export")if __name__ == "__main__":export_norm_onnx()# 自定义完onnx以后必须要进行一下验证validate_onnx()

这段代码展示了另一种注册自定义符号函数的方法,使用了 torch.onnx._internal 中的 registration 模块,通过装饰器的方式注册 asinh 操作符

与之前方式不同的是它通过两个步骤来注册自定义符号函数:(from ChatGPT)

1. 创建 _onnx_symbolic 的局部函数

_onnx_symbolic = functools.partial(registration.onnx_symbolic, opset=9)
  • 使用 functools.partial 创建一个偏函数 _onnx_symbolic,指定 opset 为 9。

  • 该偏函数用于注册符号函数,简化了装饰器的使用。

2. 定义 asinh_symbolic 符号函数并注册

@_onnx_symbolic('aten::asinh')
def asinh_symbolic(g, input, *, out=None):return g.op("Asinh", input)
  • 使用装饰器 @_onnx_symbolic('aten::asinh') 注册 asinh_symbolic 符号函数。
  • asinh_symbolic 函数用于在计算图 (graph) 中添加 ONNX 的 Asinh 操作。
  • 参数 g 是计算图对象,input 是输入张量。
  • g.op("Asinh", input) 表示在计算图中添加一个 Asinh 操作。

执行后输出如下图所示:

在这里插入图片描述

我们再来看下一个案例 sample_custom_op_autograd.py,代码如下所示:

import torch
import torch.onnx
import onnxruntimeOperatorExportTypes = torch._C._onnx.OperatorExportTypes
# 我们按照正常的方式,创建一个图。不考虑自己设计算子的话,我们其实是直接导出这个onnx的
# 只不过onnx会为这个实现根据自己内部注册的各个算子,追踪每一个节点生成图
# 我们可以观察一下onnx,会比较复杂,我们管这个叫做算子的inline autograd functionclass CustomOp(torch.autograd.Function):@staticmethoddef forward(ctx, x: torch.Tensor) -> torch.Tensor:ctx.save_for_backward(x)x = x.clamp(min=0)return x / (1 + torch.exp(-x))customOp = CustomOp.applyclass Model(torch.nn.Module):def __init__(self):super().__init__()def forward(self, x):x = customOp(x)return xdef validate_onnx():input   = torch.rand(1, 50).uniform_(-1, 1).reshape(1, 2, 5, 5)# PyTorch的推理model = Model()x     = model(input)print("result from Pytorch is :\n", x)# onnxruntime的推理sess  = onnxruntime.InferenceSession('../models/sample-customOp.onnx')x     = sess.run(None, {'input0': input.numpy()})print("result from onnx is:    \n", x)def export_norm_onnx():input   = torch.rand(1, 50).uniform_(-1, 1).reshape(1, 2, 5, 5)model   = Model()model.eval()# 我们可以在导出onnx的时候添operator_export_type的限制,防止导出的onnx进行inlinefile    = "../models/sample-customOp.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 12)# operator_export_type = OperatorExportTypes.ONNX_FALLTHROUGH)print("Finished normal onnx export")if __name__ == "__main__":export_norm_onnx()# 自定义完onnx以后必须要进行一下验证validate_onnx()

该代码定义了一个自定义的 PyTorch 算子 CustomOp,并在一个简单模型中使用它。然后将模型导出为 ONNX 格式,并通过 onnxruntime 验证导出的 ONNX 模型是否与原始 PyTorch 模型输出一致。

执行后输出如下图所示:

在这里插入图片描述

导出的 ONNX 如下图所示:

在这里插入图片描述

可以看到如果不加以特殊处理,自定义算子可能会被内联到标准 ONNX 算子中,这个过程可能会导致模型结构的复杂性增加,会导致产生一些多余的算子,而这些算子都将被 trace,这是我们不希望看到的,我们希望它导出更加简洁一点

那我们该怎么做,我们来看 sample_custom_op_autograd_register.py 案例,代码如下所示:

import torch
import torch.onnx
import onnxruntime
from torch.onnx import register_custom_op_symbolicOperatorExportTypes = torch._C._onnx.OperatorExportTypesclass CustomOp(torch.autograd.Function):@staticmethod def symbolic(g: torch.Graph, x: torch.Value) -> torch.Value:return g.op("custom_domain::customOp2", x)@staticmethoddef forward(ctx, x: torch.Tensor) -> torch.Tensor:ctx.save_for_backward(x)x = x.clamp(min=0)return x / (1 + torch.exp(-x))customOp = CustomOp.applyclass Model(torch.nn.Module):def __init__(self):super().__init__()def forward(self, x):x = customOp(x)return xdef validate_onnx():input   = torch.rand(1, 50).uniform_(-1, 1).reshape(1, 2, 5, 5)# PyTorch的推理model = Model()x     = model(input)print("result from Pytorch is :\n", x)# onnxruntime的推理sess  = onnxruntime.InferenceSession('../models/sample-customOp2.onnx')x     = sess.run(None, {'input0': input.numpy()})print("result from onnx is:    \n", x)def export_norm_onnx():input   = torch.rand(1, 50).uniform_(-1, 1).reshape(1, 2, 5, 5)model   = Model()model.eval()file    = "../models/sample-customOp2.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 12)print("Finished normal onnx export")if __name__ == "__main__":export_norm_onnx()# 自定义完onnx以后必须要进行一下验证validate_onnx()

它与之前的代码相比增加了对自定义算子的符号化注册,使得导出的 ONNX 模型更加简洁,主要区别有:

1. 增加 symbolic 方法:

  • CustomOp 类中添加了 symbolic 方法,用于定义自定义算子在 ONNX 中的符号化表示。

2. 注册自定义算子符号:

  • 通过 register_custom_op_symbolic 注册自定义算子,使其在导出 ONNX 时使用定义的符号化操作。

输出如下图所示:

在这里插入图片描述

导出的 ONNX 如下图所示:

在这里插入图片描述

可以看到导出的 ONNX 就一个我们的自定义算子非常简洁,但是我们在利用 onnxruntime 进行推理时发生了错误,这其实是因为 onnxruntime 在执行推理时无法识别我们的自定义算子 custom_domain::customOp2,我们需要在 onnxruntime 中进行相关自定义算子的实现才能正确推理

5. unsupported deformable conv算子

我们再来看 deformable conv 这个案例,先看 sample_deformable_conv.py 代码如下所示:

import torch
import torch.nn as nn
import torchvision
import torch.onnxclass Model(torch.nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(3, 18, 3)self.conv2 = torchvision.ops.DeformConv2d(3, 3, 3)def forward(self, x):x = self.conv2(x, self.conv1(x))return xdef infer():input = torch.rand(1, 3, 5, 5)model = Model()x = model(input)print("input is: ", input.data)print("result is: ", x.data)def export_norm_onnx():input   = torch.rand(1, 3, 5, 5)model   = Model()model.eval()file    = "../models/sample-deformable-conv.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 12)print("Finished normal onnx export")if __name__ == "__main__":infer()# 这里导出deformable-conv会出现错误。# torchvision支持deformable_conv的# 但是我们在onnx中是没有找到有关deformable conv的支持# 所以这个时候,我们需要做两件事情export_norm_onnx()

执行完输出如下图所示:

在这里插入图片描述

可以看到导出失败了,deformable conv 其实在 torchvision 中有实现,但是我们在 onnx 中没有找到有关deformable conv 的支持

这个时候我们其实注册下算子就行了,来看案例 sample_deformable_conv_register.py,代码如下:

import torch
import torch.nn as nn
import torchvision
import torch.onnx
import onnxruntime
from torch.onnx import register_custom_op_symbolic
from torch.onnx.symbolic_helper import parse_args# 注意
#   这里需要把args的各个参数的类型都指定
#   这里还没有实现底层对deform_conv2d的实现
#   具体dcn的底层实现是在c++完成的,这里会在后面的TensorRT plugin中回到这里继续讲这个案例
#   这里先知道对于不支持的算子,onnx如何导出即可
@parse_args("v", "v", "v", "v", "v", "i", "i", "i", "i", "i","i", "i", "i", "none")
def dcn_symbolic(g,input,weight,offset,mask,bias,stride_h, stride_w,pad_h, pad_w,dil_h, dil_w,n_weight_grps,n_offset_grps,use_mask):return g.op("custom::deform_conv2d", input, offset)register_custom_op_symbolic("torchvision::deform_conv2d", dcn_symbolic, 12)class Model(torch.nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(3, 18, 3)self.conv2 = torchvision.ops.DeformConv2d(3, 3, 3)def forward(self, x):x = self.conv2(x, self.conv1(x))return xdef validate_onnx():input = torch.rand(1, 3, 5, 5)# PyTorch的推理model = Model()x     = model(input)print("result from Pytorch is :", x)# onnxruntime的推理sess  = onnxruntime.InferenceSession('../models/sample-deformable-conv.onnx')x     = sess.run(None, {'input0': input.numpy()})print("result from onnx is:    ", x)def infer():input = torch.rand(1, 3, 5, 5)model = Model()x = model(input)print("input is: ", input.data)print("result is: ", x.data)def export_norm_onnx():input   = torch.rand(1, 3, 5, 5)model   = Model()model.eval()file    = "../models/sample-deformable-conv.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 12)print("Finished normal onnx export")if __name__ == "__main__":# infer()export_norm_onnx()validate_onnx()

这段代码通过定义和注册自定义算子符号,将包含 deformable convolution 算子的 PyTorch 模型导出为 ONNX 格式,值得注意的是 dcn_symbolic 中的 g.op("custom::deform_conv2d", input, offset) 仅定义了一个自定义操作,但并没有实际实现 deform_conv2d 的计算逻辑,这需要在后续的 onnxruntime/TensorRT 插件中实现。

另外 @parse_args() 是一个装饰器,用于指明 dcn_symbolic 函数各个参数的类型。具体解释如下:

  • “v” (variable): 表示一个张量(Tensor)参数。
  • “i” (integer): 表示一个整型参数。
  • “none”: 表示一个可以为 None 的参数。

执行后输出如下图所示:

在这里插入图片描述

导出的 ONNX 如下图所示:

在这里插入图片描述

总结

本次课程我们主要学习了 ONNX 注册算子的方法,主要是利用 register_custom_op_symbolic 函数来注册自定义算子的符号映射函数,从而将自定义的算子导出到 ONNX,值得注意的是这个小节我们并没有具体的实现自定义算子的计算逻辑,这需要在后续的 TensorRT 插件中实现,这个我们在之后的 plugin 小节再来详细讲解

OK,以上就是第 6 小节有关 ONNX 注册算子方法的全部内容了,下节我们来学习 onnx-graph-surgeon,敬请期待😄

参考

  • https://github.com/kalfazed/tensorrt_starter
  • https://github.com/onnx/onnx/blob/main/docs/Operators.md

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

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

相关文章

EmotiVoice 实时语音合成TTS

参考:https://github.com/netease-youdao/EmotiVoice 测试整体速度可以 docker安装: 运行容器:默认运行了两个服务,8501 一个streamlit页面,另外8000是一个api接口服务 docker run -dp 8501:8501 -p 8250:8000 syq163/emoti-voice:latest ##gpu运行 docker run --gpus a…

第四届微调——炼丹

学习地址&#xff1a;Tutorial/xtuner/README.md at main InternLM/Tutorial GitHub 笔记 微调是一种在已有的预训练模型基础上&#xff0c;通过使用新的数据对模型进行进一步优化和调整的技术手段。它的目的是使模型能够更好地适应特定的应用场景和任务需求&#xff0c;进一…

基础ArkTS组件:帧动画,内置动画组件,跑马灯组件(HarmonyOS学习第三课【3.6】)

帧动画 帧动画也叫序列帧动画&#xff0c;其原理就是在时间轴的每帧上逐帧绘制不同的内容&#xff0c;使其连续播放而成动画。ArkUI开发框架提供了 ImageAnimator 组件实现帧动画能力&#xff0c;本节笔者介绍一下 ImageAnimator 组件的简单使用。 官方文献 说明 该组件从A…

9. SVG中的text元素

SVG (Scalable Vector Graphics) 提供了强大的文本渲染能力&#xff0c;其中<text>元素是常用 的文本操作的元素。本文将详细介绍<text>标签的基本使用方法&#xff0c;并展示如何通过<tspan>和<textPath>增强文本的表现力。 <text>标签基础 &…

防爆巡检手持终端在燃气巡检作业中的应用

在燃气巡检作业中&#xff0c;安全始终是首要考虑的因素。面对易燃易爆的燃气环境&#xff0c;传统的巡检方式已经难以满足现代安全管理的需求。随着科技的不断进步&#xff0c;防爆巡检手持终端应运而生&#xff0c;成为燃气巡检作业的得力助手。这些终端不仅具备高度的防爆性…

【数轮】数论、质数、最大公约数、菲蜀定理

数学 唯一分解定理 n>2都可以表示为质因数的乘方。 令 n a1b1a2b2 … \dots … a1,b1 … \dots …都是质因数&#xff0c;b1,b2 … \dots …是对应质因数的数量。 调和级数 11/2 1/3 1/4 ⋯ \cdots ⋯ 1/ n 约等于 logn。 证明过程&#xff1a; 1/3 1/4 < (1/2) …

Colab微调LLaMA3模型(大模型的微调)

Colab微调LLaMA3模型 1.使用的数据集 在hugging face上搜索 kigner/ruozhiba-llama3 使用的是弱智吧的数据 2.打开Colab 选择Gpu版本T2就可以了&#xff0c;然后下载unsloth这个微调框架&#xff0c;使用该框架的主要原因在于对硬件要求比较低。 在安装这个前先看一下本文…

css实现上下左右对勾选中状态角标

&#x1f365;左上角 &#x1f365;右上角 &#x1f365;左下角 &#x1f365;右下角: &#x1f365;左上角: .blueBackground {position: relative;border: 1px solid #91c7f3;background: #F0F8FF !important;&:after {content: "";position: absolute;top:…

黏土滤镜教程分享:让你的照片瞬间变身黏土艺术

在数字时代的浪潮中&#xff0c;手机摄影和修图软件成为了我们日常生活中不可或缺的一部分。而近期&#xff0c;一款名为“黏土滤镜”的修图功能火爆全网&#xff0c;让无数摄影爱好者和创意达人为之疯狂。本文将为你详细介绍几款拥有黏土滤镜功能的软件&#xff0c;并附上详细…

RiPro主题美化【支付弹窗底部提示语根据入口不同有不同的提示】ritheme主题美化RiProV2 增加支付提示语,按支付类型不同,入口不同提示语不同的设置

RiPro主题美化【支付弹窗底部提示语根据入口不同有不同的提示】ritheme主题美化RiProV2 增加支付提示语,按支付类型不同,入口不同提示语不同的设置 背景: 接上文:https://www.uu2id.com/827.html 付费组件在以下几个地方会弹出:1)文章隐藏内容付费;2)付费资源下载;3…

【R语言篇】医学生福音,全球疾病负担数据库GBD 2021即将更新!!!

今天介绍即将于5月16日更新的全球疾病负担数据库GBD 2021&#xff0c;相信数据一经发表&#xff0c;过不了多久pubmed又将涌现一大波疾病负担相关文章。 Global Burden of Disease Study 2021 (GBD 2021) Data Resources | GHDx 在查找GBD相关文献方面&#xff0c;我个人还是比…

Redis数据结构扩容源码分析

1 Redis数据结构 redis的数据存储在dict.中&#xff0c;其数据结构为(c源码) ypedef struct dict { dictType *type; //理解为面向对象思想&#xff0c;为支持不同的数据类型对应dictType抽象方法&#xff0c;不同的数据类型可以不同实现 void *privdata; //也可不同的数据类…

10个免费视频素材网站,剪辑师们赶紧收藏!

在快速发展的数字媒体时代&#xff0c;寻找高质量的视频和音频素材成为了多数内容创作者的日常需求。无论是企业宣传、个人创作还是社交媒体更新&#xff0c;优秀的素材不仅能增强视觉效果&#xff0c;还能提高信息传播效率。以下是一些优质的免费素材网站&#xff0c;它们提供…

Eduma – WordPress教育培训机构主题

下载地址&#xff1a;Eduma – WordPress教育培训机构主题 这个主题的设计理念是让你的教育和在线学习服务在最高水平上闪耀。Eduma具有多种功能&#xff0c;使其成为展示教育和在线学习服务的理想选择。 Eduma使用AJAX创建&#xff0c;这意味着在页面浏览过程中&#xff0c;页…

pdf 版面分析与优化策略

1. 简介 版面分析作为RAG的第一步工作&#xff0c;其效果对于下游工作至关重要。 前常见的 PDF 解析方法包括三种 基于规则&#xff1a;根据 PDF 的组织特征确定每个部分的规则&#xff08;风格和内容&#xff09;缺点&#xff1a;不通用&#xff08;PDF格式不固定&#xf…

GPT搜索引擎原型曝光!

OpenAI发布会前一天&#xff0c;员工集体发疯中……上演大型套娃行为艺术。 A&#xff1a;我为B的兴奋感到兴奋&#xff1b;B&#xff1a;我为C的兴奋感到兴奋……Z&#xff1a;我为这些升级感到兴奋 与此同时还有小动作不断&#xff0c;比如现在GPT-4的文字描述已不再是“最先…

618购物节适合入手的数码好物有哪些?实用闭眼冲的数码好物清单

随着618购物节的临近&#xff0c;数码爱好者们纷纷翘首以盼&#xff0c;期待着在这个年度大促中寻觅到心仪的数码好物&#xff0c;无论是提升工作效率的办公利器&#xff0c;还是丰富业余生活的娱乐设备&#xff0c;数码产品在我们的日常生活中扮演着越来越重要的角色&#xff…

电路板维修【三】

自恢复保险丝&#xff1a; 自恢复保险丝属于慢断类型保险丝&#xff0c;自恢复保险丝的材料因为通电后发热&#xff0c;当电流过大发热到一定程度的时候&#xff0c;材料就不导电了&#xff0c;这个和普通的保险丝是一个道理&#xff0c;只不过普通的保险丝是一次型熔断而已。…

【5月13日】YesPMP平台目前最新项目

YesPMP平台目前最新项目&#xff0c;有感兴趣的用户查看项目接单&#xff0c;与项目方沟通&#xff0c;双方直接对接。 1.查看项目&#xff1a;blender动画师 2查看项目&#xff1a;找宠友信息源码 3.查看项目&#xff1a;儿童…

ComfyUI 完全入门:必备插件

大家好&#xff0c;我是每天分享AI应用的萤火君&#xff01; ComfyUI 是一个基于 Stable Diffusion 的AI绘画创作工具&#xff0c;最近发展势头特别迅猛&#xff0c;但是 ComfyUI 的上手门槛有点高&#xff0c;用户需要对 Stable Diffusion 以及各种数字技术的原理有一定的了解…