mobileNet-ssd使用tensorRT部署

mobilenet-ssd使用tensorRT部署

一,將deploy.prototxt改寫爲deploy_plugin.prototxt

1,convolution層的param{}全部去掉,convolution_param中的weight_filter{}去掉,bias_filter{}去掉

2,將自定義層的名字改寫爲IPlugin,自定義層的參數寫在新寫的class裏面

3,ssd的detection_out層的detection_output_param去掉,然後新加一個top:detection_out2
因爲在tensorRT中,默認的輸出是兩個,如果只有一個top,那麼程序會報錯。
“Plugin layer output count is not equal to caffe output count”.

二,完成自定義層的代碼

1,先實現PluginFactory的class,繼承IPluginFactory,class中定義需要使用的自定義層

2,實現 bool PluginFactory::isPlugin(const char* name)

3,實現

nvinfer1::IPlugin* PluginFactory::createPlugin(const char* layerName, const nvinfer1::Weights* weights, int nbWeights)

通過if語句判斷是屬於哪一層,然後在條件分支中實現新的層的參數傳遞,return mXXXX_layer.get()返回指針

三,tensorRT的使用步驟

1,caffeToTRTModel

void TensorNet::caffeToTRTModel(const std::string& deployFile, const std::string& modelFile, const std::vector<std::string>& outputs,
                                unsigned int maxBatchSize)
{
    IBuilder* builder = createInferBuilder(gLogger);
    INetworkDefinition* network = builder->createNetwork();

    ICaffeParser* parser = createCaffeParser();
    parser->setPluginFactory(&pluginFactory);

    bool useFp16 = builder->platformHasFastFp16();
    useFp16 = false;

    DataType modelDataType = useFp16 ? DataType::kHALF : DataType::kFLOAT;

    std::cout << deployFile.c_str() <<std::endl;
    std::cout << modelFile.c_str() <<std::endl;
    //std::cout << (*network) <<std::endl;
    std::cout << "Here : 1"<<std::endl;
    const IBlobNameToTensor* blobNameToTensor =	parser->parse(deployFile.c_str(),modelFile.c_str(),*network,                DataType::kFLOAT);
    std::cout << "Here : 2" <<std::endl;
    assert(blobNameToTensor != nullptr);
    std::cout << "Here : 3" <<std::endl;
    for (auto& s : outputs) network->markOutput(*blobNameToTensor->find(s.c_str()));

    builder->setMaxBatchSize(maxBatchSize);
    builder->setMaxWorkspaceSize(10 << 20);
    std::cout << "Here : 4"<< std::endl;
    ICudaEngine* engine = builder->buildCudaEngine( *network );
    std::cout << "Here : 5"<<std::endl;
    assert(engine);
    network->destroy();
    parser->destroy();
    gieModelStream = engine->serialize();
    engine->destroy();
    builder->destroy();
    pluginFactory.destroyPlugin();
    shutdownProtobufLibrary();

}

如果這個函數執行完成沒有問題,那麼整個網絡結構修改的沒有問題。

問題1:
我目前卡在了const IBlobNameToTensor* blobNameToTensor = parser->parse(),QtCreator提示The program has unexpectedly finished.估計是訪問了非法內存還是咋地,調試也不好使。
2018,10,10 這個問題真是自己傻逼了,prototxt改寫plugin的時候改錯了,有一層沒有輸入,檢查也沒有發現這個問題,tensorRT運行的時候直接segmentation fault。
總結一下就是,如果遇到一個問題,百度谷歌全部都找不到相關信息,那麼這個問題只有兩種可能,一是這個問題很牛逼,別人都還沒有發現;二是這個問題很傻逼,別人發現了不想寫出來。對於我來說,遇到了牛逼問題的可能性很小,所以一般都是自己傻逼了。找這種bug的時候,把自己當成一個傻逼,往自己覺得自己不可能犯錯的地方取查

問題2:

ICudaEngine* engine = builder->buildCudaEngine( *network );
assert(engine);

assertion error,創建engine失敗
最後事實證明,這也是一個弱智問題,問題還是存在於prototxt中,修改類別之後的相應的num_output不匹配導致的問題,修改之後就可以了。

問題3:
virtual void nvinfer1::plugin::DetectionOutput::configure(const nvinfer1::Dims*, int, const nvinfer1::Dims*, int, int): Assertion `numPriorsnumLocClasses4 == inputDims[param.inputOrder[0]].d[0]’ failed.
感覺這個問題會很棘手,在github上有人正在討論。
https://github.com/dusty-nv/jetson-inference/issues/171
https://github.com/chenzhi1992/TensorRT-SSD/issues/26
我嘗試了上面所說的方法,將我的tensorRT升級到tensorRT4.0 然後在detection_out層的參數中加入inputOrder = {0,1,2},然後就正常了。
如果使用tensorRT3.0的話,該怎麼辦呢?我覺得應該是有辦法的,先留個坑,全部調通之後在研究tensorRT3.0的API
(2018_10_12)找到原因了,在tensorRT3.0中,在reshape類中的getOutputDimensions的函數返回值,調整一下順序就可以了。 如果使用tensorRT4.0,只是在detection_out中增加了一個inputOrder的參數選項,其實兩者的效果是一樣的。

Dims getOutputDimensions(int index, const Dims* inputs, int nbInputDims) override
    {
        assert(nbInputDims == 1);
        assert(index == 0);
        assert(inputs[index].nbDims == 3);
        assert((inputs[0].d[0])*(inputs[0].d[1]) % OutC == 0);
        //輸出的順序
        //return DimsCHW(OutC, inputs[0].d[0] * inputs[0].d[1] / OutC, inputs[0].d[2]);
        return DimsCHW( inputs[0].d[0] * inputs[0].d[1] / OutC, OutC, inputs[0].d[2]);
    }

分別打印出detection_out層的輸入 mbox_conf_softmax,mbox_loc和mbox_priorbox的尺寸
我這裏分類是5類(4 + background)
分別 C H W
mbox_conf_softmax ------> 5 1917 1
mbox_loc ---------> 7668 1 1
mbox_priorbox ---------> 2 7668 1
可以通過如下代碼來打印想觀察的層的輸出維度

const char* OUTPUT1 = "mbox_conf_softmax";
const char* OUTPUT2 = "mbox_loc";
const char* OUTPUT3 = "mbox_priorbox";
const char* OUTPUT4 = "mbox_conf_flatten";
const char* OUTPUT_BLOB_NAME = "detection_out";

tensorNet.caffeToTRTModel( model, weight, std::vector<std::string>{ OUTPUT_BLOB_NAME }, BATCH_SIZE);

DimsCHW dimsOut  = tensorNet.getTensorDims(OUTPUT_BLOB_NAME);
DimsCHW dimsOut1  = tensorNet.getTensorDims(OUTPUT1);
DimsCHW dimsOut2  = tensorNet.getTensorDims(OUTPUT2);
DimsCHW dimsOut3  = tensorNet.getTensorDims(OUTPUT3);
DimsCHW dimsOut4  = tensorNet.getTensorDims(OUTPUT4);

cout << "INPUT Tensor Shape is: C: "  <<dimsData.c()<< "  H: "<<dimsData.h()<<"  W:  "<<dimsData.w()<<endl;
cout << "mbox_conf_softmax Tensor Shape is: C: "<<dimsOut1.c()<<"  H: "<<dimsOut1.h()<<"  W: "<<dimsOut1.w()<<endl;
cout << "mbox_loc Tensor Shape is: C: "<<dimsOut2.c()<<"  H: "<<dimsOut2.h()<<"  W: "<<dimsOut2.w()<<endl;
cout << "mbox_priorbox Tensor Shape is: C: "<<dimsOut3.c()<<"  H: "<<dimsOut3.h()<<"  W: "<<dimsOut3.w()<<endl;
cout << "mbox_conf_flatten Tensor Shape is: C: "<<dimsOut4.c()<<"  H: "<<dimsOut4.h()<<"  W: "<<dimsOut4.w()<<endl;
cout << "OUTPUT Tensor Shape is: C: "<<dimsOut.c()<<"  H: "<<dimsOut.h()<<"  W: "<<dimsOut.w()<<endl;
cout << "conv11 Tensor Shape is: C: "<<dimsOutConv11.c()<<"  H: "<<dimsOutConv11.h()<<"  W: "<<dimsOutConv11.w()<<endl;

這樣就可以判斷自己寫的層的輸出維度是不是對的,跟caffe的打印的層的維度是否一樣。如果維度是一樣的,網絡結構就應該沒有問題了

2,tensorNet.createInference()

問題4
輸出結果不對
在推理過程中沒有報錯了,但是出來的結果不對。
detection_out層輸出的結果,7個值分別是 0 -1 0 0 0 0 0 這樣的結果肯定是不對的
猜測有幾個原因:1,輸入圖片的均值和歸一化的值不對;2,取結果的方式不對;3,推理過程中有的層的參數不對。
2018-10-17
找到原因了:圖片輸入的方式不對,例如訓練VOC-SSD的時候,直接是減去三個通道的均值,並沒有進行scale操作,但是訓練mobileNet的時候有進行scale操作。所以如果tensorRT在進行圖片輸入的時候,不進行scale操作,那麼輸入的數據是有問題的。在util/loadImage.cpp中進行相應地修改。

問題5
輸出結果依然不對
輸出的7個值分別類似於
0 3 0.95 0.243 0.25 0.244 0.25
可以明顯地看出來class和confidence是正確的,就是bbox的值不對,這一點可以通過換測試圖片來驗證。
既然class和confidence都是正確的,那麼可以證明 softmax和mbox_conf_flatten是正確的,那麼也就是priorbox的輸出不正確。
然後對着原始的deploy.prototxt文件中的priorbox層的param參數來一一比對。
例如這一層

layer {
  name: "conv11_mbox_priorbox"
  type: "PriorBox"
  bottom: "conv11"
  bottom: "data"
  top: "conv11_mbox_priorbox"
  prior_box_param {
    min_size: 60.0
    aspect_ratio: 2.0
    flip: true
    clip: false
    variance: 0.1
    variance: 0.1
    variance: 0.2
    variance: 0.2
    offset: 0.5
  }
}

對應tensorRT中的mConv11_mbox_priorbox_layer中的形式應該是

else if (!strcmp(layerName, "conv11_mbox_priorbox"))
    {
        assert(mConv11_mbox_priorbox_layer.get() == nullptr);
        //參數按照原來的prototxt中的prior_box_param設置
        PriorBoxParameters params;
        float minsize[1] = {60},aspect_ratio[2] = {1.0,2.0};
        params.minSize = minsize;
        params.aspectRatios = aspect_ratio;
        params.numMinSize = 1;
        params.numAspectRatios = 2;
        params.maxSize = nullptr;
        params.numMaxSize = 0;
        params.flip = true;
        params.clip = false;
        params.variance[0] = 0.1;
        params.variance[1] = 0.1;
        params.variance[2] = 0.2;
        params.variance[3] = 0.2;
        params.imgH = 0;
        params.imgW = 0;
        params.stepH = 0;
        params.stepW = 0;
        params.offset = 0.5;
        mConv11_mbox_priorbox_layer = std::unique_ptr<INvPlugin, decltype(nvPluginDeleter)>
                (createSSDPriorBoxPlugin(params), nvPluginDeleter);

        return mConv11_mbox_priorbox_layer.get();
    }

我這裏犯錯的原因是,由於prototxt中的參數有幾個是缺省值,沒有寫進去。例如conv11_mbox_priorbox層中 prior_box_param就沒有maxSize,沒有step值等等。所以我在tensorRT中以爲也是默認值,所以沒有寫。
所以才導致最後出現的框中的座標值是錯誤的。正確的做法因該是prototxt中沒有出現的值默認應該以0補充,如果是指針形式,以nullptr補充。

踩完這些坑,結果就出來了。
在這裏插入圖片描述

還有幾點不是太明白
1,priorbox,caffe和tensorRT的輸出shape不一樣
tensorRT的priorbox的輸出shape,C通道一直都是2
caffe的priorbox的輸出shape,C 通道是W的2倍
在github上的issue區討論的,別人都說如果遇到問題,就一層一層的和caffe輸出對比。但是這裏輸出維度都不一樣,比個錘子啊????

2,tensorRT輸出結果,每次都不一樣
每次都不一樣的表現就是,bbox看上去在抖,同一張圖片。但是不影響最後的檢測效果。
不知道這裏會是什麼原因? 如果有知道的同學說一下可能的原因。

(更新~~)關於上面說的每次輸出結果都不一樣的問題,是由於一個變量在定義的時候未初始化導致的,下面的github的repo更新了,解決了這個問題,一直忘記了來更新博文,所以我不記得是哪個變量了··· 大家見諒,具體得到代碼中去找了。

代碼鏈接
https://github.com/Ghustwb/MobileNet-SSD-TensorRT/blob/master/main.cpp


關注我的公衆號,分享資源
公衆號搜索: 卡本特
掃碼關注

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章