kuiperInfer使用的模型格式是PNNX(PyTorch Neural Network Exchange)
PNNX由图结构(Graph)、运算符(Operator)和操作数(Operand)这三种结构组成。
1. Graph 图结构
Graph是管理计算图中的运算符(Operator)和操作数(Operand)。
class Graph
{
Operator* new_operator(const std::string& type, const std::string& name);
Operator* new_operator_before(const std::string& type, const std::string& name, const Operator* cur);
Operand* new_operand(const torch::jit::Value* v);
Operand* new_operand(const std::string& name);
Operand* get_operand(const std::string& name);
std::vector<Operator*> ops;
std::vector<Operand*> operands;
};
2. Operator 运算符结构
class Operator
{
public:
std::vector<Operand*> inputs;
std::vector<Operand*> outputs;
std::string type;
std::string name;
std::vector<std::string> inputnames;
std::map<std::string, Parameter> params;
std::map<std::string, Attribute> attrs;
};
3. Operand 操作数结构
class Operand
{
public:
void remove_consumer(const Operator* c);
Operator* producer;
std::vector<Operator*> consumers;
int type;
std::vector<int> shape;
std::string name;
std::map<std::string, Parameter> params;
};
4. RuntimeGraph 结构体
private:
std::string input_name_; /// 计算图输入节点的名称
std::string output_name_; /// 计算图输出节点的名称
std::string param_path_; /// 计算图的结构文件
std::string bin_path_; /// 计算图的权重文件
std::vector<std::shared_ptr<RuntimeOperator>> operators_;
std::map<std::string, std::shared_ptr<RuntimeOperator>> operators_maps_;
std::unique_ptr<pnnx::Graph> graph_; /// pnnx的graph
5. RuntimeOperator 结构体
struct RuntimeOperator {
virtual ~RuntimeOperator();
bool has_forward = false;
std::string name; /// 计算节点的名称 Operator name
std::string type; /// 计算节点的类型 Operator type
std::shared_ptr<Layer> layer; /// 节点对应的计算Layer
std::vector<std::string> output_names; /// 节点的输出节点名称
std::shared_ptr<RuntimeOperand> output_operands; /// 节点的输出操作数
std::map<std::string, std::shared_ptr<RuntimeOperand>> input_operands; /// 节点的输入操作数
std::vector<std::shared_ptr<RuntimeOperand>> input_operands_seq; /// 节点的输入操作数,顺序排列
std::map<std::string, std::shared_ptr<RuntimeOperator>> output_operators; /// 输出节点的名字和节点对应
std::map<std::string, RuntimeParameter*> params; /// 算子的参数信息
std::map<std::string, std::shared_ptr<RuntimeAttribute>>
attribute; /// 算子的属性信息,内含权重信息
};
6. RuntimeOperand 结构体
struct RuntimeOperand {
std::string name; /// 操作数的名称
std::vector<int32_t> shapes; /// 操作数的形状
std::vector<std::shared_ptr<Tensor<float>>> datas; /// 存储操作数
RuntimeDataType type = RuntimeDataType::kTypeUnknown; /// 操作数的类型,一般是float
};
7. RuntimeAttribute 结构体
/// 计算图节点的属性信息
struct RuntimeAttribute {
std::vector<char> weight_data; /// 节点中的权重参数
std::vector<int> shape; /// 节点中的形状信息
RuntimeDataType type = RuntimeDataType::kTypeUnknown; /// 节点中的数据类型
/**
* 从节点中加载权重参数
* @tparam T 权重类型
* @return 权重参数数组
*/
template <class T> //
std::vector<T> get(bool need_clear_weight = true);
/**
* 清除权重
*/
void ClearWeight();
};
class Attribute
{
public:
Attribute()
: type(0)
{
}
#if BUILD_PNNX
Attribute(const at::Tensor& t);
#endif // BUILD_PNNX
Attribute(const std::initializer_list<int>& shape, const std::vector<float>& t);
// 0=null 1=f32 2=f64 3=f16 4=i32 5=i64 6=i16 7=i8 8=u8 9=bool
int type;
std::vector<int> shape;
std::vector<char> data;
};
bool RuntimeGraph::Init() {
if (this->bin_path_.empty() || this->param_path_.empty()) {
LOG(ERROR) << "The bin path or param path is empty";
return false;
}
this->graph_ = std::make_unique<pnnx::Graph>();
int load_result = this->graph_->load(param_path_, bin_path_);
if (load_result != 0) {
LOG(ERROR) << "Can not find the param path or bin path: " << param_path_
<< " " << bin_path_;
return false;
}
std::vector<pnnx::Operator *> operators = this->graph_->ops;
if (operators.empty()) {
LOG(ERROR) << "Can not read the layers' define";
return false;
}
this->operators_.clear();
this->operators_maps_.clear();
for (const pnnx::Operator *op: operators) {
if (!op) {
LOG(ERROR) << "Meet the empty node";
continue;
} else {
std::shared_ptr<RuntimeOperator> runtime_operator =
std::make_shared<RuntimeOperator>();
// 初始化算子的名称
/* Operator{name, type} => RuntimeOperator{name, type} */
runtime_operator->name = op->name;
runtime_operator->type = op->type;
// 初始化算子中的input
/* 输出: RuntimeOperator{input_operands, input_operands_seq} */
/* 输入: Operator{inputs} */
/* producer{name}; type; shape*/
const std::vector<pnnx::Operand *> &inputs = op->inputs;
if (!inputs.empty()) {
InitGraphOperatorsInput(inputs, runtime_operator);
}
// 记录输出operand中的名称
/* 输出: RuntimeOperator{output_names} */
/* 输入: Operator{outputs} */
/* consumers{name} */
const std::vector<pnnx::Operand *> &outputs = op->outputs;
if (!outputs.empty()) {
InitGraphOperatorsOutput(outputs, runtime_operator);
}
// 初始化算子中的attribute(权重)
const std::map<std::string, pnnx::Attribute> &attrs = op->attrs;
if (!attrs.empty()) {
InitGraphAttrs(attrs, runtime_operator);
}
// 初始化算子中的parameter
const std::map<std::string, pnnx::Parameter> ¶ms = op->params;
if (!params.empty()) {
InitGraphParams(params, runtime_operator);
}
this->operators_.push_back(runtime_operator);
this->operators_maps_.insert({runtime_operator->name, runtime_operator});
}
}
return true;
}