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


关注我的公众号,分享资源
公众号搜索: 卡本特
扫码关注

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