你或许也想拥有专属于自己的AI模型文件格式(推理部署篇)-(6)

发布时间:2022-08-19 13:18

        如果有新的观众,如有疑惑,请从《专ai模》这个专栏从头开始阅读。特别是想要彻底搞清楚深度学习中模型的起源,以及如何一步一步构建自定义模型格式的观众来说,除了可视化篇,前面的三章必须要仔细阅读。

        而为什么我又有雅致来开启《推理部署篇》呢?主要原因是,目前个人野心有点大,想要从零构建一整套关于推理部署的工具链吧。也就是向着推理引擎的方向努力吧。特别是自从上次结束了《可视化篇》,总感觉意犹未尽。

        因此,全新的篇章开启了,该篇章的文章数可能会控制在10篇以下吧。不过也可能随着后续的深入,增加新文章。至少现阶段的一个目标就是能够让专ai模利用opencl进行推理部署吧。那么很自然地,任何一个推理引擎都包含一个叫做前端解析的功能,不同的推理框架对前端模型的支持各不相同,有的能够接入pytorch、tensorflow、darknet、caffe等。但是,我们的主要目的是支持自己的模型格式,因此对于我们而言前端解析工作量是少了许多。

        根据该专栏的第一篇文章的专ai模定义,我们可以很容易知道我们的前端解析的目标:解析生成的.pzkm模型文件,从中剥离出附属信息、张量信息、网络层信息等,这些信息最好是组织成C++结构体或者是类变量,以便后续的推理时接口组成推理时网络。

一、新增结构体

        之前我们只是编写了能够生成模型的接口,因此Tensor相关的结构体没有考虑专门,也就直接生成了模型文件。现在我们希望后续的代码能够脱离flatbuffers的使用,因此特意按照.fbs的定义使用C++结构体重写了一下,定义如下:

// TensorShape
struct TensorShapeS{
    uint8_t dimsize;
    std::vector dims;
};
// Weights
struct WeightsS{
    uint8_t ele_bytes;
    uint64_t ele_num;
    std::vector buffer;
};
// Tensor
struct TensorsS{
    uint32_t id;
    std::string name;
    TensorType tensor_type;
    DataType data_type;
    DataLayout data_layout;
    struct TensorShapeS shape;
    struct WeightsS weights;
};

二、定义读取模型文件的接口

        为了减少新的类的编写,尽可能复用代码,我们依然打算在PzkM类中增加前端解析接口来实现模型的读取,如下所示:

// main class for build pzkmodel
class PzkM
{
private:
    /* data */
public:
.....
    //前端解析接口,传入模型文件路径就可以实现解析
    bool ReadModel(std::string modelfile);
.....
}

三、编写读取模型文件的接口

        这里前端解析的逻辑其实非常简单,就是尽可能地使用到.fbs中PModel内部的所有元数据,解析流程如下所示:

bool PzkM::ReadModel(std::string modelfile)
{
    // 1.清空模型的所有数据,进行初始化
    this->clear_data();
    // 2.使用flatbuffer接口从读出来的模型二进制数据进行解析
    std::ifstream infile;
    infile.open(modelfile.c_str(), std::ios::binary | std::ios::in);
    infile.seekg(0, std::ios::end);
    int length = infile.tellg();
    infile.seekg(0, std::ios::beg);
    char* data = new char[length];
    infile.read(data, length);
    infile.close();
    auto my_model = GetPModel(data);
    
    // 3.从模型中读取网络信息
    // 3.1 读取附属信息
    this->author = std::string(my_model->author()->c_str());
    this->version = std::string(my_model->version()->c_str());
    this->model_name = std::string(my_model->model_name()->c_str());
    this->model_runtime_input_num = my_model->model_runtime_input_num();
    this->model_runtime_output_num = my_model->model_runtime_output_num();
    auto rt_input_id = my_model->model_runtime_input_id();
    for (size_t i = 0; i < rt_input_id->size(); i++)
        this->model_runtime_input_id.push_back(rt_input_id->Get(i));
    auto rt_output_id = my_model->model_runtime_output_id();
    for (size_t j = 0; j < rt_output_id->size(); j++)
        this->model_runtime_output_id.push_back(rt_output_id->Get(j));
    // 3.2 读取tensor信息
    auto rt_tensor_buffer = my_model->tensor_buffer();
    for (size_t m = 0; m < rt_tensor_buffer->size(); m++){
        auto one_tensor = rt_tensor_buffer->Get(m);
        struct TensorsS tess;
        tess.shape.dims.clear();
        tess.weights.buffer.clear();
        tess.id = one_tensor->id();
        tess.name = std::string(one_tensor->name()->c_str());
        tess.tensor_type = one_tensor->tesor_type();
        tess.data_type = one_tensor->data_type();
        tess.data_layout = one_tensor->data_layout();
        tess.shape.dimsize = one_tensor->shape()->dimsize();
        for (size_t n = 0; n < tess.shape.dimsize; n++)
            tess.shape.dims.push_back(one_tensor->shape()->dims()->Get(n));
        if (tess.tensor_type == TensorType_CONST){
            tess.weights.ele_bytes = one_tensor->weights()->ele_bytes();
            tess.weights.ele_num = one_tensor->weights()->ele_num();
            for (size_t n = 0; n < one_tensor->weights()->buffer()->size(); n++)
                tess.weights.buffer.push_back(one_tensor->weights()->buffer()->Get(n));
        }
        this->rTensors.push_back(tess);
    }
    // 3.3 读取网络层信息
    auto rt_layer_buffer = my_model->layer_buffer();
    for (size_t m = 0; m < rt_layer_buffer->size(); m++){
        auto one_layer = rt_layer_buffer->Get(m);
        layer_maker l = layer_maker(meta.get_meta(std::string(one_layer->type()->c_str())), one_layer->id(), std::string(one_layer->name()->c_str()));
        // 3.3.1 读取网络输入信息
        auto input_conn = one_layer->input_id();
        for (size_t n = 0; n < input_conn->size(); n++){
            auto one_conn = input_conn->Get(n);
            l.add_input(one_conn->tensor_id(), std::string(one_conn->name()->c_str()));
        }
        // 3.3.2 读取网络输出信息
        auto output_conn = one_layer->output_id();
        for (size_t n = 0; n < output_conn->size(); n++){
            auto one_conn = output_conn->Get(n);
            l.add_output(one_conn->tensor_id(), std::string(one_conn->name()->c_str()));
        }
        // 3.3.3 读取网络附属信息
        auto all_attrs = one_layer->attrs()->buffer();
        for (size_t n = 0; n < all_attrs->size(); n++){
            auto one_attr = all_attrs->Get(n);
            std::vector a;
            for (size_t k = 0; k < one_attr->buffer()->size(); k++)
                a.push_back(one_attr->buffer()->Get(k));
            l.add_attr(std::string(one_attr->key()->c_str()), a);
        }
        this->rLayers.push_back(l);
    }
    delete data;
    return true;
}

        如此一来,对于模型的解析工作就已经完成了,后续将会正式开始推理运行时的网络构图、opencl核编写等文章的编写,尽情期待!

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号