三. 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 时依旧出现同样的问题
我们到对应的代码中可以找到 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 案例代码,输出如下所示:
可以看到导出 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.beartype
是beartype
库提供的装饰器,用于在运行时进行类型检查,确保参数类型和返回值类型正确。
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
相关文章:

三. TensorRT基础入门-ONNX注册算子的方法
目录 前言0. 简述1. 执行一下我们的python程序2.转换swin-tiny时候出现的不兼容op的例子3. 当出现导出onnx不成功的时候,我们需要考虑的事情4. unsupported asinh算子5. unsupported deformable conv算子总结参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战…...

01、什么是ip、协议、端口号知道吗?计算机网络通信的组成是什么?
声明:本教程不收取任何费用,欢迎转载,尊重作者劳动成果,不得用于商业用途,侵权必究!!! 目录 前言 计算机网络 网络ip地址 网络协议 网络端口号 前言 最近有个项目要用到相关文章…...
答题套路2 阅读理解 说明文某个词是否能去掉
观点 回答:不能 解词 某个词什么意思需要解释一下 反证法 如果去掉了,会怎么样 总结 使用这个词体现了说明文的科学性,严谨性...
Pytorch图像分类模型模型实时在线验证代码
1.训练并保存自己的模型 保存的模型格式为:XXX.pth torch.save(model, "./weight/last.pth")if best_acc <(validation_acc / len_val):torch.save(model, "./weight/best.pth")2.转化为ONNX格式 2.1环境安装(window10&#x…...
Java高并发场景(银行转账问题)
最近面试问到了银行转账的高并发问题,回答的不是很理想,小编整理了下,题目大概如下: 有一张银行账号表(银行账号字段、金额字段),A账号要给B账号转账,A扣款,B收款&#x…...
TypeScript 工具类型
这些工具类型是 TypeScript 提供的强大功能,用于操作和转换类型。下面是每个工具类型的简要说明和示例: 1、Record let value: Record<string, any> { name: "", age: 0, desc: [] }; let value2: { [key: string]: any } { name: &…...

[Kotlin]创建一个私有包并使用
1.创建Kotlin测试项目 在Android Studio或其他IDE中选择“Create New Project”。选择Kotlin和Gradle作为项目类型和构建系统。指定项目名称和位置,完成设置。 2.创建Android Library模块 官方文档:创建 Android 库 | Android Studio | Android De…...
鸿蒙应用开发者高级认证指南及参考资料整理(含详细参考答案)
如何报名鸿蒙应用开发者高级认证 报名链接:点击这里进行报名。报名步骤: 点击上述链接进入报名页面。选择“立即报名”。在课程内容中找到“HarmonyOS应用开发者高级认证”,点击进入。点击“参加考试”,随后即可开始考试。考试注意事项 实名认证:考试前,请务必完成实名认…...
数据匿名化技术
不同的数据匿名化技术可用于多种行业,旨在从数据流中获取有用的见解,同时确保满足数据保护标准和法规的合规要求。 数据脱敏(Data Masking) 数据脱敏,又称数据漂白、数据去隐私化或数据变形,指的是对数据集…...
HTML学习笔记汇总
整理一些常见问题的Links,不定期更新。 Html生成自定义函数的图形(2024/5/10)-CSDN博客 HTML中插入图片(2024/5/10)-CSDN博客 Html给网页加icon图标_html icon-CSDN博客...
初始JSVMP
1.初始JSVMP JSVMP是"JavaScript Virtual Machine Protection"的缩写,是一种前端代码虚拟化保护技术。它的核心思想是在JavaScript代码保护过程中引入代码虚拟化,将目标代码转换成自定义的字节码,这些字节码只有特殊的解释器才能识…...

【机器学习数据可视化-04】Pyecharts数据可视化宝典
一、引言 在大数据和信息爆炸的时代,数据可视化成为了信息传递和展示的关键手段。通过直观的图表和图形,我们能够更好地理解数据,挖掘其背后的信息。Pyecharts,作为一款基于Python的数据可视化库,凭借其丰富的图表类型…...

通过 Java 操作 redis -- zset 有序集合基本命令
目录 使用命令 zadd,zrange 使用命令 zcard 使用命令 zrem 使用命令 zscore 使用命令 zrank 关于 redis zset 有序集合类型的相关命令推荐看Redis - Zset 有序集合 要想通过 Java 操作 redis,首先要连接上 redis 服务器,推荐看通过 Jav…...
力扣 516. 最长回文子序列 python AC
动态规划 class Solution:def longestPalindromeSubseq(self, s):size len(s)dp [[0] * size for _ in range(size)]for i in range(size):dp[i][i] 1for i in range(size - 1, -1, -1):for j in range(i 1, size):if s[i] s[j]:dp[i][j] dp[i 1][j - 1] 2else:dp[i][…...

数据库编程
PL/SQL程序 1.PL/SOL程序块 整个PL/SQL块分三部分:声明部分、执行部分、异常处理部分; 示例: declare --变量声明 v_sno varchar2(10) : ‘04001’; v_cno varchar2(10) :‘001’; v_grade number : 90; begin --程序入口 insert…...

(docker)进入容器后如何使用本机gpu
首次创建容器,不能直接使用本机gpu 在系统终端进行如下配置: 1.安装NVIDIA Container Toolkit 进入Nvidia官网Installing the NVIDIA Container Toolkit — NVIDIA Container Toolkit 1.15.0 documentation,安装NVIDIA Container Toolkit …...

java基础知识点总结2024版(8万字超详细整理)
java基础知识点总结2024版(超详细整理) 这里写目录标题 java基础知识点总结2024版(超详细整理)java语言的特点1.简单性2.面向对象3.分布式4.健壮性5.安全性6.体系结构中立7.可移植性8.解释性9.多线程10.动态性 初识java中的main方…...

vue中使用element的i18n语言转换(保姆式教程-保证能用)
话不多说,先看效果:预览地址: https://sandm00.github.io/i18n-switch/#/ 1、项目中需要使用的插件,vue2或vue3、element、vue-i18n、js-cookie、vuex我是在vue2中使用 npm i element-ui -S npm i js-cookie -S npm i vue-i18n8.28.2 //因为我项目使用…...
01 设计模式--单例模式
1. 单例模式 单例模式有两种实现方式: 1.1 饿汉模式(Eager Initialization):在类加载时就创建单例实例,无论是否需要使用该实例。 饿汉模式在类加载时就创建单例实例,无论是否需要使用该实例。 饿汉模式…...

css backdrop-filter 实现背景滤镜
官方给出的定义是:backdrop-filter属性允许您将图形效果(如模糊或颜色偏移)应用于元素后面的区域。因为它适用于元素后面的所有内容,所以要查看元素或其背景的效果,需要透明或部分透明。 大致分为以下10种:…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...
C++:std::is_convertible
C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...

现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
【HTTP三个基础问题】
面试官您好!HTTP是超文本传输协议,是互联网上客户端和服务器之间传输超文本数据(比如文字、图片、音频、视频等)的核心协议,当前互联网应用最广泛的版本是HTTP1.1,它基于经典的C/S模型,也就是客…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...