初次尝试Tengine 适配 Ncnn FP32 模型,相较于mnn,ncnn而言,这个框架对于多框架模型有着很好的支持性 – Caffe,Tensorflow,TF-Lite, Mxnet, Onnx, darknet。
首发知乎:https://zhuanlan.zhihu.com/p/...
作者:空域
最近对AI 推理框架比较感兴趣,在众多的前端推理框架中选择了一款比较适合自己学习的前端推理Tengine。相较于mnn,ncnn而言,这个框架对于多框架模型有着很好的支持性 – Caffe,Tensorflow,TF-Lite, Mxnet, Onnx, darknet。相比较于市面上的框架,还缺少一个ncnn的适配。所以自己就尝试这写了一下。
开始写的时候要对Tengine 对各种模型的适配进行学习。Tengine对于运算的规则便是建立一个graph,把各个所需要的算子转换为graph中的node来进行。相较于图的建立,第一个困难点便是对与不同框架的模型解析。
Ncnn的适配目前只针对了FP32格式。首先感谢ncnn的作者,文件解析后的数据排布想的浅显易懂,方便后人去学习。对于ncnn的模型解读,可以进下列传送门:
https://github.com/Tencent/ncnn/wiki/param-and-model-file-structure
上述网站中对ncnn的param和bin做了很好的解读。把这些信息都出来之后便可以关入到Tengine的图的构建之中。
Tengine的主题graph构建入口如下:
bool NcnnSerializer::LoadModel(const std::vector<std::string>& file_list, StaticGraph* graph)
{
if(file_list.size() != GetFileNum())
return false;
std::vector<NcnnNode> nodelist;
if(!LoadTextFile(file_list[0].c_str(), nodelist))
{
LOG_ERROR() << "Parse text file " << file_list[0].c_str() << " failed\n";
return false;
}
std::vector<NcnnParam> paramlist;
if(!LoadBinaryFile(file_list[1].c_str(), paramlist, nodelist))
{
LOG_ERROR() << "Parse binary file " << file_list[1].c_str() << " failed\n";
return false;
}
SetGraphSource(graph, file_list[1]);
SetGraphSourceFormat(graph, "ncnn");
SetGraphConstTensorFile(graph, file_list[1]);
SetGraphLayout(graph, TENGINE_LAYOUT_NCHW);
SetModelLayout(graph, TENGINE_LAYOUT_NCHW);
SetModelFormat(graph, MODEL_FORMAT_NCNN);
bool res = LoadGraph(graph, nodelist, paramlist);
if(res == false){
LOG_ERROR() << "Load Graph failed\n";
return false;
}
for(std::size_t ii = 0; ii < paramlist.size(); ++ii)
{
std::free(paramlist[ii].data);
}
return true;
}
在代码中首先用LoadTextFile解析ncnn的param文件,可以知道每一个tensor的尺寸与各种参数信息。通过这些信息可以对每个tensor和node进行内存的分配。再用LoadBinaryFile来解析每个op中所存储的数据,例如bias,weight,scale等信息。
等到获取node信息与数据后,在通过上述函数LoadGraph来构建Tengine计算图。在LoadGraph中会存在两个函数,如下:
LoadConstTensor(graph, nodelist, paramlist);
CreateInputNode(graph, nodelist, paramlist);
LoadConstTensor函数中则是对每一个参数tensor进行内存的建立以及数据的赋值。下一个CreateInputNode则是对各个tensor的整合。例如:
Convolution 为一个node,Convolution中含有weight,bias tensor。在LoadInputNode函数中则是把Weight,Bias 这两个tensor并入到Convolution node中。在这,在这个函数中则依据从ncnn param获取的每个node的inputs,outputs信息来连接每个node。
通过上述函数则初步建立Tengine计算图。具体代码通过如下传送门:
https://github.com/OAID/Tengine/tree/master/tools/plugin/serializer/ncnn
上述则是Tengine如何解析ncnn的方法。
在解析ncnn的模型中发现解析ncnn的模型最优的解析便是让ncnn的模型先通过ncnnoptimizer优化,把算子fused,后在解析模型与适配算子能提升很高的效率。
通过此次适配,可以适配ncnn优化后的模型如下:Caffe(squeezenet,mobilenet,mobilenetSSD, resnet18),mxnet(retinaface)
初次写文章,只能写个大概,哈哈哈哈哈哈哈哈,也算是自己第一篇学习笔记。
更多Tengine框架相关请关注Tengine-边缘AI推理框架专栏 及作者知乎(@空域)