发布时间: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核编写等文章的编写,尽情期待!