Pytorch は onnx モデルをエクスポートし、C++ を TensorRT に変換し、推論を実装します。

イベントアドレス:卒業シーズン・テクノロジーへの攻撃er

Pytorch は onnx モデルをエクスポートし、C++ を TensorRT に変換し、推論を実装します。

この記事は学習メモです。参考文献と相違がある場合は、黄色それをマークしてください。

主な参考文献:
1. Pytorch は onnx モデルをエクスポートし、C++ はそれを TensorRT に変換し、推論プロセスを実装します。
2. Onnxruntime のインストールと使用 (実際にはいくつかの問題が見つかりました)
3. TensorRT_Test

1.Pytorch エクスポート onnx モデル

  1. 新しいexport_onnx.pyファイルを作成します。完全な内容は次のとおりです。ResNet50_wSoftmax カスタム モデルは、公式のオリジナルに基づいており、softmax 操作が追加されています。

  2. 必要な後処理をモデルに追加して一緒にエクスポートすると、次の 2 つの利点があります: 1 )外部で後処理を行わずに
    、エンドツーエンドの onnx/tensorrt モデルを直接取得できます。onnx モデルから tensorrt モデルへの変換プロセス中、tensorrt は特定の Nvidia GPU のモデルに対して推論の最適化後処理を onnx モデルにマージします。これにより、一部のオペレーター操作を tensorrt に変換できる可能性があります。 . 最適化されます。

# export_onnx.py
import torch
import torchvision.models as models
import cv2
import numpy as np

class ResNet50_wSoftmax(torch.nn.Module):
    # 将softmax后处理合并到模型中,一起导出为onnx
    def __init__(self):
        super().__init__()
        self.base_model = models.resnet50(pretrained=True)
        self.softmax = torch.nn.Softmax(dim=1)

    def forward(self, x):
        y = self.base_model(x)
        prob = self.softmax(y)
        return prob

def preprocessing(img):
    # 预处理:BGR->RGB、归一化/除均值减标准差
    IMAGENET_MEAN = [0.485, 0.456, 0.406]
    IMAGENET_STD = [0.229, 0.224, 0.225]
    img = img[:, :, ::-1]
    img = cv2.resize(img, (224, 224))
    img = img / 255.0
    img = (img - IMAGENET_MEAN) / IMAGENET_STD
    img = img.transpose(2, 0, 1).astype(np.float32)
    tensor_img = torch.from_numpy(img)[None] # 此处增加一维, 从[3,224,224] 到 [1, 3,224,224] 
    return tensor_img

if __name__ == '__main__':
    # model = models.resnet50(pretrained=True)
    image_path = 'test.jpg'
    img = cv2.imread(image_path)
    tensor_img = preprocessing(img)
    model = ResNet50_wSoftmax()   # 将后处理添加到模型中
    model.eval()
    pred = model(tensor_img)[0]
    max_idx = torch.argmax(pred)
    print(f"test_image: {image_path}, max_idx: {max_idx}, max_logit: {pred[max_idx].item()}")

    dummpy_input = torch.zeros(1, 3, 224, 224)  # onnx的导出需要指定一个输入,这里直接用上面的tenosr_img也可
    torch.onnx.export(
            model, dummpy_input, 'resnet50_wSoftmax.onnx',
            input_names=['image'],
            output_names=['predict'],
            opset_version=11,
            dynamic_axes={'image': {0: 'batch'}, 'predict': {0: 'batch'}}		# 注意这里指定batchsize是动态可变的
    )
  1. スクリプトを実行します。
python export_onnx.py

: 操作の前に、test.jpg という名前の画像を準備する必要があります。インターネットから画像をダウンロードするだけで、次のような同様の出力結果が表示されます。

Downloading: "https://download.pytorch.org/models/resnet50-0676ba61.pth" to /home/cui/.cache/torch/hub/checkpoints/resnet50-0676ba61.pth
100%|██████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 97.8M/97.8M [07:36<00:00, 225kB/s]
test_image: test.jpg, max_idx: 285, max_logit: 0.5382498502731323

2. onnxruntime 推論テスト

私たちはただ手に入れますresnet50_wSoftmax.onnx、onnxruntime を使用して推論テストを実行し、結果が同じかどうかを確認します。

ここで新しいものを作成します。onnxruntime_test.pyファイルの完全な内容は次のとおりです。このファイルでは、export_onnx.py の前処理操作が再利用されていますが、元の関数の出力結果は torch であるため、これは次のようにする必要があります。numpy 型に変換する

# onnxruntime_test.py
import onnxruntime as ort  #若缺少,使用pip install onnxruntime-gpu -i https://pypi.tuna.tsinghua.edu.cn/simple高速安装 
import numpy as np
import cv2
from export_onnx import preprocessing

image_path = 'test.jpg'
ort_session = ort.InferenceSession("resnet50_wSoftmax.onnx", providers=['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']) # 创建一个推理session

img = cv2.imread(image_path)
input_img = preprocessing(img)[None]
input_img = input_img.numpy() ## 新添加

pred = ort_session.run(None, { 'image' : input_img } )[0][0]
max_idx = np.argmax(pred)
print(f"test_image: {image_path}, max_idx: {max_idx}, probability: {pred[max_idx]}")

埋め込む:

python onnxruntime_test.py

結果は以下の通りで、pytorch モデルの推論と基本的に一致しており、変換に問題がないことが証明されています。

test_image: test.jpg, max_idx: 285, probability: 0.5382504463195801

注意:
1) このファイルort.InferenceSession のパラメータリファレンスから少し変更があり、本家サイトのスクリプトを直接実行すると以下のエラーが発生します。
解決策の参照: [ValueError: この ORT ビルドには'TensorrtExecutionProvider'、'CUDAExecutionProvider'、'CPUExecutionP があります

ValueError: This ORT build has ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider'] enabled. Since ORT 1.9, you are required to explicitly set the providers parameter when instantiating InferenceSession. For example, onnxruntime.InferenceSession(..., providers=['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider'], ...)

2) RuntimeError: 入力は辞書のリストまたは単一の numpy 配列である必要があります。元のテキストで直接推論を実行すると、「画像」が表示されます。解決策の参照: RuntimeError: 入力は辞書のリストまたは単一の numpy 配列である必要があり
ます。入力用 '画像

3) 元のテキストでエクスポートされた onnx モデルは int64 重みであり、TensorRT は 64 ビットをサポートしていませんが、その後の使用には影響しません。現在、32 ビットのみがサポートされており、次の変換時に関連する型の警告が報告されます。

Your ONNX model has been generated with INT64 weights, while TensorRT does not natively support INT64. Attempting to cast down to INT32

参考:onnx-typecast 数字変換ツール

3. onnx モデルを tensorrt モデルに変換します

1. 環境のインストール

CUDA10.2+Cudnn8.4.1+TensorRT7.1.3.4+Onnx-tensorrt7.1、詳細なインストールについては、環境構成インストールの記事を参照してください。ここでは、特に拡張する必要はありません。たとえば、次のとおりです。

:バージョン間に対応関係がなければなりません。そうでないと、不可解なエラーが発生します。すべてのインストールが完了したら、次のコマンドを使用してバージョンを確認します。

nvcc -V    # 查看cuda版本,10.2
cat /usr/local/cuda-10.2/include/cudnn_version.h | grep CUDNN_MAJOR -A 2    # 查看cudnn8.4版本,与之前版本稍有不同,可参考第一个链接安装cudnn
find / -name NvInferVersion.h   # 查看tensorRT版本号

2. フォルダーの作成

主にヘッダー ファイル、ロガー クラス、build_model 関数などを含む build_model.cc の新しい C++ ファイルを作成します。

  • Logger クラス: tensorrt モデルの構築プロセスでエラーまたは警告を出力します。指定された重大度(severity)に従って、情報を出力します。
  • build_model 関数: onnx を使用してビルド、エクスポートするエンジン型式(trt モデルと同等、どちらもシリアル化されたバイナリ ファイル)

2.1 ファイルの完全な内容は次のとおりです。

// tensorrt相关
#include <NvInfer.h>
#include <NvInferRuntime.h>
#include <NvInferRuntimeCommon.h>  //新添加,否则找不到nvinfer1::AsciiChar

// onnx解析器相关
#include <NvOnnxParser.h>  // 与原文不同,onnx-tensorrt build后,sudo make install

// cuda_runtime相关
#include <cuda_runtime.h>

// 常用头文件
#include <math.h>
#include <stdio.h>
#include <unistd.h>
#include <chrono>
#include <fstream>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <dirent.h>

// opencv
#include <opencv2/opencv.hpp>

inline const char* severity_string(nvinfer1::ILogger::Severity t) {
  switch (t) {
    case nvinfer1::ILogger::Severity::kINTERNAL_ERROR:
      return "internal_error";
    case nvinfer1::ILogger::Severity::kERROR:
      return "error";
    case nvinfer1::ILogger::Severity::kWARNING:
      return "warning";
    case nvinfer1::ILogger::Severity::kINFO:
      return "info";
    case nvinfer1::ILogger::Severity::kVERBOSE:
      return "verbose";
    default:
      return "unknown";
  }
}

class TRTLogger : public nvinfer1::ILogger {
 public:
  virtual void log(Severity severity, const char* msg) noexcept override {
    if (severity <= Severity::kWARNING) {
      if (severity == Severity::kWARNING)
        printf("\033[33m%s: %s\033[0m\n", severity_string(severity), msg);
      else if (severity == Severity::kERROR)
        printf("\031[33m%s: %s\033[0m\n", severity_string(severity), msg);
      else
        printf("%s: %s\n", severity_string(severity), msg);
    }
  }
};

bool build_model() {
  if (access("resnet50.engine", 0) == 0) {
    printf("resnet50.engine already exists.\n");
    return true;
  }

  TRTLogger logger;

  // 下面的builder, config, network是基本需要的组件
  // 形象的理解是你需要一个builder去build这个网络,网络自身有结构,这个结构可以有不同的配置
  nvinfer1::IBuilder* builder = nvinfer1::createInferBuilder(logger);
  // 创建一个构建配置,指定TensorRT应该如何优化模型,tensorRT生成的模型只能在特定配置下运行
  nvinfer1::IBuilderConfig* config = builder->createBuilderConfig();
  // 创建网络定义,其中createNetworkV2(1)表示采用显性batch
  // size,新版tensorRT(>=7.0)时,不建议采用0非显性batch size
  nvinfer1::INetworkDefinition* network = builder->createNetworkV2(1);

  // onnx parser解析器来解析onnx模型
  auto parser = nvonnxparser::createParser(*network, logger);
  if (!parser->parseFromFile("../resnet50_wSoftmax.onnx", 1)) {
    printf("Failed to parse resnet50_wSoftmax.onnx.\n");
    return false;
  }

  // 设置工作区大小
  printf("Workspace Size = %.2f MB\n", (1 << 28) / 1024.0f / 1024.0f);
  config->setMaxWorkspaceSize(1 << 28);

  // 需要通过profile来使得batchsize时动态可变的,这与我们之前导出onnx指定的动态batchsize是对应的
  int maxBatchSize = 10;
  auto profile = builder->createOptimizationProfile();
  auto input_tensor = network->getInput(0);
  auto input_dims = input_tensor->getDimensions();

  // 设置batchsize的最大/最小/最优值
  input_dims.d[0] = 1;
  profile->setDimensions(input_tensor->getName(),
                         nvinfer1::OptProfileSelector::kMIN, input_dims);
  profile->setDimensions(input_tensor->getName(),
                         nvinfer1::OptProfileSelector::kOPT, input_dims);

  input_dims.d[0] = maxBatchSize;
  profile->setDimensions(input_tensor->getName(),
                         nvinfer1::OptProfileSelector::kMAX, input_dims);
  config->addOptimizationProfile(profile);

  // 开始构建tensorrt模型engine
  nvinfer1::ICudaEngine* engine =
      builder->buildEngineWithConfig(*network, *config);

  if (engine == nullptr) {
    printf("Build engine failed.\n");
    return false;
  }

  // 将构建好的tensorrt模型engine反序列化(保存成文件)
  nvinfer1::IHostMemory* model_data = engine->serialize();
  FILE* f = fopen("resnet50.engine", "wb");
  fwrite(model_data->data(), 1, model_data->size(), f);
  fclose(f);

  // 逆序destory掉指针
  model_data->destroy();
  engine->destroy();
  network->destroy();
  config->destroy();
  builder->destroy();

  printf("Build Done.\n");
  return true;
}

int main() {
  if (!build_model()) {
    printf("Couldn't build engine!\n");
  }
  return 0;
}

2.2 Cmakelists.txt ファイル
完全なプロジェクト リファレンス: TensorRT_Test

2.3 コンパイルと実行

mkdir build && cd build
cmake ..
make -j8
./build_model

2.4 結果
以下の状況が発生し、生成されましたrsenet50.エンジンファイルの変換は成功しました。

warning: [TRT]/home/cui/workspace/tools/onnx-tensorrt/onnx2trt_utils.cpp:220: Your ONNX model has been generated with INT64 weights, while TensorRT does not natively support INT64. Attempting to cast down to INT32.
Workspace Size = 256.00 MB
Build Done.

2.5 考えられる問題
Q1 : エラー: 'nvinfer1::AsciiChar' が宣言されていません
解決策: tensorRT バージョンの問題、const char* に変更します。

Q2 : エラー: ファイル resnet50_wSoftmax.onnx を開けませんでした。
解決策: build_model.cc ファイルの 78 行目、resnet50_wSoftmax.onnx モデル ファイルの対応するパスを変更します。


4. TensorRT モデル推論テスト

目的: 以前の Pytorch モデルと onnx モデルの推論結果が一致しているかどうかを検証するため。

  1. ファイルの完全な内容については、プロジェクトを参照してください。モデル.infer.cc元のテキストからわずかに変更を加えたドキュメント。増加したチェックランタイム達成:
#ifndef checkRuntime
#define checkRuntime(callstr)\
    {\
        cudaError_t error_code = callstr;\
        if (error_code != cudaSuccess) {\
            std::cerr << "CUDA error " << error_code << " at " << __FILE__ << ":" << __LINE__ << std::endl;\
            assert(0);\
        }\
    }
#endif  // checkRuntime

プロジェクトアドレス: TensorRT_Test

  1. コンパイルして実行する
mkdir build && cd build
cmake ..
make -j8
./model_infer
  1. 結果
test_image: ../test.jpg, max_idx: 285, probability: 0.538250(learning3d)
  1. 発生する可能性がある問題
    Q1 : 共有ライブラリのロード中にエラーが発生しました: libcudnn.so.8: 共有オブジェクト ファイルを開けません: そのようなファイルまたはディレクトリはありません
    解決策: cuda インストール環境が見つかりませんでした。conda によって構成された特定の環境に入ることができます。 , in 適切なアクションを実行してください。または、CUDAのインストール環境を参照し、環境変数に設定します。

おすすめ

転載: blog.csdn.net/weixin_36354875/article/details/125596653