多对多场景开发注意点
多对多场景开发注意点包括多对多场景开发流程、设置融合结果和内部小算子所需的信息处理。
简介
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
父主题: 融合规则开发