一文玩转pytorch转onnx-tensorRT ——(C)测试onnx转tensorRT

说明

前文讲到了如何在onnx注册自定义层,以便onnx parsing时找到所对应的层。
在示例demo前,贴出遇到的问题

  • 问题: ONNX and tensorRT: ERROR: Network must have at least one output
    • 开始当作正常bug去处理的,找到了一些参考资料:1)既然没有输出,就标记一个输出; 2)input可能出现的问题
    • 后来发现上面的方式对我的问题没有什么效果,最后经过巨量的微调后的debug,发现是因为在转onnx时强行修改了如下的代码,而upsample的修改导致结果不正确(ps:做研究,切莫随便捧别人臭脚,每一步都是有根据的才行,不然……)。实际上,这种写法不是对所有的pytorch(pytorch下的onnx)都是有效的,pytorch下的onnx有多个版本的。
import torch.onnx.symbolic
    # Override Upsample's ONNX export from old opset if required (not needed for TRT 5.1+)
    @torch.onnx.symbolic.parse_args('v', 'is')
    def upsample_nearest2d(g, input, output_size):
        height_scale = float(output_size[-2]) / input.type().sizes()[-2]
        width_scale = float(output_size[-1]) / input.type().sizes()[-1]
        return g.op("Upsample", input,
                    scales_f=(1, 1, height_scale, width_scale),
                    mode_s="nearest")
    @torch.onnx.symbolic.parse_args('v', 'is', 'i')
    def upsample_bilinear2d(g, input, output_size, align_corners):
        height_scale = float(output_size[-2]) / input.type().sizes()[-2]
        width_scale = float(output_size[-1]) / input.type().sizes()[-1]
        return g.op("Upsample", input,
                    scales_f=(1, 1, height_scale, width_scale),
                    mode_s="linear")
    torch.onnx.symbolic.upsample_bilinear2d = upsample_bilinear2d
    torch.onnx.symbolic.upsample_nearest2d = upsample_nearest2d

补充

后来的其他实验遇到的一些小bug,记录如下:

  • averagePool如果stride=1时(方便写block时会这么写),那么这一层没有实质意义的,但是转成的onnx,再转到tensorRT下时,会出现值检查的bug。这种情况可以直接在生成网络时,去掉这些层。当然,还有复杂一点点的,直接去掉可能导致一些逻辑的模型加载报错,所以可以用pad填充替代此层(当作占位符,用最简单的按序号加载参数时会用到)。

测试onnx转tensorRT

  • python代码
class gnSOLE(nn.Module):
    def __init__(self):
        super().__init__()
        out_channels = 2
        self.lr = nn.LeakyReLU()
        self.mp = nn.MaxPool2d((2, 2), stride=(2, 2))
        self.gn = nn.GroupNorm(num_groups=2, num_channels=out_channels, eps=1e-5)
        self.bn = nn.BatchNorm2d(out_channels)
        for m in self.modules():
            if isinstance(m, (nn.BatchNorm2d, nn.GroupNorm)):
                nn.init.constant_(m.weight, 1)
                nn.init.constant_(m.bias, 0)

    def forward(self, x):
        x1 = self.mp(x)
        x1 = self.gn(self.lr(x1))
        # x1 = upsample_to(x1, x)
        p4 = x1 + x
        return p4

def GN_ONNX():
    model = gnSOLE()
    model.to(torch.device('cuda'))
    zero_input = torch.rand(1, 2, 8, 8).cuda()
    # model(zero_input)
    torch.onnx.export(model, zero_input, 'gn.onnx', verbose=True)
  • c++代码。这儿包括编译方式,具体还可以参考“如何测试tensorRT添加自定义层”中的介绍。另外这里注释掉了common(tensorRT的demo中引用的头文件),注释代码仍旧保留,权当参考。
//nvcc -o gn test_onnx.cpp ../cuda/groupnorm.cu /usr/src/tensorrt/samples/common/logger.cpp
// -I/home/user/package/cub-1.8.0 -I/usr/src/tensorrt/samples/common/ -I./../cuda/ -L/usr/local/cuda/lib64
// -lcudart -lcuda -L/usr/local/lib/ -lnvonnxparser -L/usr/lib/x86_64-linux-gnu/ -lnvinfer
// -lnvparsers -lnvinfer_plugin
#include <algorithm>
#include <assert.h>
#include <cmath>
//#include <cuda_runtime_api.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <sys/stat.h>
#include <time.h>

#include "NvInfer.h"
#include <NvOnnxParser.h>
#include "logger.h"
//#include "common.h"
#include "GN.h"

#define CHECK(status)                                          \
    do                                                         \
    {                                                          \
        auto ret = (status);                                   \
        if (ret != 0)                                          \
        {                                                      \
            std::cout << "Cuda failure: " << ret << std::endl; \
            abort();                                           \
        }                                                      \
    } while (0)
using namespace nvinfer1;

static const int INPUT_H = 28;
static const int INPUT_W = 28;
static const int OUTPUT_SIZE = 10;

const std::string gSampleName = "TensorRT.sample_onnx_mnist";

bool onnxToTRTModel(const std::string &modelFile, // name of the onnx model
                    unsigned int maxBatchSize,    // batch size - NB must be at least as large as the batch we want to run with
                    IHostMemory *&trtModelStream) // output buffer for the TensorRT model
{
    // create the builder
    IBuilder *builder = createInferBuilder(gLogger.getTRTLogger());
    assert(builder != nullptr);
    nvinfer1::INetworkDefinition *network = builder->createNetwork();

    auto parser = nvonnxparser::createParser(*network, gLogger.getTRTLogger());
    //Optional - uncomment below lines to view network layer information
    //config->setPrintLayerInfo(true);
    //parser->reportParsingInfo();
    std::cout << modelFile << std::endl;
    if (!parser->parseFromFile(modelFile.c_str(),
                               static_cast<int>(gLogger.getReportableSeverity()))) {
        gLogError << "Failure while parsing ONNX file" << std::endl;
        return false;
    }

    // Build the engine
    builder->setMaxBatchSize(maxBatchSize);
    builder->setMaxWorkspaceSize(1 << 20);
//    builder->setFp16Mode(gArgs.runInFp16);
//    builder->setInt8Mode(gArgs.runInInt8);
//
//    if (gArgs.runInInt8) {
//        samplesCommon::setAllTensorScales(network, 127.0f, 127.0f);
//    }
//
//    samplesCommon::enableDLA(builder, gArgs.useDLACore);

    ICudaEngine *engine = builder->buildCudaEngine(*network);
    assert(engine);

    // we can destroy the parser
    parser->destroy();

    // serialize the engine, then close everything down
    trtModelStream = engine->serialize();
    engine->destroy();
    network->destroy();
    builder->destroy();

    return true;
}

void doInference(IExecutionContext &context, float *input, float *output, int batchSize) {
    const ICudaEngine &engine = context.getEngine();
    // input and output buffer pointers that we pass to the engine - the engine requires exactly IEngine::getNbBindings(),
    // of these, but in this case we know that there is exactly one input and one output.
    assert(engine.getNbBindings() == 2);
    void *buffers[2];

    // In order to bind the buffers, we need to know the names of the input and output tensors.
    // note that indices are guaranteed to be less than IEngine::getNbBindings()
    int inputIndex{}, outputIndex{};
    for (int b = 0; b < engine.getNbBindings(); ++b) {
        if (engine.bindingIsInput(b))
            inputIndex = b;
        else
            outputIndex = b;
    }

    // create GPU buffers and a stream
    CHECK(cudaMalloc(&buffers[inputIndex], batchSize * INPUT_H * INPUT_W * sizeof(float)));
    CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float)));

    cudaStream_t stream;
    CHECK(cudaStreamCreate(&stream));

    // DMA the input to the GPU,  execute the batch asynchronously, and DMA it back:
    CHECK(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * INPUT_H * INPUT_W * sizeof(float),
                          cudaMemcpyHostToDevice, stream));
    context.enqueue(batchSize, buffers, stream, nullptr);
    CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost,
                          stream));
    cudaStreamSynchronize(stream);

    // release the stream and the buffers
    cudaStreamDestroy(stream);
    CHECK(cudaFree(buffers[inputIndex]));
    CHECK(cudaFree(buffers[outputIndex]));
}

//!
//! \brief This function prints the help information for running this sample
//!
void printHelpInfo() {
    std::cout
            << "Usage: ./sample_onnx_mnist [-h or --help] [-d or --datadir=<path to data directory>] [--useDLACore=<int>]\n";
    std::cout << "--help          Display help information\n";
    std::cout
            << "--datadir       Specify path to a data directory, overriding the default. This option can be used multiple times to add multiple directories. If no data directories are given, the default is to use (data/samples/mnist/, data/mnist/)"
            << std::endl;
    std::cout
            << "--useDLACore=N  Specify a DLA engine for layers that support DLA. Value can range from 0 to n-1, where n is the number of DLA engines on the platform."
            << std::endl;
    std::cout << "--int8          Run in Int8 mode.\n";
    std::cout << "--fp16          Run in FP16 mode." << std::endl;
}

int main(int argc, char **argv) {
    auto sampleTest = gLogger.defineTest(gSampleName, argc, const_cast<const char **>(argv));
    gLogger.reportTestStart(sampleTest);
    // create a TensorRT model from the onnx model and serialize it to a stream
    IHostMemory *trtModelStream{nullptr};
    if (!onnxToTRTModel("/home/user/weight/gn.onnx", 1, trtModelStream))
        std::cerr << "can not read onnx!";

    assert(trtModelStream != nullptr);

    uint8_t fileData[INPUT_H * INPUT_W];
    // print an ascii representation
    gLogInfo << "Input:\n"
//    float data[INPUT_H * INPUT_W];
//    for (int i = 0; i < INPUT_H * INPUT_W; i++)
//        data[i] = 1.0 - float(fileData[i] / 255.0);
//
//    // deserialize the engine
//    IRuntime *runtime = createInferRuntime(gLogger);
//    assert(runtime != nullptr);
//    if (gArgs.useDLACore >= 0) {
//        runtime->setDLACore(gArgs.useDLACore);
//    }
//
//    ICudaEngine *engine = runtime->deserializeCudaEngine(trtModelStream->data(), trtModelStream->size(), nullptr);
//    assert(engine != nullptr);
//    trtModelStream->destroy();
//    IExecutionContext *context = engine->createExecutionContext();
//    assert(context != nullptr);
//    // run inference
//    float prob[OUTPUT_SIZE];
//    doInference(*context, data, prob, 1);
//
//    // destroy the engine
//    context->destroy();
//    engine->destroy();
//    runtime->destroy();
//*********************************
//    float val{0.0f};
//    int idx{0};
//
//    //Calculate Softmax
//    float sum{0.0f};
//    for (int i = 0; i < OUTPUT_SIZE; i++)
//    {
//        prob[i] = exp(prob[i]);
//        sum += prob[i];
//    }
//
//    gLogInfo << "Output:\n";
//    for (int i = 0; i < OUTPUT_SIZE; i++)
//    {
//        prob[i] /= sum;
//        val = std::max(val, prob[i]);
//        if (val == prob[i])
//            idx = i;
//
//        gLogInfo << " Prob " << i << "  " << std::fixed << std::setw(5) << std::setprecision(4) << prob[i] << " "
//                 << "Class " << i << ": " << std::string(int(std::floor(prob[i] * 10 + 0.5f)), '*') << "\n";
//    }
//    gLogInfo << std::endl;
//
//    bool pass{idx == num && val > 0.9f};
//
//    return gLogger.reportTest(sampleTest, pass);
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: PyTorch是一个深度学习框架,可以用来构建神经网络模型。ONNX是一种开放的神经网络交换格式,可以将不同框架的模型换为统一的格式。TensorRT是NVIDIA推出的用于加速深度学习推理的库,可以将模型换为高效的C++代码。因此,将PyTorch模型换为ONNX格式,再将ONNX模型换为TensorRT格式,可以提高模型的推理速度和效率。 ### 回答2: Pytorch是一款非常流行的深度学习框架之一,而ONNX(Open Neural Network Exchange)则是一种用于在不同深度学习框架之间交换模型的标准格式。TensorRT是基于NVIDIA GPU优化的深度学习推理引擎,可以加速深度学习模型的预测速度。PytorchONNXTensorRT的过程主要包括以下几个步骤: 第一步,使用Pytorch训练好的模型可以通过Pytorch提供的方法将模型化为ONNX格式的模型。这一步通常需要在节省内存的情况下,对训练的模型进行优化并减少其大小。Pytorch提供了一些方法,例如ONNX的exporter,可以在不损失精度的情况下将Pytorch模型换为ONNX格式。 第二步,将ONNX模型换为TensorRT格式的模型。这一步通常需要使用TensorRT提供的工具将ONNX格式的模型换为可以使用TensorRT来加速推理的格式。TensorRT可以根据模型的结构,对其进行优化,从而提高推理的速度和效率。 第三步,使用TensorRT引擎来进行推理。在这一步中,可以使用一些Python或C++的API来调用TensorRT引擎,以加速推理的过程。通常情况下,TensorRT会在NVIDIA GPU上运行,以提高推理的速度和效率。 总之,PytorchONNXTensorRT是一种很常见的深度学习模型加速优化的方法。通过这种方法,可以将训练好的模型化为可以快速进行推理的格式,并提高推理的速度和效率,从而更好的满足实际应用需求。同时也可以加深对于PytorchONNXTensorRT的理解和应用。 ### 回答3: 先介绍一下三个工具的概念。 PyTorch是一个基于Python的库,提供了高度可拓展性和可定制化的机器学习算法、模型和数据处理工具,特别适合用于深度学习。 ONNX(Open Neural Network Exchange)是一个开放的模型表示和换工具,使得不同框架之间的模型换和协作更加容易。ONNX 可以将每个框架的模型表示换为通用表示格式,这样就可以一次性完成跨框架的模型部署。 TensorRT是NVIDIA推出的高性能深度学习推理库,可以对训练好的深度学习模型进行优化,并在NVIDIA GPU上加速模型的推理过程。使用TensorRT能够提升模型的运行速度和效率。 下面是如何将PyTorch模型TensorRT模型的过程: 1.将PyTorch模型化为ONNX格式。有两种方法可以实现这一步骤:一种是使用PyTorch官方提供的torch.onnx.export方法,将PyTorch模型化为ONNX格式;另一种方法是使用ONNX官方提供的onnx-coreml换工具。 2.使用TensorRT提供的工具trtexec将ONNX模型化为TensorRT格式。通过命令行的方式调用trtexec,在将模型换为TensorRT格式的同时,可以指定一些优化参数,比如设置合适的batch size、设置推理设备的数据类型、设置最大批处理等参数,以提高模型的性能和效率。 3.将换后的模型导入到TensorRT库中,使用C++或Python等支持TensorRT的代码进行推理。 总体而言,PyTorchONNXONNXTensorRT这一过程需要先对PyTorch模型进行格式化,再将ONNX格式的模型化为TensorRT格式,最终通过TensorRT库对模型进行推理。需要注意的是,不同的深度学习模型在换过程中有着各自的特点和难点,需要根据具体情况进行优化和调整。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值