昇腾社区首页
中文
注册

多对多场景开发注意点

多对多场景开发注意点包括多对多场景开发流程、设置融合结果和内部小算子所需的信息处理。

简介

Scope多对多融合场景,即把Scope内的多个小算子融合为一系列小算子组合。

实现流程

实现流程请参考Scope多对多融合场景,不同点在于:

  1. 在融合结果设置函数GenerateFusionResult中需要设置内部小算子组合的连接关系。
  2. 内部小算子后面将不再走插件解析, 因此小算子需用户自行构造成IR表示,并设置好所需属性等参数。

设置融合结果

这里为了逻辑的正确,示例的代码是把DecodeBboxV2算子输入输出前均加上Identity算子,以此来达到添加多个算子的目的,实际操作过程中可根据具体的算子功能需求添加小算子组合。

主要过程包括:

  1. 设置融合结果的名字、描述,以及与外部的输入、输出,和多对一场景相同;
  2. 设置融合算子类型为特定类型kScopeToMultiNodes,表明融合成多个算子组合;
    fusion_rlt->SetType(kScopeToMultiNodes);  // 设置特定类型,表明融合成多个算子组合
  3. 通过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);
    完整代码示例为:
    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