深度學(xué)習(xí)模型C++部署TensorRT-創(chuàng)新互聯(lián)

一?簡(jiǎn)介:

TensorRT是一個(gè)高性能的深度學(xué)習(xí)推理(Inference)優(yōu)化器,可以為深度學(xué)習(xí)應(yīng)用提供低延遲、高吞吐率的部署推理。TensorRT可用于對(duì)超大規(guī)模數(shù)據(jù)中心、嵌入式平臺(tái)或自動(dòng)駕駛平臺(tái)進(jìn)行推理加速。TensorRT現(xiàn)已能支持TensorFlow、Caffe、Mxnet、Pytorch等幾乎所有的深度學(xué)習(xí)框架,將TensorRT和NVIDIA的GPU結(jié)合起來(lái),能在幾乎所有的框架中進(jìn)行快速和高效的部署推理。

創(chuàng)新互聯(lián)公司專注于安陸網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供安陸營(yíng)銷型網(wǎng)站建設(shè),安陸網(wǎng)站制作、安陸網(wǎng)頁(yè)設(shè)計(jì)、安陸網(wǎng)站官網(wǎng)定制、微信小程序服務(wù),打造安陸網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供安陸網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。

在平時(shí)的工作與學(xué)習(xí)中也都嘗試過(guò)使用Libtorch和onnxruntime的方式部署過(guò)深度學(xué)習(xí)模型。但這兩款多多少少存在著內(nèi)存與顯存占用的問(wèn)題,并且無(wú)法完全釋放。(下文的部署方式不僅簡(jiǎn)單并且在前向推理過(guò)程所需的顯存更低,并且在推理結(jié)束后可以隨時(shí)完全釋放顯存)。

二?安裝: 1.安裝環(huán)境

win10

vs2019

cuda10.2

pytorch1.9

只要其中的pytorch,cuda版本與后續(xù)的Tensorrt版本對(duì)應(yīng)即可

2.模型轉(zhuǎn)化

首先需要將pytorch的.pth模型轉(zhuǎn)化為onnx的模型(為了后邊的方便,目前講解的方式都是單卡的方式)。轉(zhuǎn)化方式很簡(jiǎn)單pytorch已經(jīng)提供,網(wǎng)上也有許多講解這個(gè)函數(shù)的博客。此處直接上代碼:(必須確定.pth模型是可以正常使用的否則后面轉(zhuǎn)化的模型也都無(wú)法使用)。

def Convert_ONNX(model,input_size):
    model.eval()
    dummy_input = torch.randn(input_size).cuda()

    torch.onnx.export(model,         # model being run    
         dummy_input,       # model input (or a tuple for multiple inputs) 
         "PytorchtoOnnx.onnx",       # where to save the model  
#           dynamic_axes = {'inputs':{0:"batch"}},   #表示batch這個(gè)維度可變的  (有這個(gè)參數(shù)可以關(guān)閉不用設(shè)置)會(huì)麻煩很多
         verbose = True,
         export_params=True,  # store the trained parameter weights inside the model file 
         input_names = ['inputs'],   # the model's input names 
         output_names = ['modelOutput']) # the model's output names 
    print("end")
3.將onnx模型通過(guò)tensorrt自帶工具完成轉(zhuǎn)化

首先去官網(wǎng)https://developer.nvidia.com/nvidia-tensorrt-download下載與自己pytorch版本和cuda版本適應(yīng)的tensorrt版本。

下載完成后打開(kāi)里面的bin文件夾,里面存在著一個(gè)trtexec.exe。利用以下代碼將之前獲得的onnx文件轉(zhuǎn)化為trt文件。這里講解最簡(jiǎn)單的方式,因?yàn)閠rtexec.exe有許多可以優(yōu)化的功能,最終都會(huì)影響模型的精度與速度。在命令行中輸入如下指令。

trtexec.exe --onnx=PytorchtoOnnx.onnx --saveEngine=TrtModel.trt --explicitBatch --workspace=4096

這里模型轉(zhuǎn)化可能需要一點(diǎn)時(shí)間,完成轉(zhuǎn)化后會(huì)得到TrtMedel.trt模型。那么準(zhǔn)備工作也就完成了。接下來(lái)開(kāi)始C++部署。

三?部署: 1.打開(kāi)VS新建空項(xiàng)目 2.配置環(huán)境

在VC++目錄---包含目錄中添加cuda路徑和tensorrt路徑(此處用的是相對(duì)路徑,你也可以用絕對(duì)路徑)其中$(CUDA_PATH)\include是指cuda中的include文件,我的在C:\Program?Files\NVIDIA?GPU?Computing?Toolkit\CUDA\v10.2\include

在VC++目錄---庫(kù)目錄中添加

還需要將字符集改成:使用多字節(jié)字符集。否則會(huì)報(bào)C2664?“HMODULE?LoadLibraryW(LPCWSTR)”:?無(wú)法將參數(shù)?1?從“const?_Elem?*”轉(zhuǎn)換為“LPCWSTR”??的錯(cuò)誤。

3.部署代碼

必須新建一個(gè)logger.cpp的文件。里面寫(xiě)入如下代碼。

#include "logger.h"
#include "ErrorRecorder.h"
#include "logging.h"

SampleErrorRecorder gRecorder;
namespace sample
{
Logger gLogger{Logger::Severity::kINFO};
LogStreamConsumer gLogVerbose{LOG_VERBOSE(gLogger)};
LogStreamConsumer gLogInfo{LOG_INFO(gLogger)};
LogStreamConsumer gLogWarning{LOG_WARN(gLogger)};
LogStreamConsumer gLogError{LOG_ERROR(gLogger)};
LogStreamConsumer gLogFatal{LOG_FATAL(gLogger)};

void setReportableSeverity(Logger::Severity severity)
{
    gLogger.setReportableSeverity(severity);
    gLogVerbose.setReportableSeverity(severity);
    gLogInfo.setReportableSeverity(severity);
    gLogWarning.setReportableSeverity(severity);
    gLogError.setReportableSeverity(severity);
    gLogFatal.setReportableSeverity(severity);
}
} // namespace sample

再新建一個(gè)test.cpp進(jìn)行測(cè)試。測(cè)試代碼如下:(下面是一個(gè)unet的分割模型,并且前面的預(yù)處理例如標(biāo)準(zhǔn)化等都在外面完成了。下面只涉及到推理部分)。

#include#include 
#includeusing namespace std;
#include#include "NvInfer.h"
#include "argsParser.h"
#include "logger.h"
#include "common.h"
#include "NvOnnxParser.h"
#include "buffers.h"
using namespace nvinfer1;

bool read_TRT_File(const std::string& engineFile, ICudaEngine*& engine)
{
  fstream file;
  file.open(engineFile, ios::binary | ios::in);
  file.seekg(0, ios::end);                     // 定位到 fileObject 的末尾
  int length = file.tellg();
  file.seekg(0, std::ios::beg);                // 定位到 fileObject 的開(kāi)頭
  unique_ptrdata(new char[length]);
  file.read(data.get(), length);
  file.close();

  nvinfer1::IRuntime* trtRuntime = createInferRuntime(sample::gLogger.getTRTLogger());
  engine = trtRuntime->deserializeCudaEngine(data.get(), length, nullptr);
  assert(engine != nullptr);
  std::cout<< "The engine in TensorRT.cpp is not nullptr"<< std::endl;
  //trtModelStream = engine->serialize();
  trtRuntime->destroy();
  return true;
}
void doInference(IExecutionContext& context, float* input, float* output,int InputSize, int OutPutSize,int BatchSize)
{

  const char* INPUT_BLOB_NAME = "inputs";
  const char* OUTPUT_BLOB_NAME = "modelOutput";

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

  const int inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME);
  const int outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);

  // DebugP(inputIndex); DebugP(outputIndex);
  // create GPU buffers and a stream
  CHECK(cudaMalloc(&buffers[inputIndex], InputSize * sizeof(float)));
  CHECK(cudaMalloc(&buffers[outputIndex], OutPutSize * 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, InputSize * sizeof(float), cudaMemcpyHostToDevice, stream));
  context.enqueue(BatchSize, buffers, stream, nullptr);
  CHECK(cudaMemcpyAsync(output, buffers[outputIndex], OutPutSize * sizeof(float), cudaMemcpyDeviceToHost, stream));
  cudaStreamSynchronize(stream);

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

void runs(short* Data, int ImageCol, int ImageRow, int ImageLayer, unsigned char* Outputs)
{
  int numall = ImageCol * ImageRow * ImageLayer;
  float* PatchData = new float[numall];
  Process(Data, ImageCol, ImageRow, ImageLayer, PatchData);   //前處理

  string eigineFile = "TrtMedel.trt";
  ICudaEngine* engine = nullptr;
  read_TRT_File(eigineFile, engine);

  IExecutionContext* context = engine->createExecutionContext();
  assert(context != nullptr);

  float* out_image = new float[3 * numall];
  int batchsize = 1;
  int InputSize = 1 * 1 * numall;
  int OutputSize = 1 * 3 * numall;
  doInference(*context, PatchData, out_image, InputSize, OutputSize, batchsize);
  EndProcess(out_image, 3, ImageCol, ImageRow, ImageLayer, Outputs);   //后處理


  context->destroy();
  engine->destroy();
  cudaDeviceReset();      
  delete[] out_image;
  delete[] PatchData;
  cout<<"柯西的筆"<
四?總結(jié)

如上代碼可以即可以正常運(yùn)行編譯。目前Tensorrt只支持20系以上顯卡,在10系顯卡也可以部署但是并沒(méi)有什么明顯的加速效果,但是顯存還是會(huì)比其他部署模塊低。

?如何涉及到整個(gè)項(xiàng)目完整在無(wú)CUDA環(huán)境中的部署,其實(shí)也很簡(jiǎn)單。有需求的可以私信我。(也請(qǐng)關(guān)注柯西的筆公眾。

你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購(gòu),新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧

名稱欄目:深度學(xué)習(xí)模型C++部署TensorRT-創(chuàng)新互聯(lián)
鏈接URL:http://www.muchs.cn/article10/dsoego.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、網(wǎng)站維護(hù)、網(wǎng)站導(dǎo)航、靜態(tài)網(wǎng)站、電子商務(wù)、建站公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)