多对多场景开发注意点
多对多场景开发注意点包括多对多场景开发流程、设置融合结果和内部小算子所需的信息处理。
简介
Scope多对多融合场景,即把Scope内的多个小算子融合为一系列小算子组合。
实现流程
实现流程请参考Scope多对多融合场景,不同点在于:
- 在融合结果设置函数GenerateFusionResult中需要设置内部小算子组合的连接关系。
- 内部小算子后面将不再走插件解析, 因此小算子需用户自行构造成IR表示,并设置好所需属性等参数。
设置融合结果
这里为了逻辑的正确,示例的代码是把DecodeBboxV2算子输入输出前均加上Identity算子,以此来达到添加多个算子的目的,实际操作过程中可根据具体的算子功能需求添加小算子组合。
主要过程包括:
- 设置融合结果的名字、描述,以及与外部的输入、输出,和多对一场景相同;
- 设置融合算子类型为特定类型kScopeToMultiNodes,表明融合成多个算子组合;
fusion_rlt->SetType(kScopeToMultiNodes); // 设置特定类型,表明融合成多个算子组合
- 通过AddInnerNode设置对应的小算子,并设置名字、类型、属性、输入、输出等相关信息。
图1 添加融合小算子示意图
例如:
auto in_identity_1 = fusion_rlt->AddInnerNode("input_identity_1", "Identity"); CHECK_INNER_NODE_CONDITION(in_identity_1 != nullptr, fusion_rlt); ret = in_identity_1->InsertInput(kInputFromFusionScope, 1) // 输入来自融合结果边界的第1个输入 .InsertOutput("inner_core_decode_bbox_v2", 1) // 输出给内部算子 .BuildInnerNode(); CHECK_INNER_NODE_CONDITION(ret == ge::GRAPH_SUCCESS, fusion_rlt);- kInputFromFusionScope:表示目标小算子的输入来自scope边界,另外kOutputToFusionScope表示目标小算子的输出送到scope边界。
- InsertInput:表示设置目标小算子的输入。
- InsertOutput:表示设置目标小算子的输出。
- BuildInnerNode:表示构建内部小算子。
完整代码示例为:void DecodeBboxV2MultiScopeFusionPass::GenerateFusionResult(const std::vector<Scope *> &scopes, FusionScopesResult *fusion_rlt) { if (fusion_rlt == nullptr) { return; } if (scopes.size() != 1) { fusion_rlt->SetType(kScopeInvalidType); return; } // 设置融合结果输入,来自transpose的第0个输入作为融合结果的第0个输入,transpose的第一个输入不使用 fusion_rlt->InsertInputs("transpose", {0, kFusionDisableIndex}); // 设置融合结果输入,将get_center_coordinates_and_sizes/transpose的第0个输入作为融合结果的第1个输入,get_center_coordinates_and_sizes/transpose的第一个输入不使用 fusion_rlt->InsertInputs("get_center_coordinates_and_sizes/transpose", {1, kFusionDisableIndex}); // 设置融合结果输出, 将transpose_1的第0个输出作为融合结果的输出 fusion_rlt->InsertOutputs("transpose_1", {0}); fusion_rlt->SetType(kScopeToMultiNodes); // 设置特定类型,表明融合成多个小算子组合 AscendString scope_name; Status ret = scopes[0]->Name(scope_name); if (ret != SUCCESS) { return ; } std::string str_scope_name; if (scope_name != nullptr) { str_scope_name = scope_name.GetString(); } fusion_rlt->SetName(str_scope_name.substr(0, str_scope_name.length() - 1).c_str()); fusion_rlt->SetDescription(""); // 添加融合小算子 auto in_identity_0 = fusion_rlt->AddInnerNode("input_identity_0", "Identity"); CHECK_INNER_NODE_CONDITION(in_identity_0 != nullptr, fusion_rlt); Status ret = in_identity_0->InsertInput(kInputFromFusionScope, 0) // 输入来自融合结果边界的第0个输入 .InsertOutput("inner_core_decode_bbox_v2", 0) // 输出给内部小算子 .BuildInnerNode(); CHECK_INNER_NODE_CONDITION(ret == ge::GRAPH_SUCCESS, fusion_rlt); std::string str_attr = "input_0_identity_attr"; in_identity_0->MutableOperator()->SetAttr("key", str_attr); auto in_identity_1 = fusion_rlt->AddInnerNode("input_identity_1", "Identity"); CHECK_INNER_NODE_CONDITION(in_identity_1 != nullptr, fusion_rlt); ret = in_identity_1->InsertInput(kInputFromFusionScope, 1) // 输入来自融合结果边界的第1个输入 .InsertOutput("inner_core_decode_bbox_v2", 1) // 输出给内部小算子 .BuildInnerNode(); CHECK_INNER_NODE_CONDITION(ret == ge::GRAPH_SUCCESS, fusion_rlt); auto core_decode_bbox = fusion_rlt->AddInnerNode("inner_core_decode_bbox_v2", kScopeType); CHECK_INNER_NODE_CONDITION(core_decode_bbox != nullptr, fusion_rlt); ret = core_decode_bbox->InsertInput("input_identity_0", 0) .InsertInput("input_identity_1", 0) .InsertOutput("output_identity", 0) .BuildInnerNode(); CHECK_INNER_NODE_CONDITION(ret == ge::GRAPH_SUCCESS, fusion_rlt); // 根据需要设置融合后小算子参数 auto parser_ret = DecodeBboxV2ParseParams(fusion_rlt->Nodes(), core_decode_bbox->MutableOperator()); CHECK_INNER_NODE_CONDITION(parser_ret == SUCCESS, fusion_rlt); auto out_identity = fusion_rlt->AddInnerNode("output_identity", "Identity"); CHECK_INNER_NODE_CONDITION(out_identity != nullptr, fusion_rlt); ret = out_identity->InsertInput("inner_core_decode_bbox_v2", 0) // 输入来自内部小算子的第0个输出 .InsertOutput(kOutputToFusionScope, 0) // 输出给融合结果边界的第0个输出 .BuildInnerNode(); CHECK_INNER_NODE_CONDITION(ret == ge::GRAPH_SUCCESS, fusion_rlt); ret = fusion_rlt->CheckInnerNodesInfo(); CHECK_INNER_NODE_CONDITION(ret == ge::GRAPH_SUCCESS, fusion_rlt); OP_LOGI(kOpType, "Set fusion multi-to-multi result successfully."); return; }
内部小算子所需的信息处理
融合结果内部的小算子可能需要设置某些属性等处理,由于小算子不再走插件去解析,因此需要在构造小算子时添加。示例中的目标小算子DecodeBboxV2需要根据原图scope中小算子获取scale信息。
namespace {
Status ParseFloatFromConstNode(const ge::OperatorPtr node, float &value) {
if (node == nullptr) {
return FAILED;
}
ge::Tensor tensor;
auto ret = node->GetAttr("value", tensor);
if (ret != ge::GRAPH_SUCCESS) {
AscendString op_name;
graphStatus ret = node->GetName(op_name);
if (ret != ge::GRAPH_SUCCESS) {
return FAILED;
}
OP_LOGE(kOpType, "Failed to get value from %s", op_name.GetString());
return FAILED;
}
uint8_t *data_addr = tensor.GetData();
value = *(reinterpret_cast<float *>(data_addr));
return SUCCESS;
}
Status DecodeBboxV2ParseParams(const std::vector<ge::OperatorPtr> &inside_nodes, ge::Operator *op_dest) {
if (op_dest == nullptr) {
OP_LOGE(kOpType, "Dest operator is nullptr.");
return FAILED;
}
std::map<std::string, std::string> scales_const_name_map;
std::map<string, ge::OperatorPtr> node_map;
for (const auto &node : inside_nodes) {
if (node == nullptr) {
OP_LOGE(kOpType, "Inner operator is nullptr.");
return FAILED;
}
ge::AscendString op_type;
ge::graphStatus ret = node.GetOpType(op_type);
if (ret != ge::GRAPH_SUCCESS) {
return FAILED;
}
ge::AscendString op_name;
ret = node.GetName(op_name);
string str_op_name;
if (op_name.GetString() != nullptr) {
str_op_name = op_name.GetString();
}
if (op_type == kBoxesDiv) {
if (node->GetInputsSize() < kRealDivInputSize) {
OP_LOGE(kOpType, "Input size of %s is invalid, which is %zu.", kBoxesDiv, node->GetInputsSize());
return FAILED;
}
ge::AscendString input_unpack_name0;
ret = node.GetInputDesc(0).GetName(input_unpack_name0);
string str_input_unpack_name0;
if (input_unpack_name0.GetString() != nullptr) {
str_input_unpack_name0 = input_unpack_name0.GetString();
}
ge::AscendString input_unpack_name1;
ret = node.GetInputDesc(1).GetName(input_unpack_name1);
string str_input_unpack_name1;
if (input_unpack_name1.GetString() != nullptr) {
str_input_unpack_name1 = input_unpack_name1.GetString();
}
if (str_input_unpack_name0.find(kBoxesUnpack) != string::npos) {
scales_const_name_map.insert({str_op_name, str_input_unpack_name1 });
}
}
node_map[str_op_name] = &node;
}
std::vector<float> scales_list = {1.0, 1.0, 1.0, 1.0};
if (scales_const_name_map.size() != kScaleSize) {
OP_LOGI(op_dest.GetName().c_str(), "Boxes doesn't need scale.");
} else {
size_t i = 0;
for (const auto &name_pair : scales_const_name_map) {
float scale_value = 1.0;
auto ret = ParseFloatFromConstNode(node_map[name_pair.second], scale_value);
if (ret != SUCCESS) {
return ret;
}
scales_list[i++] = scale_value;
}
}
op_dest->SetAttr("scales", scales_list);
return SUCCESS;
}
} // namespace
父主题: 融合规则开发