发布时间: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核编写等文章的编写,尽情期待!
智能运维 VS 传统运维|AIOps服务管理解决方案全面梳理
Identity Server 4使用OpenID Connect添加用户身份验证(三)
YOLOv5改进之五:改进特征融合网络PANET为BIFPN
北大换新校长!中国科学院院士龚旗煌接任,15岁考上北大物理系
视频教程-java网上书城|美妆商城|电商系统项目实战含微信支付功能-Java
c语言中定义结构体指针并指向一片内存空间和直接定义一个结构体变量的区别 Node *p=(Node *)malloc(sizeof(Node)); 和 Node p 两个有什么区别??? Node是一