26

空域 · 2020年05月09日

初次尝试Tengine 适配 Ncnn FP32 模型

初次尝试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推理框架专栏 及作者知乎(@空域)
推荐阅读
关注数
3393
内容数
68
Tengine是一款轻量级模块化高性能的神经网络推理引擎 ;欢迎体验Tengine,[链接] 《Tengine开发者入门资料包》[链接]
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息