---
title: 多对多场景开发注意点
description: "多对多场景开发注意点包括多对多场景开发流程、设置融合结果和内部小算子所需的信息处理。"
url: https://www.hiascend.com/document/detail/zh/TensorFlowCommunity/latest/Convergence/tfscopedevg/atlastfscopedev_11_0021.html
sourcePath: /source/zh/TensorFlowCommunity/910/Convergence/tfscopedevg/atlastfscopedev_11_0021.html
indexId: 4cce8481c6f0d1f77fbf6ee1570e6f9ebbb6a2fe21dad0a7822e563a9809654a87
---
# 多对多场景开发注意点

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

#### 简介

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


#### 实现流程

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

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


#### 设置融合结果

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

主要过程包括：

1. 设置融合结果的名字、描述，以及与外部的输入、输出，和多对一场景相同；
2. 设置融合算子类型为特定类型  kScopeToMultiNodes
，表明融合成多个算子组合；
  1 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);
```


  -     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
```
