win10 및 C++에서 tensorrt를 사용하여 하나 이상의 GPU에서 여러 모델을 동시에 로드

        이 기사를 읽기 전에 Win10에서 tensorrt와 함께 yolov5를 배포하는 방법에 대한 제 강의 기사를 읽으십시오. 그러면 돌아와서 이 기사를 읽는 것이 더 쉬울 것입니다.


딥 러닝 대상 탐지 프로젝트를 수행할 때 다음 표와 같은 요구 사항이 있는 경우가 있습니다.

알고리즘 FPS 요구 사항 ≥50
운영 체제 윈10
개발 언어 C++
모델 로딩 방식 하나의 GPU가 여러 모델을 로드합니다.
여러 GPU가 단일 모델을 개별적으로 로드합니다.

다음 블로그 게시물을 통해 이 두 가지 요구 사항을 개별적으로 완료할 수 있습니다.


  하나의 GPU가 여러 모델을 로드합니다.

여러 모델을 로드하기 위해 GPU를 구현하려면 서로 다른 엔진 파일이 순서대로 로드된다는 것을 이해할 수 있습니다. 즉, 두 세트의 Cuda 스트림 및 버퍼 변수를 정의하거나 스트림 변수와 버퍼 변수를 클래스에 캡슐화해야 합니다. , 그런 다음 두 개의 인터페이스 클래스를 만들고 해당 API를 호출하여 하나의 GPU에 여러 모델을 로드합니다. yolov5의 가중치 엔진을 호출하는 것을 예로 들어 보겠습니다.먼저 클래스를 캡슐화합니다.

  • .h 파일의 구현은 다음과 같습니다.
    class True_interface :
    {
    public:
    	True_interface();
    	virtual void load_model(int,std::string); // API:加载模型
        virtual void release_buffer(); // API:释放缓冲及指针变量
        virtual void interface(cv::Mat, float); // API:前向推理外部调用接口(输入图像和置信度阈值)
        void doInference(IExecutionContext& context, cudaStream_t& stream, void** buffers, float* input, float* output, int batchSize); // API:前向推理
        
        const int INPUT_H = Yolo::INPUT_H;
        const int INPUT_W = Yolo::INPUT_W;
        const int OUTPUT_SIZE = Yolo::MAX_OUTPUT_BBOX_COUNT * sizeof(Yolo::Detection) / sizeof(float) + 1;  
        float data[BATCH_SIZE * 3 * Yolo::INPUT_H * Yolo::INPUT_W];
        float prob[BATCH_SIZE * (Yolo::MAX_OUTPUT_BBOX_COUNT * sizeof(Yolo::Detection) / sizeof(float) + 1)];
        ICudaEngine* engine;
        IExecutionContext* context; // 创建context,创建一些空间来存储中间激活值
        void* buffers[2]; // 创建buffers指向 GPU 上输入和输出缓冲区
        cudaStream_t stream; // 创建cuda流
        int inputIndex;
        int outputIndex;
        IRuntime* runtime; // 创建runtime
        const char* INPUT_BLOB_NAME = "data";
        const char* OUTPUT_BLOB_NAME = "prob";
        Logger gLogger;
    };
  • .cpp 파일의 API 구현은 다음과 같습니다.실제로 정말 유용한 코드는 다음과 같습니다.
    • load_model():
      void True_interface::load_model(int gpu_id, std::string model_Name_)
      {
          if ((gpu_id == 0) || (gpu_id == 1)) // 选择要加载的GPU设备
          {
              cudaError_t cudaError = cudaSetDevice(gpu_id);
              if (cudaError == cudaSuccess)
                  std::cout << "GPU device selected successfully!" << std::endl;
              else
                  std::cout << "GPU device selection failed!" << std::endl;
          }
          else
          {
              std::cerr << "GPU device assignment error!" << std::endl;
              return;
          }
          std::string engine_name;
          if (model_Name_ == "model_one")
          {
              engine_name = std::string("model_one.engine");
          }
          else if(model_Name_ == "model_two")
          {
              engine_name = std::string("model_two.engine");  // 权重路径
          }
          else
          {
              std::cout << "Please enter the correct detector type!" << std::endl;
              return;
          }
          float gd = 0.0f, gw = 0.0f;
          std::ifstream file(engine_name, std::ios::binary);
          if (!file.good()) 
          {
              std::cerr << "read " << engine_name << " error!" << std::endl;
          }
          char* trtModelStream = nullptr;
          size_t size = 0;
          file.seekg(0, file.end);
          size = file.tellg();
          file.seekg(0, file.beg);
          trtModelStream = new char[size];
          file.read(trtModelStream, size);
          file.close();
      
          runtime = createInferRuntime(gLogger); // 创建runtime
          engine = runtime->deserializeCudaEngine(trtModelStream, size); // 反序列化引擎
          context = engine->createExecutionContext(); // 创建一个上下文,创建一些空间用来存储中间值
          delete[] trtModelStream;
          inputIndex = engine->getBindingIndex(INPUT_BLOB_NAME); // 使用输入和输出blob名称来获得相应的输入和输出索引
          outputIndex = engine->getBindingIndex(OUTPUT_BLOB_NAME); // 使用输入和输出blob名称来获得相应的输入和输出索引
          CUDA_CHECK(cudaMalloc(&buffers[inputIndex], BATCH_SIZE * 3 * INPUT_H * INPUT_W * sizeof(float))); // 为输入输出开辟GPU显存
          CUDA_CHECK(cudaMalloc(&buffers[outputIndex], BATCH_SIZE * OUTPUT_SIZE * sizeof(float))); // 为输入输出开辟GPU显存
          CUDA_CHECK(cudaStreamCreate(&stream));// 绑定Cuda流
          
      
          if (model_Name_ == "model_one")
          {
              std::cout << "model_one loaded successfully!" << std::endl;
          }
          else if (model_Name_ == "model_two")
          {
              std::cout << "model_two loaded successfully!" << std::endl;
          }
      }
    • doInference():
      void True_interface::doInference(IExecutionContext& context, cudaStream_t& stream, void** buffers, float* input, float* output, int batchSize)
      {
          // ---------------------------------从CPU到GPU,拷贝input数据-----------------------------------------------------
          CUDA_CHECK(cudaMemcpyAsync(buffers[0],  // //显存上的存储区域,用于存放输入数据
                                     input, batchSize * 3 * INPUT_H * INPUT_W * sizeof(float),  // //读入内存中的数据
                                     cudaMemcpyHostToDevice, stream));
          context.enqueue(batchSize, buffers, stream, nullptr); // 异步推理
          // ---------------------------------GPU到CPU,拷贝output数据-----------------------------------------------------
          CUDA_CHECK(cudaMemcpyAsync(output, buffers[1], batchSize * OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));
          cudaStreamSynchronize(stream); // 同步cuda流
      }
    •  상호 작용():
      void True_interface::interface_B(cv::Mat img, float conf_thresh)
      {
          // ---------------------------------------------------清空结果向量----------------------------------------------------
          result_B.clear();
          // ----------------------------------------------------图像预处理-----------------------------------------------------
          cv::Mat pr_img = preprocess_img(img, INPUT_W, INPUT_H); // resize 图像并转换通道
          int i = 0;
          for (int row = 0; row < INPUT_H; ++row)
          {
              uchar* uc_pixel = pr_img.data + row * pr_img.step;
              for (int col = 0; col < INPUT_W; ++col)
              {
                  data[i] = (float)uc_pixel[2] / 255.0;
                  data[i + INPUT_H * INPUT_W] = (float)uc_pixel[1] / 255.0;
                  data[i + 2 * INPUT_H * INPUT_W] = (float)uc_pixel[0] / 255.0;
                  uc_pixel += 3;
                  ++i;
              }
          }
          // ----------------------------------------------------前向推理-----------------------------------------------------
          doInference(*context, stream, buffers, data, prob, BATCH_SIZE);
          // ----------------------------------------------------非极大值抑制-------------------------------------------------
          std::vector<std::vector<Yolo::Detection>> batch_res(1);
          auto& res = batch_res[0];
          nms(res, &prob[0], conf_thresh, NMS_THRESH);
          // ----------------------------------------------------输出目标坐标及置信度-------------------------------------------------
          for (size_t j = 0; j < res.size(); j++)
          {
              cv::Rect r = get_rect(img, res[j].bbox);
              std::vector<int> temp = { r.tl().x, r.tl().y, r.br().x, r.br().y, (int)res[j].class_id ,int(100*res[j].conf) };
              result_B.push_back(temp);
          }
      }
    •  release_buffer():
      void True_interface::release_buffer()
      {
          // 释放资源
          cudaStreamDestroy(stream);
          CUDA_CHECK(cudaFree(buffers[inputIndex]));
          CUDA_CHECK(cudaFree(buffers[outputIndex]));
          // 销毁变量
          context->destroy();
          engine->destroy();
          runtime->destroy();
          std::cout << "Memory freed successfully!" << std::endl;
      }
  •  그런 다음 두 개의 클래스를 초기화하고 load_model() 메서드를 각각 호출하여 하나의 GPU에 두 개의 모델을 로드하기만 하면 됩니다.

다른 GPU에 모델을 로드하려면 load_model() 메서드 를 호출 할 때 다른 GPU의 ID만 첫 번째 매개변수로 전달하면 됩니다 . 편리하고 간단합니까?

추천

출처blog.csdn.net/qq_42308217/article/details/123462056