深度學習算法優化系列二十二 | 利用TensorRT部署YOLOV3-Tiny INT8量化模型

2021-03-02 GiantPandaCV
1. 前言

上一節深度學習算法優化系列二十一 | 在VS2015上利用TensorRT部署YOLOV3-Tiny模型 分享了使用TensorRT在GPU上部署FP32的YOLOV3-Tiny模型,這一節繼續分享一下如何部署INT8的YOLOV3-Tiny模型。

2. 確定走哪條路?

和上一節一樣,這裡仍然是走ONNX->TRT這條路,也就是說我這裡的INT8量化是在TensorRT中使用nvonnxparser解析了YOLOV3-Tiny 的ONNX模型之後完成的,似乎這也是比較主流的方法。官方例子中提供了一個MNIST數據集的INT8量化,過程也是先用nvcaffeparser解析Caffe模型然後直接做量化並將原始模型序列化為TRT文件以供後面的圖像推理。

所以,我這裡走的路就是直接解析ONNX模型->INT8量化->序列化為TRT文件->完成推理。

3. 準備校準集

如果你懂TensorRT的量化原理,就沒必要看這一節了,如果不懂也沒關係後面我會單獨寫一篇文章來嘗試解釋一下。首先宏觀的說一下,TensorRT對一個模型進行全INT8量化包含權重和激活值兩大部分,對於權重採用的是直接非飽和量化,也就是說直接統計權重的最大值和最小值就可以完成量化。而對於激活值的量化,則需要以下步驟:

來自公眾號的Ldpe2G作者,感謝

可以看到在量化激活值的時候需要利用校準集進行FP32的推理並收集每一層的激活值並統計直方圖。因此,在INT8量化之前我們首先需要準備一下校準集。這裡怎麼準備呢?

很簡單,你訓練YOLOV3-Tiny的驗證集抽出一部分就可以了(我這裡使用了100張,NVIDIA的PPT裡面說需要使用1000張,最好和PPT裡面指定的圖片數量一致,PPT見附錄),然後將圖片的路徑放到一個*.txt文件裡面就可以了,如下圖所示:

驗證集4. TensorRT INT8量化核心步驟

接著上一次推文的介紹,你已經可以獲得YOLOV3-Tiny的FP32的ONNX文件。然後我們只需要寫一個新的類int8EntroyCalibrator繼承Int8EntropyCalibrator這個類,然後重寫一些和數據讀取相關的成員函數即可。這樣就可以隨心所欲的去修改校驗數據的讀取格式,不用像官方例子那樣還必須轉成Caffe模型並將數據集製作為指定格式。重載後的代碼如下:

namespace nvinfer1 {
 class int8EntroyCalibrator : public nvinfer1::IInt8EntropyCalibrator {
 public:
  int8EntroyCalibrator(const int &bacthSize,
   const std::string &imgPath,
   const std::string &calibTablePath);

  virtual ~int8EntroyCalibrator();

  int getBatchSize() const override { return batchSize; }

  bool getBatch(void *bindings[], const char *names[], int nbBindings) override;

  const void *readCalibrationCache(std::size_t &length) override;

  void writeCalibrationCache(const void *ptr, std::size_t length) override;

 private:

  bool forwardFace;

  int batchSize;
  size_t inputCount;
  size_t imageIndex;

  std::string calibTablePath;
  std::vector<std::string> imgPaths;

  float *batchData{ nullptr };
  void  *deviceInput{ nullptr };



  bool readCache;
  std::vector<char> calibrationCache;
 };

 int8EntroyCalibrator::int8EntroyCalibrator(const int &bacthSize, const std::string &imgPath,
  const std::string &calibTablePath) :batchSize(bacthSize), calibTablePath(calibTablePath), imageIndex(0), forwardFace(
   false) {
  int inputChannel = 3;
  int inputH = 416;
  int inputW = 416;
  inputCount = bacthSize*inputChannel*inputH*inputW;
  std::fstream f(imgPath);
  if (f.is_open()) {
   std::string temp;
   while (std::getline(f, temp)) imgPaths.push_back(temp);
  }
  int len = imgPaths.size();
  for (int i = 0; i < len; i++) {
   cout << imgPaths[i] << endl;
  }
  batchData = new float[inputCount];
  CHECK(cudaMalloc(&deviceInput, inputCount * sizeof(float)));
 }

 int8EntroyCalibrator::~int8EntroyCalibrator() {
  CHECK(cudaFree(deviceInput));
  if (batchData)
   delete[] batchData;
 }

 bool int8EntroyCalibrator::getBatch(void **bindings, const char **names, int nbBindings) {
  cout << imageIndex << " " << batchSize << endl;
  cout << imgPaths.size() << endl;
  if (imageIndex + batchSize > int(imgPaths.size()))
   return false;
  // load batch
  float* ptr = batchData;
  for (size_t j = imageIndex; j < imageIndex + batchSize; ++j)
  {
   //cout << imgPaths[j] << endl;
   Mat img = cv::imread(imgPaths[j]);
   vector<float>inputData = prepareImage(img);
   cout << inputData.size() << endl;
   cout << inputCount << endl;
   if ((int)(inputData.size()) != inputCount)
   {
    std::cout << "InputSize error. check include/ctdetConfig.h" << std::endl;
    return false;
   }
   assert(inputData.size() == inputCount);
   int len = (int)(inputData.size());
   memcpy(ptr, inputData.data(), len * sizeof(float));

   ptr += inputData.size();
   std::cout << "load image " << imgPaths[j] << "  " << (j + 1)*100. / imgPaths.size() << "%" << std::endl;
  }
  imageIndex += batchSize;
  CHECK(cudaMemcpy(deviceInput, batchData, inputCount * sizeof(float), cudaMemcpyHostToDevice));
  bindings[0] = deviceInput;
  return true;
 }
 const void* int8EntroyCalibrator::readCalibrationCache(std::size_t &length)
 {
  calibrationCache.clear();
  std::ifstream input(calibTablePath, std::ios::binary);
  input >> std::noskipws;
  if (readCache && input.good())
   std::copy(std::istream_iterator<char>(input), std::istream_iterator<char>(),
    std::back_inserter(calibrationCache));

  length = calibrationCache.size();
  return length ? &calibrationCache[0] : nullptr;
 }

 void int8EntroyCalibrator::writeCalibrationCache(const void *cache, std::size_t length)
 {
  std::ofstream output(calibTablePath, std::ios::binary);
  output.write(reinterpret_cast<const char*>(cache), length);
 }
}

有了這個類,所有的問題都解決了,接下來只需要在解析ONNX模型之後利用這個類進行INT8量化就可以了。

帶注釋的代碼解析如下:

// ONNX模型轉為TensorRT引擎
bool onnxToTRTModel(const std::string& modelFile, // onnx文件的名字
 const std::string& filename,  // TensorRT引擎的名字 
 IHostMemory*& trtModelStream) // output buffer for the TensorRT model
{
 // 創建builder
 IBuilder* builder = createInferBuilder(gLogger.getTRTLogger());
 assert(builder != nullptr);
 nvinfer1::INetworkDefinition* network = builder->createNetwork();

 if (!builder->platformHasFastInt8()) return false;

 // 解析ONNX模型
 auto parser = nvonnxparser::createParser(*network, gLogger.getTRTLogger());


 //可選的 - 取消下面的注釋可以查看網絡中每層的詳細信息
 //config->setPrintLayerInfo(true);
 //parser->reportParsingInfo();

 //判斷是否成功解析ONNX模型
 if (!parser->parseFromFile(modelFile.c_str(), static_cast<int>(gLogger.getReportableSeverity())))
 {
  gLogError << "Failure while parsing ONNX file" << std::endl;
  return false;
 }

 
 // 建立推理引擎
 builder->setMaxBatchSize(BATCH_SIZE);
 builder->setMaxWorkspaceSize(1 << 30);

 nvinfer1::int8EntroyCalibrator *calibrator = nullptr;
 if (calibFile.size()>0) calibrator = new nvinfer1::int8EntroyCalibrator(BATCH_SIZE, calibFile, "F:/TensorRT-6.0.1.5/data/v3tiny/calib.table");


 //builder->setFp16Mode(true);
 std::cout << "setInt8Mode" << std::endl;
 if (!builder->platformHasFastInt8())
  std::cout << "Notice: the platform do not has fast for int8" << std::endl;
 builder->setInt8Mode(true);
 builder->setInt8Calibrator(calibrator);
 /*if (gArgs.runInInt8)
 {
  samplesCommon::setAllTensorScales(network, 127.0f, 127.0f);
 }*/
 //samplesCommon::setAllTensorScales(network, 1.0f, 1.0f);
 cout << "start building engine" << endl;
 ICudaEngine* engine = builder->buildCudaEngine(*network);
 cout << "build engine done" << endl;
 assert(engine);
 if (calibrator) {
  delete calibrator;
  calibrator = nullptr;
 }
 // 銷毀模型解釋器
 parser->destroy();

 // 序列化引擎
 trtModelStream = engine->serialize();

 // 保存引擎
 nvinfer1::IHostMemory* data = engine->serialize();
 std::ofstream file;
 file.open(filename, std::ios::binary | std::ios::out);
 cout << "writing engine file..." << endl;
 file.write((const char*)data->data(), data->size());
 cout << "save engine file done" << endl;
 file.close();

 // 銷毀所有相關的東西
 engine->destroy();
 network->destroy();
 builder->destroy();

 return true

剩下的內容就是一些預處理和NMS後處理,這裡就不再贅述了,執行完程序後就會在指定路徑下生成INT8量化的Table文件以及INT8量化後的TRT序列化文件,後面就可以直接加載這個文件進行推理了。所有完整細節請看我的提供的完整源碼。

5. 1050Ti的速度測試YOLOV3-Tiny TRT模型Inference TimeFP3217msINT84ms

「1050Ti」上運行了20個Loop測試了速度,發現前向推理的速度有4倍提升,同時TRT序列化文件的大小也減少了4倍左右。

6. 源碼獲取

「GiantPandaCV」公眾號後臺回復 INT8 獲取完整CPP文件。「注意TensorRT版本為6.0。」

公眾號二維碼:

公眾號7. 附錄TensorRT INT8量化官方PPT:http://on-demand.gputechconf.com/gtc/2017/presentation/s7310-8-bit-inference-with-tensorrt.pdfhttps://github.com/NVIDIA/TensorRT/tree/release/6.0/samples/opensource/sampleINT8

歡迎關注GiantPandaCV, 在這裡你將看到獨家的深度學習分享,堅持原創,每天分享我們學習到的新鮮知識。( • ̀ω•́ )✧

有對文章相關的問題,或者想要加入交流群,歡迎添加BBuf微信:

二維碼

相關焦點

  • 工程之道,深度學習的工業級模型量化實戰
    量化的目的是為了追求極致的推理計算速度,為此捨棄了數值表示的精度,直覺上會帶來較大的模型掉點,但是在使用一系列精細的量化處理之後,其在推理時的掉點可以變得微乎其微,並能支持正常的部署應用。CUDA 平臺CUDA 平臺是指 NVIDIA 旗下 GPU 平臺,由於提供 CUDNN 和 Toolkit 系列接口以及 TensorRT 專用推理庫,大部分算子可以使用官方優化,而 MegEngine 則在此基礎上進行了更多細節的優化,比如如何更好地利用 GPU 的TensorCore 進行加速,不同型號之間一些差異的處理等
  • 深度學習算法優化系列十八 | TensorRT Mnist數字識別使用示例
    注意這裡使用的LeNet模型是Caffe的原始模型,因為TensorRT是直接支持Caffe的原始模型解析的,但例如Pytorch模型之類的還要進行轉換,這在以後的文章中會涉及到。//!//! 簡介: 使用命令行參數初始化params結構的成員//!
  • 深度學習模型推理優化加速技術
    Winograd、模型剪裁與稀疏化FFT/Winograd卷積算法(Op-level)模型剪枝(許多實驗證明:神經網絡都是過參數化的):稀疏連接、張量分解、channel剪枝;結構化剪枝(通常能提升運算速度,非結構剪枝過於隨機的話,效果不明顯)核的稀疏化模型參數共享低秩分解:使用矩陣對參數進行分解估計(屬於模型壓縮);SVD、tucker
  • 視覺算法工業部署及優化學習路線分享
    仗著這個經驗,我又把組裡其他模型:OCR,簡化版超清視頻到TX2,NX,AGX,智能相機等設備上,用盡了各種能優化的框架ncnn,mnn (其中包括針對工業級的二階段的OCR模型,det用tensorRT,reg用mnn的vulkan後端聯合開多線程優化在TX2上達到real-time)也熟悉了很多不同的backend和指令集架構。
  • TensorFlow 模型優化工具包:模型大小減半,精度幾乎不變!
    它將模型常量(如權重和偏差值)從全精度浮點(32 位)量化為減少精度的浮點數據類型(IEEE FP16)。訓練後的 float16 quantization 是量化 TensorFlow Lite 模型很好的方法,因為它對精度的影響極小並且能夠使得模型大小顯著減小。
  • 基於TensorFlow的深度學習實戰
    毫不誇張得說,TensorFlow的流行讓深度學習門檻變得越來越低,只要你有Python和機器學習基礎,入門和使用神經網絡模型變得非常簡單。TensorFlow簡介如前所述,TensorFlow是一個深度學習庫,使用這一框架,可以用來構建和測試深度神經網絡。深度學習讓我們能夠以極高的準確性構建複雜的應用程式。
  • 如何優化深度學習模型
    深度學習的一個非常重要的步驟是找到正確的超參數,超參數是模型無法學習的。但是對於我們絕大多數隻想在黑色星期五銷售之後用經濟型機器分類貓狗的人來說,現在是時候該弄清楚如何使這些深度學習模型真正起作用了。網格搜索這是獲得良好超參數的最簡單方法。它實際上就是暴力解決。算法:從一組給定的超參數中嘗試一堆超參數,看看哪種方法效果最好。
  • 【項目實踐】Pytorch YOLO項目推薦 建議收藏學習
    ,考慮到現在Pytorch是做實驗發論文最流行的深度學習框架,所以我就針對Pytorch實現的YOLO項目做了一個盤點和匯總,真心希望可以幫助到入門目標檢測的同學。,結果也非常驚豔,推薦大家學習。yolo
  • 伯克利《深度強化學習》更新 | 第十三講:利用模仿優化控制器學習...
    該課程主題選擇深度增強學習,即緊跟當前人工智慧研究的熱點,又可作為深度學習的後續方向,值得推薦。想要學習伯克利 CS 294-112 《深度強化學習》這門課程,學生需要先學習 CS189 或者其他同等學力課程。本課程將假定學生掌握強化學習、數值優化和機器學習的相關背景知識。
  • Python安裝TensorFlow 2、tf.keras和深度學習模型的定義
    TensorFlow教程概述本教程旨在為您的深度學習項目提供tf.keras的完整介紹。重點是將API用於常見的深度學習模型開發任務;我們不會深入研究深度學習的數學和理論。學習python深度學習的最好方法是邊做邊做。
  • 入門 | 深度學習模型的簡單優化技巧
    以下是我與同事和學生就如何優化深度模型進行的對話、消息和辯論的摘要。如果你發現了有影響力的技巧,請分享。首先,為什麼要改進模型?像卷積神經網絡(CNN)這樣的深度學習模型具有大量的參數;實際上,我們可以調用這些超參數,因為它們原本在模型中並沒有被優化。
  • 教程| 如何用TensorFlow在安卓設備上實現深度學習推斷
    她在 Insight 工作的時候,在安卓系統上用 TensorFlow 部署了一個 WaveNet 模型。本文詳細介紹了部署和實現過程。對於個人和公司來說,存在許多狀況是更希望在本地設備上做深度學習推斷的:想像一下當你在旅行途中沒有可靠的網際網路連結時,或是要處理傳輸數據到雲服務的隱私問題和延遲問題時。
  • AI嵌入式設備部署如何搞?秘訣在此!
    同時,依託於百度飛槳深度學習平臺相關功能的支持,EasyDL具備強大的端計算部署能力,在生成端計算模型時,會經過一些列的優化、加速、壓縮功能。 對於這一系列能力的講解,可以從網絡結構層面和晶片能力兩方面入手。
  • 從零開始學習YOLOv3教程資源分享
    《從零開始學習YOLOv3》教程是首發於GiantPandaCV公眾號的一個完整的系列,針對的是Github上ultralytics版本的yolov3
  • 從淺層模型到深度模型:概覽機器學習優化算法
    該論文從淺層模型到深度模型縱覽監督學習中常用的優化算法,並指出了每一種優化算法的優點及局限性,同時其還包括了一階和二階等各種算法的形式化表達。機器之心主要對本論文選擇性地編譯了優化算法的部分,更詳細的推導及介紹請查看原論文。
  • 如何從系統層面優化深度學習計算?
    然而,深度學習對於計算能力有著很大的依賴,除了改變模型和算法,是否可以從系統的層面來優化深度學習計算,進而改善計算資源的使用效率?本文中,來自微軟亞洲研究院異構計算組資深研究員伍鳴與大家分享他對深度學習計算優化的一些看法。深度學習在近幾年裡取得了巨大的進步,它已經或者是有望成功地被應用在我們許多生活場景中,比如自動駕駛、安防、翻譯、醫療等等。
  • 用TensorForce快速搭建深度強化學習模型
    強化學習與自動駕駛編輯 · Tony— 正文 —AlphaGo第一作者Davild Silver就認為通用人工智慧需要強化學習結合深度學習來實現,即AGI=DL+RL。,很多機構發布了強化學習基準算法或框架,其中較早的有OpenAI Baselines [1]、伯克利的rllab [2],最新的有18年穀歌發布的Dopamine [3]、Facebook發布的Horizon [4]。
  • 天才黑客George Hotz開源了一個小型深度學習框架tinygrad
    George Hotz 開源了一個小型深度學習框架 tinygrad,兼具 PyTorch 和 micrograd 的功能。tinygrad 的代碼數量不到 1000 行,目前該項目獲得了 GitHub 1400 星。 在深度學習時代,谷歌、Facebook、百度等科技巨頭開源了多款框架來幫助開發者更輕鬆地學習、構建和訓練不同類型的神經網絡。而這些大公司也花費了很大的精力來維護 TensorFlow、PyTorch 這樣龐大的深度學習框架。
  • TensorTrade:基於深度強化學習的Python交易框架
    網際網路上有很多關於強化學習交易系統零零碎碎的東西,但是沒有一個是可靠和完整的。出於這個原因,我們決定創建一個開源的Python框架,使用深度強化學習,有效地將任何交易策略從想法轉化為實際應用。Tensortrade 是一個開源的 Python 框架,用於使用深度強化學習進行訓練、評估和部署穩健的交易策略。
  • 基於 Flask 部署 Keras 深度學習模型
    文 | 風玲兒出處 | 掘金本文主要記錄在進行Flask部署過程中所使用的流程,遇到的問題以及相應的解決方案。1、項目簡介該部分簡要介紹一下前一段時間所做的工作:這是第一次進行深度學習模型的 web 應用部署,在整個過程中,進一步折射出以前知識面之窄,在不斷的入坑、解坑中實現一版。