一. 產生背景
深度學習的發展帶動了一批深度學習架構,caffe、tensorflow、pytorch等,對於計算量龐大的CNN,效率一直是大家所關注的,接觸過深度網路壓縮的同學應該知道網路壓縮最關鍵的兩個思路,剪枝和量化。
TensorRT就是量化,將FP32位權值資料最佳化為 FP16 或者 INT8,而推理精度不發生明顯的降低。
關於TensorRT首先要清楚以下幾點:
1. TensorRT是NVIDIA開發的深度學習推理工具,只支援推理,不支援訓練;
目前TensorRT3已經支援Caffe、Caffe2、TensorFlow、MxNet、Pytorch等主流深度學習庫;
2. TensorRT底層針對NVIDIA顯卡做了多方面的最佳化,不僅僅是量化,可以和 CUDA CODEC SDK 結合使用,
也就是另一個開發包DeepStream;
3. TensorRT獨立於深度學習架構,通過解析架構檔案來實現,不需要額外安裝DL庫;
參考示意圖:
二. 使用TensorRT
上面是TensorRT的介紹,也可以參考官方文檔,更權威一些:https://developer.nvidia.com/tensorrt
下面以Caffe為例介紹TensorRT的使用:
1. caffeToGIEModel - 將 caffe model 轉換到 TensorRT 格式
+ void caffeToGIEModel( const std::string& deployFile, // name for caffe prototxt
const std::string& modelFile, // name for model
const std::vector<std::string>& outputs, // network outputs
unsigned int maxBatchSize, // batch size - NB must be at least as large as the batch we want to run with)
IHostMemory *&gieModelStream) // output buffer for the GIE model
{
// 1.建立builderIBuilder* builder = createInferBuilder(gLogger);// 2.解析caffe模型,儲存到 NetworkINetworkDefinition* network = builder->createNetwork();ICaffeParser* parser = createCaffeParser();const IBlobNameToTensor* blobNameToTensor = parser->parse(locateFile(deployFile, directories).c_str(), locateFile(modelFile, directories).c_str(),*network, DataType::kFLOAT);// 3.指定輸出Tensorfor (auto& s : outputs) network->markOutput(*blobNameToTensor->find(s.c_str()));// 4.構建enginebuilder->setMaxBatchSize(maxBatchSize);builder->setMaxWorkspaceSize(1 << 20);ICudaEngine* engine = builder->buildCudaEngine(*network);assert(engine);// 5.銷毀parsernetwork->destroy();parser->destroy();// 6.將engine序列化到GIE,退出gieModelStream = engine->serialize();engine->destroy();builder->destroy();
}
2. 執行過程 main
// 1.從caffe模型建立GIE模型,序列化到流IHostMemory *gieModelStream{nullptr};caffeToGIEModel("mnist.prototxt", "mnist.caffemodel", std::vector < std::string > { OUTPUT_BLOB_NAME }, 1, gieModelStream);// x.資料擷取(略)// x.解析mean檔案(略)// 2.還原序列化,得到Runtime engine IRuntime* runtime = createInferRuntime(gLogger);ICudaEngine* engine = runtime->deserializeCudaEngine(gieModelStream->data(), gieModelStream->size(), nullptr);if (gieModelStream) gieModelStream->destroy();// 3.建立上下文IExecutionContext *context = engine->createExecutionContext();// 4.運行inferencefloat prob[OUTPUT_SIZE];doInference(*context, data, prob, 1);// 5.銷毀enginecontext->destroy();engine->destroy();runtime->destroy(); 3. 推理過程 doInference
void doInference(IExecutionContext& context, float* input, float* output, int batchSize)
{
const ICudaEngine& engine = context.getEngine();// 傳遞給引擎的輸入輸出buffer指標- 需要精確的 IEngine::getNbBindings(),這裡1個輸入+1個輸出assert(engine.getNbBindings() == 2);void* buffers[2];// 1.為了綁定buffer,需要知道輸入和輸出tensor的namesint inputIndex = engine.getBindingIndex(INPUT_BLOB_NAME), outputIndex = engine.getBindingIndex(OUTPUT_BLOB_NAME);// 2.建立 GPU buffer 和 streamCHECK(cudaMalloc(&buffers[inputIndex], batchSize * INPUT_H * INPUT_W * sizeof(float)));CHECK(cudaMalloc(&buffers[outputIndex], batchSize * OUTPUT_SIZE * sizeof(float)));cudaStream_t stream;CHECK(cudaStreamCreate(&stream));// 3.通過DMA 輸入到 GPU, 非同步之行batch,並通過DMA回傳CHECK(cudaMemcpyAsync(buffers[inputIndex], input, batchSize * INPUT_H * INPUT_W * sizeof(float), cudaMemcpyHostToDevice, stream));context.enqueue(batchSize, buffers, stream, nullptr);CHECK(cudaMemcpyAsync(output, buffers[outputIndex], batchSize * OUTPUT_SIZE*sizeof(float), cudaMemcpyDeviceToHost, stream));cudaStreamSynchronize(stream);// 4.釋放 stream 和 buffercudaStreamDestroy(stream);CHECK(cudaFree(buffers[inputIndex]));CHECK(cudaFree(buffers[outputIndex]));
}
三. 模型轉化
TensorRT3.0 雖然號稱支援 Caffe、caffe2、TensorFlow、Pytorch等網路模型,實際上例子只提供了Caffe和TensorFlow的直接支援。
對於caffe的支援比較簡單,可以直接通過載入deploy file和caffemodel來做,而對於tensorflow則是通過轉換為uff格式來載入,可以參考範例程式。
網路模型轉換及部署可以分為三個步驟:
1)訓練模型並儲存為.pb檔案;
2)將.pb檔案轉成.uff格式;
3)利用TensorRT載入並運行模型;
對於caffe2,暫時沒找到比較好的轉換工具,有經驗的朋友可以探討。
四. 關於統一模型的討論
在當下深度學習架構肆虐的情況下,tensorflow、pytorch、caffe、caffe2、mxnet、CNTK等等,都有自己的使用者群,不同架構之間的格式為交流及資料共用帶來了巨大的弊端,各種轉碼,另攻城獅不勝其煩,有競爭必然有結盟,大一統是必然結果。
比如我們來看從 caffe -> caffe2模型格式轉換過程
可以採用caffe2提供的轉換指令碼 caffe_translator.py
python -m caffe2.python.caffe_translator deploy.prototxt pretrained.caffemodel
不同架構之間通過指令碼進行格式轉換。 NNVM & ONNX
NNVM來自於陳天奇團隊,對其比較直觀的描述,我們可以參考:
TVM和之前發布的模組化深度學習系統NNVM一起,“組成深度學習到各種硬體的完整最佳化工具鏈”。
與LLVM一致,可以理解為將不同語言(DL架構)進行編譯、最佳化、連結的 編譯器。其目的在於將多個DL架構的使用者進行整合,與TF進行PK(TF底層也有同樣的功能模組)。
或者說,基於NNVM,你可以實現一套自己的深度學習架構(利用tinyflow),代碼只需要2000行,可以實現到各種硬體的快速編譯部署,聽著就很Tool。
ONNX是Facebook、微軟聯合推出的一個開放標準,旨在不同架構之間完成互操作;
聽起來像NNVM如此宏大,將來是一個什麼樣的關係,還有待於進一步跟進。
就目前來看,ONNX首先著力於結果模型的轉化,也就是推理部分。
(關於標準和入口向來是兵家必爭,目前階段不需要盲目投入過多精力,靜等某方勝出方為上冊,形勢很快明朗)