对于我们自定义的层如果写到了caffe prototxt中,在部署模型时调用caffeparser来解析就会报错。还是以Upsample为例,如果在prototxt中有下面这段来添加了一个upsample的层
layer {
name: "upsample0"
type: "Upsample"
bottom: "ReLU11"
top: "Upsample1"
}
这时再调用
const IBlobNameToTensor *blobNameToTensor = parser->parse(deployFile.c_str(),
modelFile.c_str(),
*network,
modelDataType);
就会出现错误
could not parse layer type Upsample
之前我们已经编写了Upsample的插件,怎么让tensorRT的caffe parser识别出prototxt中的upsample层自动构建我们自己编写的插件呢?这时我们就需要定义一个插件工程类继承基类nvinfer1::IPluginFactory, nvcaffeparser1::IPluginFactoryExt。
class PluginFactory : public nvinfer1::IPluginFactory, public nvcaffeparser1::IPluginFactoryExt
{
//其中必须要的实现的方法有:
//判断一个层是否是plugin的方法,
//输入的参数就是prototxt中layer的name,通过name来判断一个层是否注册为插件
bool isPlugin(const char *name) override {
return isPluginExt(name);
}
//判断层名字是否是upsample层的名字
bool isPluginExt(const char *name) override {
char *aa = new char[6];
memcpy(aa, name, 5);
aa[5] = 0;
int res = !strcmp(aa, "upsam");
return res;
}
//根据名字创建插件的方法,有两中方式:
//一个是由权重构建
//另一个是由序列化后的比特流创建,
//对应了插件的两种构造函数,Upsample没有权重,对于其他有权重的插件就能够用传入的weights初始化层。mplugin是一个vector用来存储所有创建的插件层
IPlugin *createPlugin(const char *layerName, const nvinfer1::Weights *weights, int nbWeights) override {
assert(isPlugin(layerName));
mPlugin.push_back(std::unique_ptr(new Upsample(2)));
return mPlugin[mPlugin.size() - 1].get();
}
IPlugin *createPlugin(const char *layerName, const void *serialData, size_t serialLength) override {
assert(isPlugin(layerName));
return new Upsample(serialData, serialLength);
}
std::vector > mPlugin;
//最后需要定义一个destroy方法来释放所有创建的插件层。
void destroyPlugin()
{
for (unsigned int i = 0; i < mPlugin.size(); i++)
{
mPlugin[i].reset();
}
}
}
对于prototxt存在多个多种插件的情况,可以在isPlugin,createPlugin方法中添加新的条件分支,根据层的名字创建对应的插件层。
实现了PluginFactory之后在调用caffeparser的时候需要设置使用它,在调用parser->parser之前加入如下代码
PluginFactory pluginFactory;
parser->setPluginFactoryExt(&pluginFactory);
就可以设置parser按照pluginFactory里面定义的规则来创建插件层,这样之前出现的不能解析Upsample层的错误就不会再出现了。