この記事を読む前に、Win10でtenorrtを使用してyolov5をデプロイする方法についての私の教育記事を読んでください。そうすれば、戻ってこの記事を読みやすくなります。
ディープラーニングターゲット検出プロジェクトを実行する場合、次の表に示すような要件が存在する場合があります。
アルゴリズムFPS要件 | ≥50 |
オペレーティング・システム | Win10 |
開発言語 | C ++ |
モデルの読み込み方法 | 1つのGPUが複数のモデルをロードします |
複数のGPUが単一のモデルを個別にロードします |
次のブログ投稿では、これら2つの要件を別々に完了することができます。
1つのGPUが複数のモデルをロードします
GPUを実装して複数のモデルをロードするには、異なるエンジンファイルが順番にロードされること、つまり、2セットのCudaストリームとバッファー変数を定義する必要があること、またはストリーム変数とバッファー変数がクラスにカプセル化されることを理解できます。 、次に2つのインターフェイスクラスを作成し、対応するAPIを呼び出して、1つの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():
- 次に、2つのクラスを初期化し、それぞれload_model()メソッドを呼び出して、1つのGPUに2つのモデルをロードするだけです。
異なるGPUにモデルをロードする場合は、load_model()メソッドを呼び出すときに、最初のパラメーターとして異なるGPUのIDを渡すだけで済みます。便利でシンプルですか。