三. TensorRT基础入门-导出并分析ONNX

2024-05-13 04:52

本文主要是介绍三. TensorRT基础入门-导出并分析ONNX,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

    • 前言
    • 0. 简述
    • 1. generate-onnx
    • 2. export-onnx
    • 3. 补充-ONNX
      • 3.1 概念
      • 3.2 组成
    • 总结
    • 参考

前言

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

本次课程我们来学习课程第三章—TensorRT 基础入门,一起来学习如何导出并分析 ONNX

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

在这里插入图片描述

0. 简述

本小节目标:学习 Pytorch 导出 ONNX 的方法以及 onnx-simplifier 的使用

这节我们学习第三章节第四小节—导出 ONNX、分析 ONNX,这里我们拆分成了几个小节来分析 ONNX,包括 ONNX 内部的 protobuf 怎么用以及它的数据表达形式是什么样的,导出 ONNX 时遇到不兼容的算子怎么处理等等内容

这个小节还是比较简单的,我们主要来学习下 Pytorch 模型怎么导出 ONNX,以及 onnx-simplifier 模块的使用

1. generate-onnx

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

这个部分对应的案例主要是 3.1-generate-onnx,如下所示:

在这里插入图片描述

我们直接进入 src 代码文件中看下,先看下 example.py 文件,代码如下所示:

import torch
import torch.nn as nn
import torch.onnxclass Model(torch.nn.Module):def __init__(self, in_features, out_features, weights, bias=False):super().__init__()self.linear = nn.Linear(in_features, out_features, bias)with torch.no_grad():self.linear.weight.copy_(weights)def forward(self, x):x = self.linear(x)return xdef infer():in_features = torch.tensor([1, 2, 3, 4], dtype=torch.float32)weights = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)model = Model(4, 3, weights)x = model(in_features)print("result is: ", x)def export_onnx():input   = torch.zeros(1, 1, 1, 4)weights = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)model   = Model(4, 3, weights)model.eval() #添加eval防止权重继续更新# pytorch导出onnx的方式,参数有很多,也可以支持动态size# 我们先做一些最基本的导出,从netron学习一下导出的onnx都有那些东西torch.onnx.export(model         = model, args          = (input,),f             = "../models/example.onnx",input_names   = ["input0"],output_names  = ["output0"],opset_version = 12)print("Finished onnx export")if __name__ == "__main__":infer()export_onnx()

上述代码定义了一个简单的 PyTorch 模型,进行了推理和模型导出为 ONNX 的操作,我们可以使用 Netron 工具来查看下我们导出的 ONNX,如下图所示:

在这里插入图片描述

它其实是一个非常简单的 ONNX 架构,一个 input,一个 output,中间有一个 MatMul 的节点,右侧栏中可以看到各个节点的相关信息,比如 input 的 name 叫 input0,它的 type 是 float32,维度是 1x1x1x4

在代码中我们使用的是 torch.onnx.export 函数来将我们定义的 pytorch 模型导出为 onnx 模型 torch.onnx.export 函数是 PyTorch 用来将模型导出到 ONNX (Open Neural Network Exchange) 格式的工具。这个函数非常重要,因为它允许模型在不同的机器学习框架间进行转换和部署。这里博主询问了 ChatGPT 并记录了该函数中各个参数的介绍方便后续查看,下面是这个函数及其主要参数的详细介绍:(from ChatGPT

def export(model: Union[torch.nn.Module, torch.jit.ScriptModule, torch.jit.ScriptFunction],args: Union[Tuple[Any, ...], torch.Tensor],f: Union[str, io.BytesIO],export_params: bool = True,verbose: bool = False,training: _C_onnx.TrainingMode = _C_onnx.TrainingMode.EVAL,input_names: Optional[Sequence[str]] = None,output_names: Optional[Sequence[str]] = None,operator_export_type: _C_onnx.OperatorExportTypes = _C_onnx.OperatorExportTypes.ONNX,opset_version: Optional[int] = None,do_constant_folding: bool = True,dynamic_axes: Optional[Union[Mapping[str, Mapping[int, str]], Mapping[str, Sequence[int]]]] = None,keep_initializers_as_inputs: Optional[bool] = None,custom_opsets: Optional[Mapping[str, int]] = None,export_modules_as_functions: Union[bool, Collection[Type[torch.nn.Module]]] = False,autograd_inlining: Optional[bool] = True,
) -> None:
  • model (torch.nn.Module): 这是你要导出的 PyTorch 模型。它应该是一个已经训练好的模型。(重要
  • args (tupletorch.Tensor): 这是传递给模型的输入数据。这个参数非常关键,因为它用于推导模型中张量的形状。如果你的模型接受多个输入,可以通过元组传递每个输入。(重要
  • f (strio.BytesIO): 指定导出的 ONNX 文件的保存路径或一个二进制文件。如果是字符串,它是文件的路径;如果是 BytesIO 对象,模型将被保存到这个内存中的对象。
  • export_params (bool, 默认为 True): 指定是否导出模型的权重。设为 True 会一同导出模型参数,否则只导出模型结构。
  • verbose (bool, 默认为 False): 如果设为 True,在导出过程中会打印出模型的详细信息,这有助于调试。
  • training (torch.onnx.TrainingModebool): 用于指定模型是处于训练模式 (TrainingMode.TRAINING) 还是评估模式 (TrainingMode.EVAL)。这会影响某些层(如批归一化层)的导出方式。
  • input_names (list[str]): 为输入张量指定名字,这些名字在 ONNX 图中会用作节点的标识。
  • output_names (list[str]): 为输出张量指定名字,这有助于在 ONNX 模型中识别输出节点。
  • operator_export_type (torch.onnx.OperatorExportTypes): 控制某些 PyTorch 操作如何转换为 ONNX 操作。例如,ONNX_ATEN_FALLBACK 使得如果某个操作无法被标准 ONNX 操作覆盖,可以使用 PyTorch 自定义操作。
  • opset_version (int): 指定导出的 ONNX 模型使用的操作集版本。不同的版本可能支持不同的操作。默认是使用 PyTorch 支持的最新的 ONNX 版本。(重要
  • do_constant_folding (bool, 默认值为 True): 此参数控制是否应用常量折叠优化。常量折叠是一种优化技术,它会预先计算那些所有输入都是常量的操作,并将这些操作替换为预计算的常量节点。
  • dynamic_axes (dict): 允许指定哪些维度可以是动态的。这在处理变长的序列或者批量大小可变的情况时非常有用。(重要
  • keep_initializers_as_inputs (Optional[bool]): 这个参数决定 initializers(如权重和偏差)是否也应该作为输入列在 ONNX 计算图中。将此设置为 True 可以增加与某些期望权重作为图输入的 ONNX 推理引擎的旧版本的兼容性。如果 opset_version < 9,那么 initializers 必须作为图的输入。
  • custom_opsets (Optional[Mapping[str, int]]): 允许你为特定操作集定义版本号。这在使用非标准或自定义 ONNX 操作时特别有用,可以通过这个参数指定这些操作的版本。
  • export_modules_as_functions (Union[bool, Collection[Type[torch.nn.Module]]]): 这个参数可以设置为 True 或者一个包含模块类型的集合,用于导出时将这些模块作为 ONNX 中的函数进行管理。
  • autograd_inlining (Optional[bool]): 这个参数用于控制是否在导出过程中将自动微分操作内联到图中。如果设置为 True(默认值),则相关的自动微分计算会被内联进图中,这有可能会使导出的模型更简洁,但在某些情况下可能不希望这种行为。

下面是该函数的一个使用示例:

import torch
import torch.nn as nn# 定义模型
class SimpleModel(nn.Module):def __init__(self):super(SimpleModel, self).__init__()self.linear = nn.Linear(10, 5)def forward(self, x):return self.linear(x)# 实例化模型并设置为评估模式
model = SimpleModel().eval()# 创建一个示例输入
example_input = torch.randn(1, 10)# 导出模型
torch.onnx.export(model,               # 要导出的模型example_input,       # 模型的示例输入"model.onnx",        # 保存模型的文件名export_params=True,  # 导出模型的参数opset_version=11,    # 指定 ONNX opset 版本do_constant_folding=True,  # 是否执行常数折叠优化input_names=['input'],      # 输入的名字output_names=['output'],    # 输出的名字dynamic_axes={'input' : {0 : 'batch_size'},  # 指定批量大小是动态的'output' : {0 : 'batch_size'}})

我们再来看下 example_two_head.py,代码如下所示:

import torch
import torch.nn as nn
import torch.onnxclass Model(torch.nn.Module):def __init__(self, in_features, out_features, weights1, weights2, bias=False):super().__init__()self.linear1 = nn.Linear(in_features, out_features, bias)self.linear2 = nn.Linear(in_features, out_features, bias)with torch.no_grad():self.linear1.weight.copy_(weights1)self.linear2.weight.copy_(weights2)def forward(self, x):x1 = self.linear1(x)x2 = self.linear2(x)return x1, x2def infer():in_features = torch.tensor([1, 2, 3, 4], dtype=torch.float32)weights1 = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)weights2 = torch.tensor([[2, 3, 4, 5],[3, 4, 5, 6],[4, 5, 6, 7]],dtype=torch.float32)model = Model(4, 3, weights1, weights2)x1, x2 = model(in_features)print("result is: \n")print(x1)print(x2)def export_onnx():input    = torch.zeros(1, 1, 1, 4)weights1 = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)weights2 = torch.tensor([[2, 3, 4, 5],[3, 4, 5, 6],[4, 5, 6, 7]],dtype=torch.float32)model   = Model(4, 3, weights1, weights2)model.eval() #添加eval防止权重继续更新# pytorch导出onnx的方式,参数有很多,也可以支持动态sizetorch.onnx.export(model         = model, args          = (input,),f             = "../models/example_two_head.onnx",input_names   = ["input0"],output_names  = ["output0", "output1"],opset_version = 12)print("Finished onnx export")if __name__ == "__main__":infer()export_onnx()

这个示例代码与之前的代码相比,主要的区别在于模型中增加了一个额外的线性层和一个额外的输出,导出的 ONNX 如下图所示:

在这里插入图片描述

可以看到导出的 ONNX 有两个 head,与我们代码中设置的一样

我们继续看下一个 example_dynamic_shape.py,代码如下所示:

import torch
import torch.nn as nn
import torch.onnxclass Model(torch.nn.Module):def __init__(self, in_features, out_features, weights, bias=False):super().__init__()self.linear = nn.Linear(in_features, out_features, bias)with torch.no_grad():self.linear.weight.copy_(weights)def forward(self, x):x = self.linear(x)return xdef infer():in_features = torch.tensor([1, 2, 3, 4], dtype=torch.float32)weights = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)model = Model(4, 3, weights)x = model(in_features)print("result of {1, 1, 1 ,4} is ", x.data)def export_onnx():input   = torch.zeros(1, 1, 1, 4)weights = torch.tensor([[1, 2, 3, 4],[2, 3, 4, 5],[3, 4, 5, 6]],dtype=torch.float32)model   = Model(4, 3, weights)model.eval() #添加eval防止权重继续更新# pytorch导出onnx的方式,参数有很多,也可以支持动态sizetorch.onnx.export(model         = model, args          = (input,),f             = "../models/example_dynamic_shape.onnx",input_names   = ["input0"],output_names  = ["output0"],dynamic_axes  = {'input0':  {0: 'batch'},'output0': {0: 'batch'}},opset_version = 12)print("Finished onnx export")if __name__ == "__main__":infer()export_onnx()

上述代码和前面的示例相比最显著的变化在于 ONNX 导出部分,这里引入了动态维度的支持,导出的 ONNX 如下图所示:

在这里插入图片描述

我们可以对比之前导出的 ONNX,在没有动态维度支持时导出的 ONNX 中 batch 维度为固定数值 1,如果是动态 shape 如上图所示可以看到 batch 维度不再是一个固定的数值,而是动态可变化的。值得注意的是,我们在导出 ONNX 时一般只会让 batch 维度动态,宽高不动态

2. export-onnx

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

这个部分对应的案例主要是 3.2-export-onnx,如下所示:

在这里插入图片描述

我们直接进入 src 代码文件中看下,先看下 sample_cbr.py 文件,代码如下所示:

import torch
import torch.nn as nn
import torch.onnxclass Model(torch.nn.Module):def __init__(self):super().__init__()self.conv1 = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3)self.bn1   = nn.BatchNorm2d(num_features=16)self.act1  = nn.ReLU()def forward(self, x):x = self.conv1(x)x = self.bn1(x)x = self.act1(x)return xdef export_norm_onnx():input   = torch.rand(1, 3, 5, 5)model   = Model()model.eval()# 通过这个案例,我们一起学习一下onnx导出的时候,其实有一些节点已经被融合了# 思考一下为什么batchNorm不见了file    = "../models/sample-cbr.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 15)print("Finished normal onnx export")if __name__ == "__main__":export_norm_onnx()

上面代码展示了一个简单的 PyTorch 模型,其中包括一个卷积层(Conv2d)、一个批量归一化层(BatchNorm2d)和一个激活函数(ReLU),以及如何将这个模型导出为ONNX格式。

导出的 ONNX 如下图所示:

在这里插入图片描述

那大家可以对上面导出的 ONNX 有所疑问,为什么 BatchNorm 层在导出的ONNX文件中不见了呢?那这其实因为当 PyTorch 模型被导出到 ONNX 格式时,可以发生所谓的图优化,其中一些连续的操作可能被融合为一个操作,以优化执行效率。尤其是当 Conv2dBatchNorm2d 层相连时,这两个操作经常会被融合为一个单一的卷积操作。这种优化被称为卷积-批量归一化融合:(from ChatGPT)

  • 原因: 在评估模式下,批量归一化的参数(均值、方差)是固定的,而卷积层的权重也是固定的。这意味着这两个层的操作可以组合成一个具有新权重的单一卷积操作,而无需改变输出结果。这样做可以减少运行时的计算负担,因为减少了运行的层的数量
  • 效果: 这样的融合能有效减少模型的复杂性和提高执行效率,特别是在硬件加速环境下(如使用 GPU 或专用 AI 加速器)

总的来说,通过此案例我们需要了解到 ONNX 导出过程中的节点融合是一个常见的优化手段,用于提高模型的运行效率。这种优化是自动进行的,基于当前的模型结构和 opset 版本的支持。

我们来看下一个案例 sample_reshape.py 文件,代码如下所示:

import torch
import torch.nn as nn
import torch.onnx
import onnxsim
import onnxclass Model(torch.nn.Module):def __init__(self):super().__init__()self.conv1   = nn.Conv2d(in_channels=3, out_channels=16, kernel_size=3, padding=1)self.bn1     = nn.BatchNorm2d(num_features=16)self.act1    = nn.ReLU()self.conv2   = nn.Conv2d(in_channels=16, out_channels=64, kernel_size=5, padding=2)self.bn2     = nn.BatchNorm2d(num_features=64)self.act2    = nn.ReLU()self.avgpool = nn.AdaptiveAvgPool1d(1)self.head    = nn.Linear(in_features=64, out_features=10)def forward(self, x):x = self.conv1(x)x = self.bn1(x)x = self.act1(x)x = self.conv2(x)x = self.bn2(x)x = self.act2(x) x = torch.flatten(x, 2, 3)  # B, C, H, W -> B, C, L (这一个过程产生了shape->slice->concat->reshape这一系列计算节点, 思考为什么)# b, c, w, h = x.shape# x = x.reshape(b, c, w * h)# x = x.view(b, c, -1)x = self.avgpool(x)         # B, C, L    -> B, C, 1x = torch.flatten(x, 1)     # B, C, 1    -> B, Cx = self.head(x)            # B, L       -> B, 10return xdef export_norm_onnx():input   = torch.rand(1, 3, 64, 64)model   = Model()file    = "../models/sample-reshape.onnx"torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 15)print("Finished normal onnx export")model_onnx = onnx.load(file)# 检查导入的onnx modelonnx.checker.check_model(model_onnx)# 使用onnx-simplifier来进行onnx的简化。# 可以试试把这个简化给注释掉,看看flatten操作在简化前后的区别# onnx中其实会有一些constant value,以及不需要计算图跟踪的节点# 大家可以一起从netron中看看这些节点都在干什么# print(f"Simplifying with onnx-simplifier {onnxsim.__version__}...")# model_onnx, check = onnxsim.simplify(model_onnx)# assert check, "assert check failed"onnx.save(model_onnx, file)if __name__ == "__main__":export_norm_onnx()

上述代码实现了一个包含多个层的神经网络,并展示了如何将 PyTorch 模型导出为 ONNX 格式,以及如何检查和简化 ONNX 模型,导出的 ONNX 模型如下图所示:

在这里插入图片描述

那看上面的 ONNX 模型大家可能会想为什么 torch.flatten 这个操作产生了 shape->slice->concat->reshape 这一系列计算节点呢?

这是因为在 PyTorch 模型中使用 torch.flatten 操作时,如果涉及到多个维度的平展,这个操作在转换到 ONNX 格式时通常会被分解成几个更基础的操作(如 ShapeSliceConcatReshape),主要是因为 ONNX 需要明确地追踪数据的形状变化,让 ONNX 标准化其操作,以确保与不同的后端和硬件兼容性。下面是每个步骤作用的详细解释:(from ChatGPT)

  • Shape:这个操作获取张量的维度。在 ONNX 中,许多操作需要知道输入张量的具体形状来执行后续的操作,尤其是在维度转换或变化时。
  • Slice:这个操作用于从 Shape 得到的形状张量中提取特定的维度。在示例中 torch.flatten(x, 2, 3) 意味着要将第二维到第三维的尺寸合并。Slice 操作帮助提取这些维度的尺寸。
  • Concat:这个操作将 Slice 操作得到的尺寸值与其他维度的尺寸进行合并。在 flatten 操作中,合并的目的是创建一个新的尺寸数组,用于下一步的 Reshape
  • Reshape:根据 Concat 得到的新尺寸数组,重新塑形张量。这实际上是实现 flatten 的最终步骤,将指定的多个维度合并为一个维度。

这个过程看起来比直接在 PyTorch 中使用 flatten 更复杂,因为 ONNX 需要更明确地表达维度变化,以确保模型的兼容性和可移植性。每一个步骤都是必要的,以在不同的平台和框架之间确保操作的一致性。

如果在将模型导出为 ONNX 时遇到性能问题或者想要优化这一过程,可以考虑是否有方法简化网络结构或者预处理步骤,或者使用 ONNX 的优化工具来尝试合并这些操作。

下面我们就通过 onnx-simplifier 来优化这个 ONNX 模型,优化后的 ONNX 模型如下图所示:

在这里插入图片描述

可以看到优化后的 ONNX 模型非常的干净,之前 flatten 的一系列操作直接变成了 reshape 一个节点搞定

补充onnx-simplifier 是一个用于简化 ONNX 模型的工具。它通过合并冗余的操作、消除不必要的中间节点等方法,优化模型的计算图。onnx-simplifier 通常以命令行工具的形式提供,可以直接通过 Python 包管理器安装,使用简单的命令就可以对 ONNX 模型进行简化。例如:

pip install onnx-simplifier
python -m onnxsim input_model.onnx output_model.onnx

关于 onnx-simplifier 的更多细节可以查看 https://github.com/daquexian/onnx-simplifier

OK,我们来看最后一个案例程序 load_torchvision.py,代码如下所示:

import torch
import torchvision
import onnxsim
import onnx
import argparsedef get_model(type, dir):if type == "resnet":model = torchvision.models.resnet50()file  = dir + "resnet50.onnx"elif type == "vgg":model = torchvision.models.vgg11()file  = dir + "vgg11.onnx"elif type == "mobilenet":model = torchvision.models.mobilenet_v3_small()file  = dir + "mobilenetV3.onnx"elif type == "efficientnet":model = torchvision.models.efficientnet_b0()file  = dir + "efficientnetb0.onnx"elif type == "efficientnetv2":model = torchvision.models.efficientnet_v2_s()file  = dir + "efficientnetV2.onnx"elif type == "regnet":model = torchvision.models.regnet_x_1_6gf()file  = dir + "regnet1.6gf.onnx"return model, filedef export_norm_onnx(model, file, input):model.cuda()torch.onnx.export(model         = model, args          = (input,),f             = file,input_names   = ["input0"],output_names  = ["output0"],opset_version = 15)print("Finished normal onnx export")model_onnx = onnx.load(file)# 检查导入的onnx modelonnx.checker.check_model(model_onnx)# 使用onnx-simplifier来进行onnx的简化。print(f"Simplifying with onnx-simplifier {onnxsim.__version__}...")model_onnx, check = onnxsim.simplify(model_onnx)assert check, "assert check failed"onnx.save(model_onnx, file)def main(args):type        = args.typedir         = args.dirinput       = torch.rand(1, 3, 224, 224, device='cuda')model, file = get_model(type, dir)export_norm_onnx(model, file, input)if __name__ == "__main__":parser = argparse.ArgumentParser()parser.add_argument("-t", "--type", type=str, default="resnet")parser.add_argument("-d", "--dir", type=str, default="../models/")opt = parser.parse_args()main(opt)

上述代码的主要功能是导出不同类型的预训练神经网络模型到 ONNX 格式,并对它们进行简化。它使用了 PyTorch 的 torchvision 库来获取各种常用的神经网络模型,然后将它们转换为 ONNX 格式,并使用 onnx-simplifier 进行优化。代码整合了命令行参数支持,以便用户可以选择导出的模型类型和保存位置。

执行后导出的 ONNX 如下图所示:

在这里插入图片描述

大家可以导出其它的一些模型,利用 Netron 看看它们的网络结构都长什么样的,有什么不同

3. 补充-ONNX

以下内容均 copy 自:Jetson嵌入式系列模型部署-1

3.1 概念

  • onnx 可以理解为一种通用货币,开发者可以把自己开发训练好的模型保存为onnx文件,而部署工程师可以借助部署框架(如 tensorRT、openvino、ncnn 等)部署在不同的硬件平台上,而不必关系开发者使用的是哪一种框架
  • onnx 的本质是一种 protobuf 格式文件
  • protobuf 通过编译 onnx-ml.proto 文件得到 onnx-ml.pb.h 和 onnx-ml.pb.cc 用于 C++ 调用或 onnx_ml_pb2.py 用于 python 调用,如下图所示。如果本地 python 环境下安装了 onnx 第三方库,则在该库下可以找到 onnx_ml_pb2.py 文件

在这里插入图片描述

  • 通过编译得到的 onnx-ml.pb.cc 和代码就可以操作 onnx 模型文件,实现对应的增删改
  • onnx-ml.proto 用于描述 onnx 文件是如何组成的,具有什么结构,它是 onnx 经常参照的东西,如下是 onnx-ml.proto 部分内容,参考自:https://github.com/shouxieai/tensorRT_Pro/blob/main/onnx/onnx-ml.proto

在这里插入图片描述

3.2 组成

onnx 文件组成如下图所示

在这里插入图片描述

  • model:表示整个 onnx 模型,包括图结构和解析器版本、opset 版本、导出程序类型
    • opset 版本即operator 版本号即 pytorch 的 op (操作算子)版本
  • model.graph:表示图结构,通常是 Netron 可视化工具中看到的结构
  • model.graph.node:表示图结构中所有节点如 conv、bn、relu 等
  • model.graph.initializer:权重数据大都存储在这里
  • model.graph.input:模型的输入
  • model.graph.output:模型的输出

总结

本次课程我们主要学习了利用 torch.onnx.export 函数将 Pytorch 模型导出为 ONNX 模型,并学习了利用 onnx-simplifier 来优化我们导出的 ONNX 模型。

OK,以上就是第 4 小节有关 ONNX 导出的全部内容了,下节我们来学习剖析 ONNX 架构并理解 Protobuf,敬请期待😄

参考

  • Netron
  • Jetson嵌入式系列模型部署-1
  • https://github.com/daquexian/onnx-simplifier

这篇关于三. TensorRT基础入门-导出并分析ONNX的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/984725

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联

poj 2104 and hdu 2665 划分树模板入门题

题意: 给一个数组n(1e5)个数,给一个范围(fr, to, k),求这个范围中第k大的数。 解析: 划分树入门。 bing神的模板。 坑爹的地方是把-l 看成了-1........ 一直re。 代码: poj 2104: #include <iostream>#include <cstdio>#include <cstdlib>#include <al

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

MySQL-CRUD入门1

文章目录 认识配置文件client节点mysql节点mysqld节点 数据的添加(Create)添加一行数据添加多行数据两种添加数据的效率对比 数据的查询(Retrieve)全列查询指定列查询查询中带有表达式关于字面量关于as重命名 临时表引入distinct去重order by 排序关于NULL 认识配置文件 在我们的MySQL服务安装好了之后, 会有一个配置文件, 也就