已合并
【PR】: 修复 Broadcast/Tile + Reduce 时 Reshape 输入输出定义 #4474
JacsonPile创建于 15 天前
【PR】: 修复 Broadcast/Tile + Reduce 时 Reshape 输入输出定义 #4474
已合并
共 2 个文件变更+85-26
Mcompiler/graph/optimize/autofuse/autofuse/pattern_fusion/broadcast_reduce_elimination_pass.cpp+37-11
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | 16 | ||
| 17 | + | ||
| 17 | 18 | ||
| 18 | 19 | ||
| 19 | 20 | ||
| @@ -25,6 +26,7 @@ constexpr auto kOpTypeBroadcastTo = "BroadcastTo"; | |||
| 25 | constexpr auto kOpTypeFill = "Fill"; | 26 | constexpr auto kOpTypeFill = "Fill"; |
| 26 | constexpr auto kOpTypeTile = "Tile"; | 27 | constexpr auto kOpTypeTile = "Tile"; |
| 27 | constexpr auto kOpTypeTileD = "TileD"; | 28 | constexpr auto kOpTypeTileD = "TileD"; |
| 29 | +constexpr auto kOpTypeReshape = "Reshape"; | ||
| 28 | 30 | ||
| 29 | // 归约操作类型(仅支持直接消除的操作,避免数值精度问题) | 31 | // 归约操作类型(仅支持直接消除的操作,避免数值精度问题) |
| 30 | // 注意:ReduceSum 和 ReduceProd 不支持,因为: | 32 | // 注意:ReduceSum 和 ReduceProd 不支持,因为: |
| @@ -34,7 +36,6 @@ const std::unordered_set<std::string> kReduceOpTypes = {"ReduceMax", "ReduceMin | |||
| 34 | "ReduceMaxD", "ReduceMinD", "ReduceMeanD"}; | 36 | "ReduceMaxD", "ReduceMinD", "ReduceMeanD"}; |
| 35 | 37 | ||
| 36 | constexpr auto kAttrNameAxes = "axes"; | 38 | constexpr auto kAttrNameAxes = "axes"; |
| 37 | -constexpr auto kAttrNameShape = "shape"; | ||
| 38 | constexpr auto kAttrNameMultiples = "multiples"; | 39 | constexpr auto kAttrNameMultiples = "multiples"; |
| 39 | constexpr auto kAttrNameKeepDims = "keep_dims"; | 40 | constexpr auto kAttrNameKeepDims = "keep_dims"; |
| 40 | 41 | ||
| @@ -328,22 +329,47 @@ Status ReplaceAndCleanup(const ComputeGraphPtr &graph, const NodePtr &brc_node, | |||
| 328 | // 创建 Reshape 节点 | 329 | // 创建 Reshape 节点 |
| 329 | NodePtr CreateReshapeNode(const ComputeGraphPtr &graph, const NodePtr &input, const std::vector<int64_t> &target_shape, | 330 | NodePtr CreateReshapeNode(const ComputeGraphPtr &graph, const NodePtr &input, const std::vector<int64_t> &target_shape, |
| 330 | const std::string &name) { | 331 | const std::string &name) { |
| 331 | - auto op_desc = std::make_shared<OpDesc>(name, "Reshape"); | ||
| 332 | - GE_ASSERT_NOTNULL(op_desc); | ||
| 333 | - | ||
| 334 | GeTensorDesc input_desc = input->GetOpDesc()->GetOutputDesc(0); | 332 | GeTensorDesc input_desc = input->GetOpDesc()->GetOutputDesc(0); |
| 335 | - GE_ASSERT_GRAPH_SUCCESS(op_desc->AddInputDesc(input_desc)); | ||
| 336 | - | ||
| 337 | - // 设置输出 shape | ||
| 338 | GeTensorDesc output_desc(input_desc); | 333 | GeTensorDesc output_desc(input_desc); |
| 339 | output_desc.SetShape(GeShape(target_shape)); | 334 | output_desc.SetShape(GeShape(target_shape)); |
| 340 | - GE_ASSERT_GRAPH_SUCCESS(op_desc->AddOutputDesc(output_desc)); | 335 | + output_desc.SetOriginShape(GeShape(target_shape)); |
| 341 | 336 | ||
| 342 | - // 设置 shape 属性 | 337 | + // 输入描述可能携带原始张量的符号化 shape。替换节点是真实的 Reshape, |
| 343 | - GE_ASSERT_TRUE(AttrUtils::SetListInt(op_desc, kAttrNameShape, target_shape)); | 338 | + // 因此符号化 shape 的 rank 必须与目标 shape 以及静态 GeShape 保持一致。 |
| 339 | + auto symbolic_attr = output_desc.GetOrCreateAttrsGroup<SymbolicDescAttr>(); | ||
| 340 | + GE_ASSERT_NOTNULL(symbolic_attr); | ||
| 341 | + std::vector<Expression> symbolic_shape; | ||
| 342 | + symbolic_shape.reserve(target_shape.size()); | ||
| 343 | + for (const auto dim : target_shape) { | ||
| 344 | + symbolic_shape.emplace_back(Symbol(dim)); | ||
| 345 | + } | ||
| 346 | + symbolic_attr->symbolic_tensor.MutableOriginSymbolShape().MutableDims() = symbolic_shape; | ||
| 344 | 347 | ||
| 345 | - auto reshape_node = graph->AddNode(op_desc); | 348 | + // Reshape 有两个输入:数据 x 和目标 shape。创建目标 shape 常量并连接到第二个输入。 |
| 349 | + const auto shape_tensor = ComGraphMakeShared<GeTensor>(); | ||
| 350 | + GE_ASSERT_NOTNULL(shape_tensor); | ||
| 351 | + auto &shape_desc = shape_tensor->MutableTensorDesc(); | ||
| 352 | + const GeShape shape_tensor_shape({static_cast<int64_t>(target_shape.size())}); | ||
| 353 | + shape_desc.Update(shape_tensor_shape, FORMAT_ND, DT_INT64); | ||
| 354 | + shape_desc.SetOriginShape(shape_tensor_shape); | ||
| 355 | + if (!target_shape.empty()) { | ||
| 356 | + GE_ASSERT_GRAPH_SUCCESS(shape_tensor->SetData(reinterpret_cast<const uint8_t *>(target_shape.data()), | ||
| 357 | + target_shape.size() * sizeof(int64_t))); | ||
| 358 | + } | ||
| 359 | + const auto shape_op_desc = OpDescUtils::CreateConstOpZeroCopy(shape_tensor); | ||
| 360 | + GE_ASSERT_NOTNULL(shape_op_desc); | ||
| 361 | + const auto shape_node = graph->AddNode(shape_op_desc); | ||
| 362 | + GE_ASSERT_NOTNULL(shape_node); | ||
| 363 | + | ||
| 364 | + const auto reshape_op_desc = ComGraphMakeShared<OpDesc>(name, kOpTypeReshape); | ||
| 365 | + GE_ASSERT_NOTNULL(reshape_op_desc); | ||
| 366 | + GE_ASSERT_GRAPH_SUCCESS(reshape_op_desc->AddInputDesc("x", input_desc)); | ||
| 367 | + GE_ASSERT_GRAPH_SUCCESS(reshape_op_desc->AddInputDesc("shape", shape_desc)); | ||
| 368 | + GE_ASSERT_GRAPH_SUCCESS(reshape_op_desc->AddOutputDesc("y", output_desc)); | ||
| 369 | + auto reshape_node = graph->AddNode(reshape_op_desc); | ||
| 346 | GE_ASSERT_NOTNULL(reshape_node); | 370 | GE_ASSERT_NOTNULL(reshape_node); |
| 371 | + GE_ASSERT_GRAPH_SUCCESS(GraphUtils::AddEdge(shape_node->GetOutDataAnchor(0), reshape_node->GetInDataAnchor(1)), | ||
| 372 | + "Failed to add shape edge to Reshape node %s", reshape_node->GetNamePtr()); | ||
| 347 | GE_ASSERT_GRAPH_SUCCESS(GraphUtils::AddEdge(input->GetOutDataAnchor(0), reshape_node->GetInDataAnchor(0)), | 373 | GE_ASSERT_GRAPH_SUCCESS(GraphUtils::AddEdge(input->GetOutDataAnchor(0), reshape_node->GetInDataAnchor(0)), |
| 348 | "Failed to add edge to Reshape node %s", reshape_node->GetNamePtr()); | 374 | "Failed to add edge to Reshape node %s", reshape_node->GetNamePtr()); |
| 349 | 375 | ||
| @@ -23,6 +23,9 @@ | |||
| 23 | 23 | ||
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 26 | 29 | ||
| 27 | 30 | ||
| 28 | namespace ge { | 31 | namespace ge { |
| @@ -79,6 +82,50 @@ class BroadcastReduceEliminationPassTest : public testing::Test { | |||
| 79 | relu.GetEsbTensor()->GetProducer()}; | 82 | relu.GetEsbTensor()->GetProducer()}; |
| 80 | } | 83 | } |
| 81 | 84 | ||
| 85 | + static void CheckReshapeResult(const ComputeGraphPtr &graph, const std::vector<int64_t> &expected_shape) { | ||
| 86 | + const auto reshape_node = graph->FindNode("data_reduce_max_reshape"); | ||
| 87 | + ASSERT_NE(reshape_node, nullptr); | ||
| 88 | + EXPECT_EQ(reshape_node->GetType(), "Reshape"); | ||
| 89 | + ASSERT_EQ(reshape_node->GetAllInDataAnchorsSize(), 2U); | ||
| 90 | + | ||
| 91 | + const auto shape_node = NodeUtils::GetInDataNodeByIndex(*reshape_node, 1); | ||
| 92 | + ASSERT_NE(shape_node, nullptr); | ||
| 93 | + EXPECT_EQ(shape_node->GetType(), CONSTANT); | ||
| 94 | + ConstGeTensorPtr shape_tensor; | ||
| 95 | + ASSERT_TRUE(AttrUtils::GetTensor(shape_node->GetOpDesc(), ATTR_NAME_WEIGHTS, shape_tensor)); | ||
| 96 | + ASSERT_NE(shape_tensor, nullptr); | ||
| 97 | + ASSERT_EQ(shape_tensor->GetTensorDesc().GetDataType(), DT_INT64); | ||
| 98 | + ASSERT_EQ(shape_tensor->GetTensorDesc().GetShape().GetDims(), | ||
| 99 | + std::vector<int64_t>({static_cast<int64_t>(expected_shape.size())})); | ||
| 100 | + ASSERT_EQ(shape_tensor->GetData().GetSize(), expected_shape.size() * sizeof(int64_t)); | ||
| 101 | + ASSERT_NE(shape_tensor->GetData().GetData(), nullptr); | ||
| 102 | + const auto shape_data = reinterpret_cast<const int64_t *>(shape_tensor->GetData().GetData()); | ||
| 103 | + for (size_t i = 0; i < expected_shape.size(); ++i) { | ||
| 104 | + EXPECT_EQ(shape_data[i], expected_shape[i]); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + const auto &output_desc = reshape_node->GetOpDesc()->GetOutputDesc(0); | ||
| 108 | + EXPECT_EQ(output_desc.GetShape().GetDims(), expected_shape); | ||
| 109 | + EXPECT_EQ(output_desc.GetOriginShape().GetDims(), expected_shape); | ||
| 110 | + const auto symbolic_attr = output_desc.GetAttrsGroup<SymbolicDescAttr>(); | ||
| 111 | + ASSERT_NE(symbolic_attr, nullptr); | ||
| 112 | + const auto &symbolic_dims = symbolic_attr->symbolic_tensor.GetOriginSymbolShape().GetDims(); | ||
| 113 | + ASSERT_EQ(symbolic_dims.size(), expected_shape.size()); | ||
| 114 | + for (size_t i = 0; i < expected_shape.size(); ++i) { | ||
| 115 | + EXPECT_EQ(symbolic_dims[i], Symbol(expected_shape[i])); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + const auto data_node = graph->FindNode("data"); | ||
| 119 | + const auto relu_node = graph->FindNode("relu"); | ||
| 120 | + ASSERT_NE(data_node, nullptr); | ||
| 121 | + ASSERT_NE(relu_node, nullptr); | ||
| 122 | + EXPECT_EQ(NodeUtils::GetInDataNodeByIndex(*reshape_node, 0), data_node); | ||
| 123 | + const auto reshape_out_anchor = reshape_node->GetOutDataAnchor(0); | ||
| 124 | + const auto relu_in_anchor = relu_node->GetInDataAnchor(0); | ||
| 125 | + EXPECT_EQ(reshape_out_anchor->GetPeerInDataAnchors().size(), 1); | ||
| 126 | + EXPECT_EQ(*reshape_out_anchor->GetPeerInDataAnchors().begin(), relu_in_anchor); | ||
| 127 | + } | ||
| 128 | + | ||
| 82 | std::unique_ptr<es::Graph> es_graph_; | 129 | std::unique_ptr<es::Graph> es_graph_; |
| 83 | }; | 130 | }; |
| 84 | 131 | ||
| @@ -299,21 +346,7 @@ TEST_F(BroadcastReduceEliminationPassTest, BroadcastReduceMax_EliminateWithSquee | |||
| 299 | EXPECT_EQ(graph->FindNode("broadcast"), nullptr); | 346 | EXPECT_EQ(graph->FindNode("broadcast"), nullptr); |
| 300 | EXPECT_EQ(graph->FindNode("reduce_max"), nullptr); | 347 | EXPECT_EQ(graph->FindNode("reduce_max"), nullptr); |
| 301 | 348 | ||
| 302 | - // 验证创建了 Reshape 节点 | 349 | + CheckReshapeResult(graph, {10}); |
| 303 | - auto reshape_node = graph->FindNode("data_reduce_max_reshape"); | ||
| 304 | - ASSERT_NE(reshape_node, nullptr); | ||
| 305 | - EXPECT_EQ(reshape_node->GetType(), "Reshape"); | ||
| 306 | - | ||
| 307 | - // 验证 data -> Reshape -> relu 的连接 | ||
| 308 | - auto data_node = graph->FindNode("data"); | ||
| 309 | - auto relu_node = graph->FindNode("relu"); | ||
| 310 | - ASSERT_NE(data_node, nullptr); | ||
| 311 | - ASSERT_NE(relu_node, nullptr); | ||
| 312 | - | ||
| 313 | - auto reshape_out_anchor = reshape_node->GetOutDataAnchor(0); | ||
| 314 | - auto relu_in_anchor = relu_node->GetInDataAnchor(0); | ||
| 315 | - EXPECT_EQ(reshape_out_anchor->GetPeerInDataAnchors().size(), 1); | ||
| 316 | - EXPECT_EQ(*reshape_out_anchor->GetPeerInDataAnchors().begin(), relu_in_anchor); | ||
| 317 | } | 350 | } |
| 318 | 351 | ||
| 319 | // ========== Fill + Reduce 测试用例 ========== | 352 | // ========== Fill + Reduce 测试用例 ========== |