学习部署中的onnx知识并且通过swin transformer的onnx导出和使用onnx surgeon 进行YOLOV5 GPU optimization

本文围绕ONNX展开,先介绍导出ONNX所需检查的版本及主要SDK。接着详细阐述导出ONNX的方法,包括复习PyTorch、分析torch.export.onnx参数等。还提及多个输出头、动态形状等情况,以及onnxsim工具的使用。最后通过YOLOv5和Swin - Transformer两个实战案例,展示ONNX的优化与导出。

Onnx_Basic

一切开始前先检查自己的onnx版本

pip list | grep onnx

期待的4个主要的SDK是

onnx                          1.14.0
onnx-graphsurgeon             0.3.27
onnxruntime                   1.15.1
onnxsim                       0.4.33

3 学会如何导出ONNX, 分析ONNX

3.1 简单复习一下pytorch

定义一个模型, 这个模型实质上是一个线性层 (nn.Linear)。线性层执行的操作是 y = x * W^T + b,其中 x 是输入,W 是权重,b 是偏置。

class 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 x

定义一个infer的case, 权重的形状通常为 (out_features, in_features),这里复习一下矩阵相乘, 这里的in_features(X)的shape是[4], 而我们希望模型输出的是[3], 那么y = x * W^T + b可以知道W^T需要是[4, 3], nn.Linear会帮我们转置, 所以这里的W的shape是[3, 4]
在这里插入图片描述

def 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)

3.2 torch.export.onnx参数

这里就是infer完了之后export onnx, 重点看一下这里的参数,

  • model (torch.nn.Module): 需要导出的PyTorch模型,它应该是torch.nn.Module的一个实例。

  • args (tuple or Tensor): 一个元组,其中包含传递给模型的输入张量,用于确定ONNX图的结构。在您的代码中,您传递了一个包含一个张量的元组,这指示您的模型接受单个输入。

  • f (str): 要保存导出模型的文件路径。在您的代码中,该模型将被保存到“…/models/example.onnx”路径。

  • input_names (list of str): 输入节点的名字的列表。这些名字可以用于标识ONNX图中的输入节点。在您的代码中,您有一个名为“input0”的输入。

  • output_names (list of str): 输出节点的名字的列表。这些名字可以用于标识ONNX图中的输出节点。在您的代码中,您有一个名为“output0”的输出。

  • opset_version (int): 用于导出模型的ONNX操作集版本。

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")

当然可以。以下是torch.onnx.export函数中参数的解释:

torch.onnx.export(
    model         = model, 
    args          = (input,),
    f             = "../models/example.onnx",
    input_names   = ["input0"],
    output_names  = ["output0"],
    opset_version = 12)

3.3 多个输出头

在这里插入图片描述

模型的定义上就要有多个

class 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, x2

输出的时候只要更改output_names的参数就可以了

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的方式,参数有很多,也可以支持动态size
    torch.onnx.export(
        model         = model, 
        args          = (input,),
        f             = "../models/example_two_head.onnx",
        input_names   = ["input0"]
### 将Swin Transformer模型转换为ONNX格式并进行部署 #### 1. 准备工作 为了将Swin Transformer模型转换为ONNX格式,首先需要加载预训练的PyTorch模型,并通过`torch.onnx.export()`函数将其导出ONNX文件。以下是具体的操作流程: ```python import torch from swin_transformer import SwinTransformer # 假设这是您使用Swin Transformer实现类 # 加载预训练模型 model = SwinTransformer(img_size=224, patch_size=4, in_chans=3, embed_dim=96, depths=[2, 2, 6, 2], num_heads=[3, 6, 12, 24], window_size=7, drop_rate=0., attn_drop_rate=0., drop_path_rate=0.2, norm_layer=torch.nn.LayerNorm) state_dict = torch.load('path/to/pretrained_model.pth') model.load_state_dict(state_dict) model.eval() # 定义输入张量形状 dummy_input = torch.randn(1, 3, 224, 224) # 导出ONNX格式 torch.onnx.export(model, dummy_input, 'swin_transformer.onnx', opset_version=17, input_names=['input'], output_names=['output'], dynamic_axes={'input': {0: 'batch_size'}, 'output': {0: 'batch_size'}}) ``` 此代码片段展示了如何将Swin Transformer模型保存为ONNX格式[^1]。 --- #### 2. ONNX模型优化 在完成ONNX模型导出之后,可以进一步对其进行优化以提高性能兼容性。推荐使用ONNX Runtime工具来验证优化模型: ```bash pip install onnxruntime-gpu ``` 运行以下命令检查模型的有效性结构: ```bash onnx-check-model swin_transformer.onnx ``` 如果发现任何潜在问题,可以通过简化器(如`onnx-simplifier`)对模型进行清理压缩: ```bash pip install onnxsim onnxsim swin_transformer.onnx optimized_swin_transformer.onnx ``` 这一步骤有助于减少冗余操作节点,从而提升后续TensorRT引擎生成过程中的效率[^4]。 --- #### 3. 转换至TensorRT引擎 一旦获得了经过优化的ONNX模型,则可利用NVIDIA TensorRT库将其编译为目标平台上的高效推理引擎。下面是一个简单的Python脚本用于执行该任务: ```python import tensorrt as trt def build_engine(onnx_file_path, engine_file_path): TRT_LOGGER = trt.Logger(trt.Logger.WARNING) with trt.Builder(TRT_LOGGER) as builder, \ builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)) as network, \ trt.OnnxParser(network, TRT_LOGGER) as parser: config = builder.create_builder_config() config.max_workspace_size = 1 << 30 # 设置最大GPU临时内存大小 with open(onnx_file_path, 'rb') as model: if not parser.parse(model.read()): for error in range(parser.num_errors): print(parser.get_error(error)) return None engine = builder.build_serialized_network(network, config) with open(engine_file_path, "wb") as f: f.write(engine) build_engine("optimized_swin_transformer.onnx", "swin_transformer.trt") ``` 这段代码定义了一个名为`build_engine`的功能模块,它接受原始ONNX路径以及目标TRT引擎存储位置作为参数[^1]。 注意:对于某些特定场景下的动态批量尺寸支持可能引发错误,建议依据实际需求调整配置或者禁用相应选项[^4]。 --- #### 4. 推理测试 最后,在成功创建好TensorRT引擎后即可开展实时推断实验。这里给出一段基础示例供参考: ```python import numpy as np import pycuda.driver as cuda import pycuda.autoinit import tensorrt as trt class HostDeviceMem(object): def __init__(self, host_mem, device_mem): self.host = host_mem self.device = device_mem bindings = [] for binding in engine: size = trt.volume(engine.get_binding_shape(binding)) * engine.max_batch_size dtype = trt.nptype(engine.get_binding_dtype(binding)) host_mem = cuda.pagelocked_empty(size, dtype) device_mem = cuda.mem_alloc(host_mem.nbytes) bindings.append((int(device_mem),)) context.execute_v2(bindings) h_output = bindings[1][0] d_output = bindings[1][1] np.copyto(h_output, data.reshape(-1).astype(np.float32)) cuda.memcpy_htod(d_output, h_output) ``` 以上部分实现了基于CUDA上下文中调用已序列化的TensorRT Engine实例来进行前向传播计算的过程[^3]。 --- ### 结论 综上所述,从PyTorch到ONNX再到最终适配硬件加速框架(例如TensorRT),整个流水线涵盖了多个环节的技术要点。每步都需要仔细校验数据流一致性与精度损失情况,才能确保生产环境中稳定可靠的表现。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值