模型部署技巧(一)

模型部署技巧(一)

以下內容是參考CUDA與TensorRT模型部署內容第六章,主要針對圖像的前/后處理中的trick。

參考:
1.部署分類器-int8-calibration
2. cudnn安裝地址
3. 如何查找Tensor版本,與cuda 和 cudnn匹配
4. timing cache


一. 前處理 preprocess

學習目標

  • 分析學習幾種cv::Mat bgr2rgb 的方式,比較運行速度

1. 圖像 BGR2RGB 在cpu中的方式

某些小圖像前處理部分如果放在GPU上跑,并不能充分的硬件資源吃滿,導致硬件資源比較浪費。
如果這種情況出現的話,我們可能會考慮把前處理放在CPU上,DNN的forward部分放在GPU上,進行異步的推理。

1.1 cv::cvtColor

void preprocess_cv_cvtcolor(cv::Mat src, cv::Mat tar){cv::cvtColor(src, tar, cv::COLOR_BGR2RGB);
}

1.2 .at 方式

void preprocess_cv_mat_at(cv::Mat src, cv::Mat tar){for (int i = 0; i < src.rows; i++) {for (int j = 0; j < src.cols; j++) {tar.at<cv::Vec3b>(i, j)[2] = src.at<cv::Vec3b>(i, j)[0];tar.at<cv::Vec3b>(i, j)[1] = src.at<cv::Vec3b>(i, j)[1];tar.at<cv::Vec3b>(i, j)[0] = src.at<cv::Vec3b>(i, j)[2];}}
}

1.3 cv::MatIterator_方式

void preprocess_cv_mat_iterator(cv::Mat src, cv::Mat tar){cv::MatIterator_<cv::Vec3b> src_it = src.begin<cv::Vec3b>();cv::MatIterator_<cv::Vec3b> tar_it = tar.begin<cv::Vec3b>();cv::MatIterator_<cv::Vec3b> end    = src.end<cv::Vec3b>();for (; src_it != end; src_it++, tar_it++) {(*tar_it)[2] = (*src_it)[0];(*tar_it)[1] = (*src_it)[1];(*tar_it)[0] = (*src_it)[2];}
}

1.4 .data方法

void preprocess_cv_mat_data(cv::Mat src, cv::Mat tar){int height   = src.rows;int width    = src.cols;int channels = src.channels();for (int i = 0; i < height; i ++) {for (int j = 0; j < width; j ++) {int index = i * width * channels + j * channels;tar.data[index + 2] = src.data[index + 0];tar.data[index + 1] = src.data[index + 1];tar.data[index + 0] = src.data[index + 2];}}
}

1.5 pointer

void preprocess_cv_pointer(cv::Mat src, cv::Mat tar){for (int i = 0; i < src.rows; i ++) {cv::Vec3b* src_ptr = src.ptr<cv::Vec3b>(i);cv::Vec3b* tar_ptr = tar.ptr<cv::Vec3b>(i);for (int j = 0; j < src.cols; j ++) {tar_ptr[j][2] = src_ptr[j][0];tar_ptr[j][1] = src_ptr[j][1];tar_ptr[j][0] = src_ptr[j][2];}}
}

結論:

  • 使用cv::Mat::at:速度最慢
  • 使用cv::MatIterator_ 速度中等
  • 使用cv::Mat.data
  • 使用cv::Mat.ptr: 速度最快

Tips.圖像 BGR2RGB + norm + hwc2chw 最優方式

void preprocess_cv_pointer(cv::Mat src, float* tar, float* mean, float* std){int area = src.rows * src.cols;int offset_ch0 = area * 0;int offset_ch1 = area * 1;int offset_ch2 = area * 2;for (int i = 0; i < src.rows; i ++) {cv::Vec3b* src_ptr = src.ptr<cv::Vec3b>(i);for (int j = 0; j < src.cols; j ++) {tar[offset_ch2++] = (src_ptr[j][0] / 255.0f - mean[0]) / std[0];tar[offset_ch1++] = (src_ptr[j][1] / 255.0f - mean[1]) / std[1];tar[offset_ch0++] = (src_ptr[j][2] / 255.0f - mean[2]) / std[2];}}
}

二. 通用模型推理框架設計

2.1 worker類
根據模型的種類(分類、檢測、分割)在構造函數中初始化一個模型,另外包含一個推理函數即可。

Worker::Worker(string onnxPath, logger::Level level, model::Params params) {m_logger = logger::create_logger(level);// 這里根據task_type選擇創建的trt_model的子類,今后會針對detection, segmentation擴充if (params.task == model::task_type::CLASSIFICATION) m_classifier = model::classifier::make_classifier(onnxPath, level, params);}void Worker::inference(string imagePath) {if (m_classifier != nullptr) {m_classifier->load_image(imagePath);m_classifier->inference();}
}

2.2 model基類

成員變量包含模型參數集合,各類路徑字符串,logger, timer等。

Model::Model(string onnx_path, logger::Level level, Params params) {m_onnxPath      = onnx_path;m_enginePath    = getEnginePath(onnx_path);m_workspaceSize = WORKSPACESIZE;m_logger        = make_shared<logger::Logger>(level);m_timer         = make_shared<timer::Timer>();m_params        = new Params(params);
}

成員函數有初始化模型,加載數據,推理,構建/加載/保存引擎,幾個純虛函數setup, 前/后處理cpu版本,前/后處理gpu版本)。
純虛函數需要子類去具體實現。

setup負責分配host/device的memory, bindings, 以及創建推理所需要的上下文。由于不同task的input/output的tensor不一樣,所以這里的setup需要在子類實現。

2.3 classifier 分類器子類
主要是針對model基類中的幾個純虛函數,進行具體實現。
Eg.

void Classifier::setup(void const* data, size_t size) {m_runtime     = shared_ptr<IRuntime>(createInferRuntime(*m_logger), destroy_trt_ptr<IRuntime>);m_engine      = shared_ptr<ICudaEngine>(m_runtime->deserializeCudaEngine(data, size), destroy_trt_ptr<ICudaEngine>);m_context     = shared_ptr<IExecutionContext>(m_engine->createExecutionContext(), destroy_trt_ptr<IExecutionContext>);m_inputDims   = m_context->getBindingDimensions(0);m_outputDims  = m_context->getBindingDimensions(1);// 考慮到大多數classification model都是1 input, 1 output, 這邊這么寫。如果像BEVFusion這種有多輸出的需要修改CUDA_CHECK(cudaStreamCreate(&m_stream));m_inputSize     = m_params->img.h * m_params->img.w * m_params->img.c * sizeof(float);m_outputSize    = m_params->num_cls * sizeof(float);m_imgArea       = m_params->img.h * m_params->img.w;// 這里對host和device上的memory一起分配空間CUDA_CHECK(cudaMallocHost(&m_inputMemory[0], m_inputSize));CUDA_CHECK(cudaMallocHost(&m_outputMemory[0], m_outputSize));CUDA_CHECK(cudaMalloc(&m_inputMemory[1], m_inputSize));CUDA_CHECK(cudaMalloc(&m_outputMemory[1], m_outputSize));// //創建m_bindings,之后再尋址就直接從這里找m_bindings[0] = m_inputMemory[1];m_bindings[1] = m_outputMemory[1];
}

2.4 logger類
日志類,通過設置等級進行打印消息,相比于cout更清爽。

2.5 timer類
記錄cpu和gpu的開始/結束時間,計算相應的時間差。

    m_timer->start_cpu();/* 處理程序 */m_timer->stop_cpu();m_timer->duration_cpu<timer::Timer::ms>("preprocess(CPU)");

2.6 process命名空間
process 命名空間下,定義了preprocess_resize_cpu, preprocess_resize_gpu等一些函數。


三. int8量化

3.1 創建calibrator類的時候需要繼承nvinfer1里的calibrator,NVIDIA官方提供了以下五種:

  • nvinfer1::IInt8EntropyCalibrator2 是tensorRT 7.0引入的接口,實現基于熵的INT8量化校準器。(默認情況下優先使用它)
  • nvinfer1::IInt8MinMaxCalibrator
  • nvinfer1::IInt8EntropyCalibrator 是tensorRT 7.0之前的接口,實現基于熵的INT8量化校準器。(目前已被棄用)
  • nvinfer1::IInt8LegacyCalibrator(percentile)
  • nvinfer1::IInt8Calibrator(被棄用)

3.2 在calibrator類中需要實現的函數只需要四個:

int         getBatchSize() const noexcept override {return m_batchSize;};
bool        getBatch(void* bindings[], const char* names[], int nbBindings) noexcept override;
const void* readCalibrationCache(std::size_t &length) noexcept override;
void        writeCalibrationCache (const void* ptr, std::size_t legth) noexcept override;
  • getBatchSize: 獲取calibration的batch大小,需要注意的是不同的batch size會有不同的校準效果。一般而言,越大越好。
  • getBatch獲取的圖像必須要和真正推理時所采用的預處理保持一直。不然dynamic range會不準
  • readCalibrationCache: 用來讀取calibration table,也就是之前做calibration統計得到的各個layer輸出tensor的dynamic range。實現這個函數可以讓我們避免每次做int8推理的時候都需要做一次calibration
  • writeCalibrationCache: 將統計得到的dynamic range寫入到calibration table中去

3.3 實現完了基本的calibrator之后,在build引擎的時候通過config指定calibrator就可以了。

shared_ptr<Int8EntropyCalibrator> calibrator(new Int8EntropyCalibrator(64, "calibration/calibration_list_imagenet.txt", "calibration/calibration_table.txt",3 * 224 * 224, 224, 224));
config->setInt8Calibrator(calibrator.get());

這里面的calibration_list_imagenet.txt使用的是ImageNet2012的test數據集的一部分。可以根據各自的情況去更改,注意batch_size 64需要改成能被calibration dataset的整除的數,否則core dump。

需要注意的是,如果calibrator改變了,或者模型架構改變了,需要刪除掉calibration_table.txt來重新計算dynamic range。否則會報錯

Tips.
實操生成過程中遇到的core dump情況,報出一個cudnn庫加載版本不正確的警告。通過ldd ./bin/trt-infer 定位到libnvinfer.so.8 => /home/xx/opt/TensorRT-8.5.3.1/lib/libnvinfer.so.8 ,TensorRT版本與Makefile配置文件中指定的版本不一致
查看服務器動態庫路徑,先刪除動態庫其中被指定的TensorRT動態庫路徑,再指定自己的動態庫路徑

echo $LD_LIBRARY_PATH
export LD_LIBRARY_PATH=""  # 先清空
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/cuda/lib64:/home/xxxpersonal/others/TensorRT-8.4.0.6/lib

四. Timing Cache

主要作用:它可以加快 engine 的創建過程,為了優化和加速內核選擇過程。

如何使用 Timing Cache:

  • 創建和保存 Timing Cache: 在第一次構建 engine 時,TensorRT 會創建一個 timing cache。你可以將這個 timing cache 保存到文件中,以便未來復用。
  • 加載 Timing Cache: 在構建新的 engine 時,可以加載已經保存的 timing cache,從而避免重新進行時間消耗的內核調優過程。

五. trt-engine-explorer

trt-engine-explorer是NVIDIA官方提供的分析TensorRT優化后的推理引擎架構的工具包。鏈接在這里:

TensorRT tool: trt-engine-explorer

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/903549.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/903549.shtml
英文地址,請注明出處:http://en.pswp.cn/news/903549.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL--數據引擎詳解

存儲引擎 MySQL體系結構 連接層&#xff1a; 主要接收客戶端的連接&#xff0c;然后完成一些鏈接的處理&#xff0c;以及認證授權的相關操作和安全方案&#xff0c;還要去檢查是否超過最大連接數等等&#xff0c;比如在連接MySQL服務器時需要輸入用戶名&#xff0c;密碼&#…

【含文檔+PPT+源碼】基于微信小程序的健康飲食食譜推薦平臺的設計與實現

項目介紹 本課程演示的是一款基于微信小程序的健康飲食食譜推薦平臺的設計與實現&#xff0c;主要針對計算機相關專業的正在做畢設的學生與需要項目實戰練習的 Java 學習者。 1.包含&#xff1a;項目源碼、項目文檔、數據庫腳本、軟件工具等所有資料 2.帶你從零開始部署運行本…

當OA闖入元宇宙:打卡、報銷和會議的未來狂想

引言&#xff1a;虛實共生中的組織基因突變 元宇宙正以虛實共生的形態重構人類協作的底層邏輯。傳統OA系統建立的物理規則——指紋打卡驗證在場性、紙質票據堆砌信任鏈、會議室排期協調時空資源——在元宇宙的數字原野上迎來基因級重組。這場變革不僅是技術工具的迭代&#xf…

解決vscode cmake提示檢測到 #include 錯誤

一、問題 cmake已經包含了動態庫文件&#xff0c;依然提示“檢測到 #include 錯誤。請更新 includePath。” 二、解決方案 Ctrl Shift P進入CPP編輯配置&#xff0c;然后在JSON中加入下面一行&#xff1a; "configurationProvider": "ms-vscode.cmake-tools&…

2024ICPC成都題解

文章目錄 L. Recover Statistics(簽到)J. Grand Prix of Ballance(模擬簽到)A. Arrow a Row(構造)B. Athlete Welcome Ceremony(線性dp)G. Expanding Array(打表結論)I. Good Partitions(線段樹)E. Disrupting Communications(換根dpLCA倍增)K. Magical Set(費用流) 題目鏈接 …

達夢數據庫官方遷移工具SQLark:支持Oracle/MySQL/PostgreSQL遷移至達夢數據庫!

SQLark 百靈連接是一款面向信創應用開發者的數據庫開發和管理工具&#xff0c;由達夢數據歷時三年自主研發&#xff0c;注冊即可免費使用客戶端&#xff08;官網鏈接www.sqlark.com&#xff09;。今天&#xff0c;我們將重點介紹SQLark的特色功能之一——數據遷移&#xff0c;該…

映射關系4

好&#xff01;我明白了&#xff01;&#x1f4a5; 你希望我在你的基礎上&#xff0c;繼續優化 insertPathWithIds&#xff0c;讓它&#xff1a; ? 支持每一級節點的 idPart 是字符串&#xff08;而不是int&#xff09;。 ? 結構更清晰&#xff0c;更快拼接。 ? 完全符合C98…

PDF Shaper v15.0

如今對PDF處理的軟件很多都是只是單一的功能。PDF Shaper給你完全不同的體驗&#xff0c;因為PDF Shaper是一款免費的PDF工具集合的軟件。有了PDF Shaper&#xff0c;你以后再也不用下載其他處理PDF的軟件了。PDF Shaper的功能有&#xff1a;合并&#xff0c;分割&#xff0c;加…

【Python爬蟲基礎篇】--4.Selenium入門詳細教程

先解釋&#xff1a;Selenium&#xff1a;n.硒&#xff1b;硒元素 目錄 1.Selenium--簡介 2.Selenium--原理 3.Selenium--環境搭建 4.Selenium--簡單案例 5.Selenium--定位方式 6.Selenium--常用方法 6.1.控制操作 6.2.鼠標操作 6.3.鍵盤操作 6.4.獲取斷言信息 6.5.…

mysql8.0版本部署+日志清理+rsync備份策略

mysql安裝&#xff1a;https://blog.csdn.net/qq_39399966/article/details/120205461 系統&#xff1a;centos7.9 數據庫版本&#xff1a;mysql8.0.28 1.卸載舊的mysql,保證環境純凈 rpm -qa | grep mariadb mariadb-5.... rpm -e --nodeps 軟件 rpm -e --nodeps mariadb-5.…

C#進階學習(十七)PriorityQueue<TElement, TPriority>優先級隊列的介紹

1. PriorityQueue是什么&#xff1f;作用是什么&#xff1f; 定義&#xff1a;PriorityQueue<TElement, TPriority> 是 C# (.NET 6 引入) 中的泛型優先級隊列數據結構。 那么是什么是優先級隊列呢&#xff1f;優先級隊列是一種抽象數據結構&#xff0c;其核心特性是元素按…

如何查看和驗證AWS CloudFront的托管區域ID

在使用AWS Route 53設置DNS記錄時,正確識別CloudFront分發的托管區域ID是至關重要的。本文將詳細介紹幾種查看和驗證CloudFront托管區域ID的方法,特別關注中國區CloudFront的特殊情況。 為什么托管區域ID很重要? 托管區域ID是AWS服務中的一個關鍵標識符。在創建指向CloudF…

kafka整合flume與DStream轉換

一、Kafka整合flume cd /opt/software/flume/conf/ vi flume-kafka.conf a1.sourcesr1 a1.sinksk1 a1.channelsc1 a1.sources.r1.typespooldirt a1.sources.r1.spoolDir/root/flume-kafka a1.sinks.k1.typeorg.apache.flume.sink.kafka.KafkaSink a1.sinks.k1.kafka.to…

網絡通訊【QTcpServer、QTcpSocket、QAbstractSocket】

目錄 QTcpServer class簡單描述成員函數和信號 QTcpSocket Class詳細描述成員函數和信號 QAbstractSocket Class詳細描述成員函數和信號成員函數說明文檔 QT實現服務器和客戶端通訊服務器端&#xff1a;通訊流程原代碼 客戶端通信流程原代碼 QTcpServer class header: #includ…

大模型在腎癌診療全流程中的應用研究報告

目錄 一、引言 1.1 研究背景與意義 1.2 研究目的與方法 1.3 國內外研究現狀 二、大模型預測腎癌術前情況 2.1 基于影像組學的腎癌良惡性及分級預測 2.1.1 MRI 影像組學模型預測腎透明細胞癌分級 2.1.2 CT 影像深度學習模型鑒別腎腫物良惡性及侵襲性 2.2 大模型對手術風…

網絡原理 - 11(HTTP/HTTPS - 2 - 請求)

目錄 HTTP 請求&#xff08;Request&#xff09; 認識 URL URL 基本格式 關于 URL encode 認識方法&#xff08;method&#xff09; 1. GET 方法 2. POST 方法 認識請求“報頭”&#xff08;header&#xff09; Host Content-Length Content-Type User-Agent&…

實現MySQL高可用性:從原理到實踐

目錄 一、概述 1.什么是MySQL高可用 2.方案組成 3.優勢 二、資源清單 三、案例實施 1.修改主機名 2.安裝MySQL數據庫&#xff08;Master1、Master2&#xff09; 3.配置mysql雙主復制 4.安裝haproxy&#xff08;keepalived1、keepalived2&#xff09; 5.安裝keepaliv…

CSS學習筆記8——表格

一、表格 1-1、創建表格 在Word文檔中&#xff0c;如果要創建表格&#xff0c;只需插入表格&#xff0c;然后設定相應的行數和列數即可。然而在HTML網頁中&#xff0c;所有的元素都是通過標簽定義的&#xff0c;要想創建表格&#xff0c;就需要使用與表格相關的標簽。使用標簽…

爬蟲學習筆記(一)

目的 通過編寫程序爬取互聯網上的優質資源 爬蟲必須要使用python嗎 非也~ 編程語言知識工具&#xff0c;抓取到數據才是目的&#xff0c;而大多數爬蟲采用python語言編寫的原因是python的語法比較簡單&#xff0c;python寫爬蟲比較簡單&#xff01;好用&#xff01;而且pyt…

大廠面試:MySQL篇

前言 本章內容來自B站黑馬程序員java大廠面試題和小林coding 博主學習筆記&#xff0c;如果有不對的地方&#xff0c;海涵。 如果這篇文章對你有幫助&#xff0c;可以點點關注&#xff0c;點點贊&#xff0c;謝謝你&#xff01; 1.MySQL優化 1.1 定位慢查詢 定位 一個SQL…