Closed
refactor: finalize native inference migration after July quality fixes #12383
raoliang_sacAdd on 21 days agoClosed at 20 days ago
refactor: finalize native inference migration after July quality fixes #12383
Closed
Total: 42 files changed+835-378
| @@ -57,23 +57,29 @@ | |||
| 57 | <tr> | 57 | <tr> |
| 58 | <td>features</td> | 58 | <td>features</td> |
| 59 | <td>输入</td> | 59 | <td>输入</td> |
| 60 | - <td>作为输入传递给对应Relu6操作的特征,或其输出,使用任一者产生相同结果,公式中的features。其shape须与gradients一致,或为标量[1]。</td> | 60 | + <td>作为输入传递给对应Relu6操作的特征,或其输出,使用任一者产生相同结果,公式中的features。与gradients的shape关系见产品差异说明。</td> |
| 61 | <td>FLOAT16、FLOAT、BFLOAT16</td> | 61 | <td>FLOAT16、FLOAT、BFLOAT16</td> |
| 62 | <td>ND</td> | 62 | <td>ND</td> |
| 63 | </tr> | 63 | </tr> |
| 64 | <tr> | 64 | <tr> |
| 65 | <td>backprops</td> | 65 | <td>backprops</td> |
| 66 | <td>输出</td> | 66 | <td>输出</td> |
| 67 | - <td>公式中的输出张量,shape与gradients一致。</td> | 67 | + <td>公式中的输出张量,shape由输入shape按各产品的广播约束确定,见产品差异说明。</td> |
| 68 | <td>FLOAT16、FLOAT、BFLOAT16</td> | 68 | <td>FLOAT16、FLOAT、BFLOAT16</td> |
| 69 | <td>ND</td> | 69 | <td>ND</td> |
| 70 | </tr> | 70 | </tr> |
| 71 | </tbody></table> | 71 | </tbody></table> |
| 72 | 72 | ||
| 73 | +### 产品差异说明 | ||
| 74 | + | ||
| 75 | +| 产品 | 输入shape约束 | 输出shape | | ||
| 76 | +| --- | --- | --- | | ||
| 77 | +| <term>Ascend 950PR&950DT系列产品</term> | gradients与features支持双向广播,各自rank不超过8;GE图模式支持满足广播关系的空Tensor | 两个输入广播后的shape;空Tensor的输出元素数为0 | | ||
| 78 | +| <term>Atlas A3系列产品</term>、<term>Atlas A2系列产品</term> | features的shape须与gradients一致,或为[1] | 与gradients一致 | | ||
| 79 | + | ||
| 73 | ## 约束说明 | 80 | ## 约束说明 |
| 74 | 81 | ||
| 75 | - gradients、features、backprops的数据类型须保持一致,不支持混合数据类型。 | 82 | - gradients、features、backprops的数据类型须保持一致,不支持混合数据类型。 |
| 76 | -- 不支持广播:features的shape须与gradients完全一致,或为标量[1]。 | ||
| 77 | - 支持动态shape与动态rank。 | 83 | - 支持动态shape与动态rank。 |
| 78 | 84 | ||
| 79 | ## 实现方案 | 85 | ## 实现方案 |
| @@ -83,6 +89,7 @@ | |||
| 83 | | 计算图原型 | `op_graph/relu6_grad_proto.h` | `REG_OP(Relu6Grad)`,二输入一输出 | | 89 | | 计算图原型 | `op_graph/relu6_grad_proto.h` | `REG_OP(Relu6Grad)`,二输入一输出 | |
| 84 | | 算子定义 | `op_host/relu6_grad_def.cpp` | `OpDef::AddConfig("ascend950", ...)` | | 90 | | 算子定义 | `op_host/relu6_grad_def.cpp` | `OpDef::AddConfig("ascend950", ...)` | |
| 85 | | InferShape | `op_host/relu6_grad_infershape.cpp` | 复用`Ops::Base::InferShape4Broadcast` | | 91 | | InferShape | `op_host/relu6_grad_infershape.cpp` | 复用`Ops::Base::InferShape4Broadcast` | |
| 92 | +| 图模式InferDataType | `op_graph/relu6_grad_graph_infer.cpp` | 校验两个输入dtype一致并推导输出dtype | | ||
| 86 | | Tiling | `op_host/arch35/relu6_grad_tiling_arch35.{h,cpp}` | 按dtype分支调用`Ops::Base::BroadcastBaseTiling<OpDag>` | | 93 | | Tiling | `op_host/arch35/relu6_grad_tiling_arch35.{h,cpp}` | 按dtype分支调用`Ops::Base::BroadcastBaseTiling<OpDag>` | |
| 87 | | DAG | `op_kernel/arch35/relu6_grad_dag.h` | fp32通路原生计算;fp16/bf16通路提升fp32中间精度 | | 94 | | DAG | `op_kernel/arch35/relu6_grad_dag.h` | fp32通路原生计算;fp16/bf16通路提升fp32中间精度 | |
| 88 | | Struct | `op_kernel/arch35/relu6_grad_struct.h` | `BRC_TEMP_SCH_MODE_KEY_DECL/SEL` | | 95 | | Struct | `op_kernel/arch35/relu6_grad_struct.h` | `BRC_TEMP_SCH_MODE_KEY_DECL/SEL` | |
| @@ -90,7 +97,7 @@ | |||
| 90 | 97 | ||
| 91 | ### fp32通路 | 98 | ### fp32通路 |
| 92 | 99 | ||
| 93 | -```Cpp | 100 | +```text |
| 94 | In0(dy) ─CopyInBrc─+ | 101 | In0(dy) ─CopyInBrc─+ |
| 95 | +──────────────────────────────+ | 102 | +──────────────────────────────+ |
| 96 | | | 103 | | |
| @@ -102,13 +109,13 @@ In1(x) ─CopyInBrc─+ | | |||
| 102 | Const(0,fp32) ─Duplicate─ Zero ─────────────── ┴ Select(mask, dy, Zero) ─ CopyOut ─ Out0 | 109 | Const(0,fp32) ─Duplicate─ Zero ─────────────── ┴ Select(mask, dy, Zero) ─ CopyOut ─ Out0 |
| 103 | ``` | 110 | ``` |
| 104 | 111 | ||
| 105 | -```Cpp | ||
| 106 | `Vec::Compare<u8, fp32, GT/LT>`输出位掩码,`Vec::And<u8>`合成`(0 < x < 6)` | 112 | `Vec::Compare<u8, fp32, GT/LT>`输出位掩码,`Vec::And<u8>`合成`(0 < x < 6)` |
| 107 | 区间mask;`Vec::Select<u8, fp32, TENSOR_TENSOR>`按`mask ? dy : 0`逐元素 | 113 | 区间mask;`Vec::Select<u8, fp32, TENSOR_TENSOR>`按`mask ? dy : 0`逐元素 |
| 108 | 选择,避免分支,硬件原生vsel实现。 | 114 | 选择,避免分支,硬件原生vsel实现。 |
| 109 | 115 | ||
| 110 | ### fp16 / bf16通路 | 116 | ### fp16 / bf16通路 |
| 111 | 117 | ||
| 118 | +```text | ||
| 112 | In0/In1 ─CopyInBrc─ Cast(->fp32) ─+ | 119 | In0/In1 ─CopyInBrc─ Cast(->fp32) ─+ |
| 113 | +─ Compare ── And ─ Select ── Cast(->T,RINT) ─ CopyOut ─ Out0 | 120 | +─ Compare ── And ─ Select ── Cast(->T,RINT) ─ CopyOut ─ Out0 |
| 114 | Const(0,fp32) ── Zero ─+ | 121 | Const(0,fp32) ── Zero ─+ |
| @@ -126,4 +133,4 @@ In0/In1 ─CopyInBrc─ Cast(->fp32) ─+ | |||
| 126 | 133 | ||
| 127 | | 调用方式 | 样例代码 | 说明 | | 134 | | 调用方式 | 样例代码 | 说明 | |
| 128 | | ---------------- | --------------------------- |-----------------------------------------------| | 135 | | ---------------- | --------------------------- |-----------------------------------------------| |
| 129 | -| 图模式 | [test_geir_relu6_grad.cpp](examples/arch35/test_geir_relu6_grad.cpp) | 通过[算子IR](op_graph/relu6_grad_proto.h)构图方式调用Relu6Grad算子。 | | 136 | +| GE图模式 | [test_geir_relu6_grad.cpp](examples/arch35/test_geir_relu6_grad.cpp) | 通过[算子IR](op_graph/relu6_grad_proto.h)构图方式调用Relu6Grad算子。 | |
| @@ -0,0 +1,28 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 2 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 3 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 4 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 6 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 7 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 8 | + | ||
| 9 | +cmake_minimum_required(VERSION 3.18) | ||
| 10 | +project(GE_IR_EXAMPLE LANGUAGES CXX) | ||
| 11 | + | ||
| 12 | +set(ASCEND_PATH "$ENV{ASCEND_HOME_PATH}") | ||
| 13 | +if(NOT ASCEND_PATH) | ||
| 14 | + message(FATAL_ERROR "Source the CANN set_env.sh before configuring this example.") | ||
| 15 | +endif() | ||
| 16 | +find_library(GRAPH_LIBRARY NAMES libgraph.so PATHS "${ASCEND_PATH}/lib64" NO_DEFAULT_PATH REQUIRED) | ||
| 17 | +find_library(GE_RUNNER_LIBRARY NAMES libge_runner.so PATHS "${ASCEND_PATH}/lib64" NO_DEFAULT_PATH REQUIRED) | ||
| 18 | +find_library(GRAPH_BASE_LIBRARY NAMES libgraph_base.so PATHS "${ASCEND_PATH}/lib64" NO_DEFAULT_PATH REQUIRED) | ||
| 19 | +find_library(GE_COMPILER_LIBRARY NAMES libge_compiler.so PATHS "${ASCEND_PATH}/lib64" NO_DEFAULT_PATH REQUIRED) | ||
| 20 | + | ||
| 21 | +add_executable(test_geir_relu6_grad test_geir_relu6_grad.cpp) | ||
| 22 | +target_include_directories(test_geir_relu6_grad PRIVATE | ||
| 23 | + "${ASCEND_PATH}/include/graph" | ||
| 24 | + "${ASCEND_PATH}/include/ge" | ||
| 25 | + "${ASCEND_PATH}/opp/built-in/op_proto/inc" | ||
| 26 | + "${ASCEND_PATH}/include") | ||
| 27 | +target_link_libraries(test_geir_relu6_grad PRIVATE | ||
| 28 | + ${GRAPH_LIBRARY} ${GE_RUNNER_LIBRARY} ${GRAPH_BASE_LIBRARY} ${GE_COMPILER_LIBRARY}) | ||
| @@ -35,6 +35,8 @@ | |||
| 35 | #include <map> | 35 | #include <map> |
| 36 | #include <cmath> | 36 | #include <cmath> |
| 37 | #include <limits> | 37 | #include <limits> |
| 38 | +#include <memory> | ||
| 39 | +#include <new> | ||
| 38 | #include "assert.h" | 40 | #include "assert.h" |
| 39 | 41 | ||
| 40 | #include "graph.h" | 42 | #include "graph.h" |
| @@ -159,23 +161,41 @@ static int32_t GenConstTensor(const vector<int64_t>& shape, DataType dtype, doub | |||
| 159 | size_t bytes = numel * GetDataTypeSize(dtype); | 161 | size_t bytes = numel * GetDataTypeSize(dtype); |
| 160 | 162 | ||
| 161 | if (dtype == ge::DT_FLOAT) { | 163 | if (dtype == ge::DT_FLOAT) { |
| 162 | - float* p = new (std::nothrow) float[numel]; | 164 | + std::unique_ptr<float[]> p(new (std::nothrow) float[numel]); |
| 165 | + if (!p) { | ||
| 166 | + return FAILED; | ||
| 167 | + } | ||
| 163 | float v = (float)value; | 168 | float v = (float)value; |
| 164 | for (size_t i = 0; i < numel; ++i) | 169 | for (size_t i = 0; i < numel; ++i) |
| 165 | p[i] = v; | 170 | p[i] = v; |
| 166 | - tensor = Tensor(desc, (uint8_t*)p, bytes); | 171 | + tensor = Tensor(desc); |
| 172 | + if (tensor.SetData(reinterpret_cast<const uint8_t*>(p.get()), bytes) != ge::GRAPH_SUCCESS) { | ||
| 173 | + return FAILED; | ||
| 174 | + } | ||
| 167 | } else if (dtype == ge::DT_FLOAT16) { | 175 | } else if (dtype == ge::DT_FLOAT16) { |
| 168 | - uint16_t* p = new (std::nothrow) uint16_t[numel]; | 176 | + std::unique_ptr<uint16_t[]> p(new (std::nothrow) uint16_t[numel]); |
| 177 | + if (!p) { | ||
| 178 | + return FAILED; | ||
| 179 | + } | ||
| 169 | uint16_t v = FloatToHalfBits((float)value); | 180 | uint16_t v = FloatToHalfBits((float)value); |
| 170 | for (size_t i = 0; i < numel; ++i) | 181 | for (size_t i = 0; i < numel; ++i) |
| 171 | p[i] = v; | 182 | p[i] = v; |
| 172 | - tensor = Tensor(desc, (uint8_t*)p, bytes); | 183 | + tensor = Tensor(desc); |
| 184 | + if (tensor.SetData(reinterpret_cast<const uint8_t*>(p.get()), bytes) != ge::GRAPH_SUCCESS) { | ||
| 185 | + return FAILED; | ||
| 186 | + } | ||
| 173 | } else if (dtype == ge::DT_BF16) { | 187 | } else if (dtype == ge::DT_BF16) { |
| 174 | - uint16_t* p = new (std::nothrow) uint16_t[numel]; | 188 | + std::unique_ptr<uint16_t[]> p(new (std::nothrow) uint16_t[numel]); |
| 189 | + if (!p) { | ||
| 190 | + return FAILED; | ||
| 191 | + } | ||
| 175 | uint16_t v = FloatToBf16Bits((float)value); | 192 | uint16_t v = FloatToBf16Bits((float)value); |
| 176 | for (size_t i = 0; i < numel; ++i) | 193 | for (size_t i = 0; i < numel; ++i) |
| 177 | p[i] = v; | 194 | p[i] = v; |
| 178 | - tensor = Tensor(desc, (uint8_t*)p, bytes); | 195 | + tensor = Tensor(desc); |
| 196 | + if (tensor.SetData(reinterpret_cast<const uint8_t*>(p.get()), bytes) != ge::GRAPH_SUCCESS) { | ||
| 197 | + return FAILED; | ||
| 198 | + } | ||
| 179 | } else { | 199 | } else { |
| 180 | return FAILED; | 200 | return FAILED; |
| 181 | } | 201 | } |
| @@ -200,6 +220,7 @@ static int BuildRelu6GradGraph(Graph& graph, std::vector<ge::Tensor>& inputData, | |||
| 200 | if (GenConstTensor(sDy, dtype, dyVal, t, d) != SUCCESS) | 220 | if (GenConstTensor(sDy, dtype, dyVal, t, d) != SUCCESS) |
| 201 | return FAILED; | 221 | return FAILED; |
| 202 | ph.update_input_desc_x(d); | 222 | ph.update_input_desc_x(d); |
| 223 | + ph.update_output_desc_y(d); | ||
| 203 | inputData.push_back(t); | 224 | inputData.push_back(t); |
| 204 | graph.AddOp(ph); | 225 | graph.AddOp(ph); |
| 205 | op.set_input_gradients(ph); | 226 | op.set_input_gradients(ph); |
| @@ -207,7 +228,7 @@ static int BuildRelu6GradGraph(Graph& graph, std::vector<ge::Tensor>& inputData, | |||
| 207 | } | 228 | } |
| 208 | // Input features (x) | 229 | // Input features (x) |
| 209 | { | 230 | { |
| 210 | - auto ph = ge::op::Data("phX_" + caseTag).set_attr_index(0); | 231 | + auto ph = ge::op::Data("phX_" + caseTag).set_attr_index(1); |
| 211 | TensorDesc d(ge::Shape(sX), FORMAT_ND, dtype); | 232 | TensorDesc d(ge::Shape(sX), FORMAT_ND, dtype); |
| 212 | d.SetPlacement(ge::kPlacementHost); | 233 | d.SetPlacement(ge::kPlacementHost); |
| 213 | d.SetFormat(FORMAT_ND); | 234 | d.SetFormat(FORMAT_ND); |
| @@ -215,6 +236,7 @@ static int BuildRelu6GradGraph(Graph& graph, std::vector<ge::Tensor>& inputData, | |||
| 215 | if (GenConstTensor(sX, dtype, xVal, t, d) != SUCCESS) | 236 | if (GenConstTensor(sX, dtype, xVal, t, d) != SUCCESS) |
| 216 | return FAILED; | 237 | return FAILED; |
| 217 | ph.update_input_desc_x(d); | 238 | ph.update_input_desc_x(d); |
| 239 | + ph.update_output_desc_y(d); | ||
| 218 | inputData.push_back(t); | 240 | inputData.push_back(t); |
| 219 | graph.AddOp(ph); | 241 | graph.AddOp(ph); |
| 220 | op.set_input_features(ph); | 242 | op.set_input_features(ph); |
| @@ -273,7 +295,7 @@ static int RunOneCase(const CaseSpec& spec) | |||
| 273 | graph.SetInputs(inputs).SetOutputs(outputs); | 295 | graph.SetInputs(inputs).SetOutputs(outputs); |
| 274 | 296 | ||
| 275 | std::map<AscendString, AscendString> buildOpts; | 297 | std::map<AscendString, AscendString> buildOpts; |
| 276 | - ge::Session* session = new Session(buildOpts); | 298 | + ge::Session* session = new (std::nothrow) Session(buildOpts); |
| 277 | if (!session) | 299 | if (!session) |
| 278 | return FAILED; | 300 | return FAILED; |
| 279 | 301 | ||
| @@ -293,10 +315,23 @@ static int RunOneCase(const CaseSpec& spec) | |||
| 293 | return FAILED; | 315 | return FAILED; |
| 294 | } | 316 | } |
| 295 | 317 | ||
| 318 | + if (output.size() != 1) { | ||
| 319 | + printf("Expected exactly one output, got %zu\n", output.size()); | ||
| 320 | + delete session; | ||
| 321 | + return FAILED; | ||
| 322 | + } | ||
| 296 | int failCount = 0; | 323 | int failCount = 0; |
| 297 | for (size_t i = 0; i < output.size(); ++i) { | 324 | for (size_t i = 0; i < output.size(); ++i) { |
| 298 | - int64_t numel = output[i].GetTensorDesc().GetShape().GetShapeSize(); | 325 | + const auto& desc = output[i].GetTensorDesc(); |
| 326 | + int64_t numel = desc.GetShape().GetShapeSize(); | ||
| 299 | uint8_t* raw = output[i].GetData(); | 327 | uint8_t* raw = output[i].GetData(); |
| 328 | + if (desc.GetShape().GetDims() != spec.outShape || desc.GetDataType() != spec.dtype || numel < 0 || | ||
| 329 | + output[i].GetSize() != static_cast<size_t>(numel) * GetDataTypeSize(spec.dtype) || | ||
| 330 | + (numel != 0 && raw == nullptr)) { | ||
| 331 | + printf("Output shape, dtype or buffer mismatch\n"); | ||
| 332 | + delete session; | ||
| 333 | + return FAILED; | ||
| 334 | + } | ||
| 300 | for (int64_t j = 0; j < numel; ++j) { | 335 | for (int64_t j = 0; j < numel; ++j) { |
| 301 | double got; | 336 | double got; |
| 302 | if (spec.dtype == ge::DT_FLOAT) { | 337 | if (spec.dtype == ge::DT_FLOAT) { |
| @@ -336,12 +371,8 @@ static int RunOneCase(const CaseSpec& spec) | |||
| 336 | 371 | ||
| 337 | int main(int argc, char* argv[]) | 372 | int main(int argc, char* argv[]) |
| 338 | { | 373 | { |
| 339 | - (void)argc; | 374 | + if (argc > 2) { |
| 340 | - (void)argv; | 375 | + printf("Usage: %s [case_tag|--list]\n", argv[0]); |
| 341 | - | ||
| 342 | - std::map<AscendString, AscendString> globalOpts = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 343 | - if (ge::GEInitialize(globalOpts) != SUCCESS) { | ||
| 344 | - printf("GEInitialize failed\n"); | ||
| 345 | return FAILED; | 376 | return FAILED; |
| 346 | } | 377 | } |
| 347 | 378 | ||
| @@ -377,6 +408,16 @@ int main(int argc, char* argv[]) | |||
| 377 | {"bf16_boundary_x_eq_0", ge::DT_BF16, 1.0, 0.0, {4}, {4}, {4}, exact(0.0, 2e-2)}, | 408 | {"bf16_boundary_x_eq_0", ge::DT_BF16, 1.0, 0.0, {4}, {4}, {4}, exact(0.0, 2e-2)}, |
| 378 | {"bf16_boundary_x_eq_6", ge::DT_BF16, 1.0, 6.0, {4}, {4}, {4}, exact(0.0, 2e-2)}, | 409 | {"bf16_boundary_x_eq_6", ge::DT_BF16, 1.0, 6.0, {4}, {4}, {4}, exact(0.0, 2e-2)}, |
| 379 | {"bf16_x_pos_inf", ge::DT_BF16, 1.0, kInf, {4}, {4}, {4}, exact(0.0, 2e-2)}, | 410 | {"bf16_x_pos_inf", ge::DT_BF16, 1.0, kInf, {4}, {4}, {4}, exact(0.0, 2e-2)}, |
| 411 | + // --- output shape must be inferred from both broadcast inputs --- | ||
| 412 | + {"fp16_broadcast", ge::DT_FLOAT16, 1.5, 2.0, {2, 1, 5}, {1, 3, 1}, {2, 3, 5}, exact(1.5, 5e-3)}, | ||
| 413 | + {"fp32_broadcast_rank8", | ||
| 414 | + ge::DT_FLOAT, | ||
| 415 | + 2.5, | ||
| 416 | + 3.0, | ||
| 417 | + {2, 1, 2, 1, 2, 1, 2, 1}, | ||
| 418 | + {1, 2, 1, 2, 1, 2, 1, 2}, | ||
| 419 | + {2, 2, 2, 2, 2, 2, 2, 2}, | ||
| 420 | + exact(2.5, 1e-5)}, | ||
| 380 | // the case passes iff the graph compiles and runs (expect is unused for numel == 0). --- | 421 | // the case passes iff the graph compiles and runs (expect is unused for numel == 0). --- |
| 381 | {"fp32_empty_1d", ge::DT_FLOAT, 2.5, 3.0, {0}, {0}, {0}, exact(0.0, 1e-5)}, | 422 | {"fp32_empty_1d", ge::DT_FLOAT, 2.5, 3.0, {0}, {0}, {0}, exact(0.0, 1e-5)}, |
| 382 | {"fp32_empty_2d", ge::DT_FLOAT, 2.5, 3.0, {2, 0}, {2, 0}, {2, 0}, exact(0.0, 1e-5)}, | 423 | {"fp32_empty_2d", ge::DT_FLOAT, 2.5, 3.0, {2, 0}, {2, 0}, {2, 0}, exact(0.0, 1e-5)}, |
| @@ -386,6 +427,32 @@ int main(int argc, char* argv[]) | |||
| 386 | {"bf16_empty_2d", ge::DT_BF16, 1.0, 2.0, {0, 3}, {0, 3}, {0, 3}, exact(0.0, 2e-2)}, | 427 | {"bf16_empty_2d", ge::DT_BF16, 1.0, 2.0, {0, 3}, {0, 3}, {0, 3}, exact(0.0, 2e-2)}, |
| 387 | }; | 428 | }; |
| 388 | 429 | ||
| 430 | + if (argc == 2 && string(argv[1]) == "--list") { | ||
| 431 | + for (const auto& c : cases) { | ||
| 432 | + std::cout << c.tag << std::endl; | ||
| 433 | + } | ||
| 434 | + return SUCCESS; | ||
| 435 | + } | ||
| 436 | + if (argc == 2) { | ||
| 437 | + vector<CaseSpec> selected; | ||
| 438 | + for (const auto& c : cases) { | ||
| 439 | + if (c.tag == argv[1]) { | ||
| 440 | + selected.push_back(c); | ||
| 441 | + } | ||
| 442 | + } | ||
| 443 | + if (selected.empty()) { | ||
| 444 | + printf("Unknown case: %s\n", argv[1]); | ||
| 445 | + return FAILED; | ||
| 446 | + } | ||
| 447 | + cases = std::move(selected); | ||
| 448 | + } | ||
| 449 | + | ||
| 450 | + std::map<AscendString, AscendString> globalOpts = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}}; | ||
| 451 | + if (ge::GEInitialize(globalOpts) != SUCCESS) { | ||
| 452 | + printf("GEInitialize failed\n"); | ||
| 453 | + return FAILED; | ||
| 454 | + } | ||
| 455 | + | ||
| 389 | int allFail = 0; | 456 | int allFail = 0; |
| 390 | for (const auto& c : cases) { | 457 | for (const auto& c : cases) { |
| 391 | if (RunOneCase(c) != SUCCESS) { | 458 | if (RunOneCase(c) != SUCCESS) { |
| @@ -0,0 +1,28 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include "log/log.h" | ||
| 12 | +#include "register/op_impl_registry.h" | ||
| 13 | + | ||
| 14 | +namespace ops { | ||
| 15 | +ge::graphStatus InferDataType4Relu6Grad(gert::InferDataTypeContext* context) | ||
| 16 | +{ | ||
| 17 | + if (context == nullptr) { | ||
| 18 | + return ge::GRAPH_FAILED; | ||
| 19 | + } | ||
| 20 | + const auto dtype = context->GetInputDataType(0); | ||
| 21 | + OP_CHECK_IF(dtype != context->GetInputDataType(1), | ||
| 22 | + OP_LOGE(context->GetNodeName(), "gradients and features must have the same dtype."), | ||
| 23 | + return ge::GRAPH_FAILED); | ||
| 24 | + return context->SetOutputDataType(0, dtype); | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +IMPL_OP(Relu6Grad).InferDataType(InferDataType4Relu6Grad); | ||
| 28 | +} // namespace ops | ||
| @@ -24,25 +24,29 @@ namespace ge { | |||
| 24 | * @brief Gradient of ReLU6. Strict open-interval semantics: | 24 | * @brief Gradient of ReLU6. Strict open-interval semantics: |
| 25 | * dx[i] = gradients[i] if 0 < features[i] < 6 | 25 | * dx[i] = gradients[i] if 0 < features[i] < 6 |
| 26 | * dx[i] = 0 otherwise (including features[i] == 0 and 6) | 26 | * dx[i] = 0 otherwise (including features[i] == 0 and 6) |
| 27 | -* Broadcasting is NOT supported: features must have the same shape as | 27 | +* On <term>Ascend 950PR&950DT系列产品</term>, gradients and features support |
| 28 | -* gradients, or be a scalar ([1]). backprops always has the same shape | 28 | +* bidirectional broadcasting: dimensions are aligned from the right and |
| 29 | -* as gradients. | 29 | +* each pair must be equal or contain a dimension of size 1. |
| 30 | +* On <term>Atlas A3系列产品</term> and <term>Atlas A2系列产品</term>, features | ||
| 31 | +* must have the same shape as gradients or have shape [1]. | ||
| 30 | 32 | ||
| 31 | * @par Inputs: | 33 | * @par Inputs: |
| 32 | * Two inputs, including: | 34 | * Two inputs, including: |
| 33 | * @li gradients: A ND tensor. Upstream gradient (dy). Must be one of the | 35 | * @li gradients: A ND tensor. Upstream gradient (dy). Must be one of the |
| 34 | * following types: float16, float32, bfloat16. | 36 | * following types: float16, float32, bfloat16. |
| 35 | -* @li features: A ND tensor. Forward input x of ReLU6 (NOT the forward | 37 | +* @li features: A ND tensor. Forward input or output of ReLU6; either gives |
| 36 | -* output). Must have the same dtype as gradients; shape must be the same | 38 | +* the same interval mask. Must have the same dtype as gradients and a |
| 37 | -* as gradients, or be a scalar ([1]). \n | 39 | +* shape satisfying the product-specific constraints above. \n |
| 38 | 40 | ||
| 39 | * @par Outputs: | 41 | * @par Outputs: |
| 40 | * backprops: A ND tensor. Has the same dtype as gradients; shape is the | 42 | * backprops: A ND tensor. Has the same dtype as gradients; shape is the |
| 41 | -* same as gradients. \n | 43 | +* broadcast shape under the product-specific constraints above. \n |
| 42 | 44 | ||
| 43 | * @par Third-party framework compatibility | 45 | * @par Third-party framework compatibility |
| 44 | -* Compatible with the TensorFlow operator Relu6Grad and PyTorch | 46 | +* For finite gradients, matches TensorFlow Relu6Grad and PyTorch |
| 45 | -* hardtanh_backward(min=0, max=6) at boundary x in {0, 6} returning 0. | 47 | +* hardtanh_backward(min=0, max=6) at features in {0, 6}, returning 0. Unlike multiplication by a zero mask, |
| 48 | +* the selection semantics above also return 0 for NaN/Inf gradients | ||
| 49 | +* outside the open interval. | ||
| 46 | */ | 50 | */ |
| 47 | #ifndef OPS_PROTO_DEF_RELU6GRAD | 51 | #ifndef OPS_PROTO_DEF_RELU6GRAD |
| 48 | #define OPS_PROTO_DEF_RELU6GRAD | 52 | #define OPS_PROTO_DEF_RELU6GRAD |
| @@ -18,7 +18,7 @@ | |||
| 18 | #include "tiling/platform/platform_ascendc.h" | 18 | #include "tiling/platform/platform_ascendc.h" |
| 19 | #include "log/log.h" | 19 | #include "log/log.h" |
| 20 | #include "register/tilingdata_base.h" | 20 | #include "register/tilingdata_base.h" |
| 21 | -#include "atvoss/elewise/elewise_tiling.h" | 21 | +#include "atvoss/broadcast/broadcast_tiling.h" |
| 22 | #include "activation/relu6_grad/op_kernel/arch35/relu6_grad_dag.h" | 22 | #include "activation/relu6_grad/op_kernel/arch35/relu6_grad_dag.h" |
| 23 | #include "activation/relu6_grad/op_kernel/arch35/relu6_grad_struct.h" | 23 | #include "activation/relu6_grad/op_kernel/arch35/relu6_grad_struct.h" |
| 24 | #include "op_host/tiling_templates_registry.h" | 24 | #include "op_host/tiling_templates_registry.h" |
| @@ -138,12 +138,22 @@ static ge::graphStatus Tiling4Relu6Grad(gert::TilingContext* tilingContextGen) | |||
| 138 | return tiling.DoTiling(); | 138 | return tiling.DoTiling(); |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | -ge::graphStatus TilingPrepareForRelu6Grad([[maybe_unused]] gert::TilingParseContext* context) | 141 | +ge::graphStatus TilingPrepareForRelu6Grad(gert::TilingParseContext* context) |
| 142 | { | 142 | { |
| 143 | + auto compileInfo = context->GetCompiledInfo<Ops::Base::BroadcastCompileInfo>(); | ||
| 144 | + OP_CHECK_NULL_WITH_CONTEXT(context, compileInfo); | ||
| 145 | + auto platformInfo = context->GetPlatformInfo(); | ||
| 146 | + OP_CHECK_NULL_WITH_CONTEXT(context, platformInfo); | ||
| 147 | + | ||
| 148 | + auto ascendcPlatform = platform_ascendc::PlatformAscendC(platformInfo); | ||
| 149 | + compileInfo->coreNum = ascendcPlatform.GetCoreNumAiv(); | ||
| 150 | + ascendcPlatform.GetCoreMemSize(platform_ascendc::CoreMemType::UB, compileInfo->ubSize); | ||
| 143 | return ge::GRAPH_SUCCESS; | 151 | return ge::GRAPH_SUCCESS; |
| 144 | } | 152 | } |
| 145 | 153 | ||
| 146 | -IMPL_OP_OPTILING(Relu6Grad).Tiling(Tiling4Relu6Grad).TilingParse<ElewiseCompileInfo>(TilingPrepareForRelu6Grad); | 154 | +IMPL_OP_OPTILING(Relu6Grad) |
| 155 | + .Tiling(Tiling4Relu6Grad) | ||
| 156 | + .TilingParse<Ops::Base::BroadcastCompileInfo>(TilingPrepareForRelu6Grad); | ||
| 147 | 157 | ||
| 148 | REGISTER_OPS_TILING_TEMPLATE(Relu6Grad, Relu6GradTiling, RELU6_GRAD_COMMON_TILING_PRIORITY); | 158 | REGISTER_OPS_TILING_TEMPLATE(Relu6Grad, Relu6GradTiling, RELU6_GRAD_COMMON_TILING_PRIORITY); |
| 149 | } // namespace optiling | 159 | } // namespace optiling |
| @@ -25,7 +25,7 @@ using namespace Ops::Base; | |||
| 25 | 25 | ||
| 26 | class Relu6GradTiling : public TilingBaseClass { | 26 | class Relu6GradTiling : public TilingBaseClass { |
| 27 | public: | 27 | public: |
| 28 | - explicit Relu6GradTiling(gert::TilingContext* context) : TilingBaseClass(context){}; | 28 | + explicit Relu6GradTiling(gert::TilingContext* context) : TilingBaseClass(context) {} |
| 29 | 29 | ||
| 30 | protected: | 30 | protected: |
| 31 | bool IsCapable() override; | 31 | bool IsCapable() override; |
| @@ -25,18 +25,6 @@ static ge::graphStatus InferShape4Relu6Grad(gert::InferShapeContext* context) | |||
| 25 | return Ops::Base::InferShape4Broadcast(context); | 25 | return Ops::Base::InferShape4Broadcast(context); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | -static ge::graphStatus InferDataType4Relu6Grad(gert::InferDataTypeContext* context) | 28 | +IMPL_OP_INFERSHAPE(Relu6Grad).InferShape(InferShape4Relu6Grad); |
| 29 | -{ | ||
| 30 | - if (context == nullptr) { | ||
| 31 | - return ge::GRAPH_FAILED; | ||
| 32 | - } | ||
| 33 | - OP_LOGD(context->GetNodeName(), "InferDataType4Relu6Grad enter"); | ||
| 34 | - // backprops 与 gradients 同类型 | ||
| 35 | - context->SetOutputDataType(0, context->GetInputDataType(0)); | ||
| 36 | - OP_LOGD(context->GetNodeName(), "InferDataType4Relu6Grad end"); | ||
| 37 | - return ge::GRAPH_SUCCESS; | ||
| 38 | -} | ||
| 39 | - | ||
| 40 | -IMPL_OP_INFERSHAPE(Relu6Grad).InferShape(InferShape4Relu6Grad).InferDataType(InferDataType4Relu6Grad); | ||
| 41 | 29 | ||
| 42 | } // namespace ops | 30 | } // namespace ops |
| @@ -12,7 +12,12 @@ | |||
| 12 | 12 | ||
| 13 | import numpy as np | 13 | import numpy as np |
| 14 | 14 | ||
| 15 | -__golden__ = {"kernel": {"relu6_grad": "relu6_grad_golden"}} | 15 | +__golden__ = { |
| 16 | + "kernel": { | ||
| 17 | + "relu6_grad": "relu6_grad_golden", | ||
| 18 | + "Relu6Grad": "relu6_grad_golden", | ||
| 19 | + } | ||
| 20 | +} | ||
| 16 | 21 | ||
| 17 | 22 | ||
| 18 | def relu6_grad_golden(gradients, features, **kwargs): | 23 | def relu6_grad_golden(gradients, features, **kwargs): |
| @@ -29,22 +34,18 @@ def relu6_grad_golden(gradients, features, **kwargs): | |||
| 29 | (any comparison with NaN is false). dy is passed through verbatim when | 34 | (any comparison with NaN is false). dy is passed through verbatim when |
| 30 | the mask is true, so dy = NaN/Inf propagates only inside the (0, 6) band. | 35 | the mask is true, so dy = NaN/Inf propagates only inside the (0, 6) band. |
| 31 | """ | 36 | """ |
| 32 | - import tensorflow as tf | 37 | + import torch |
| 33 | 38 | ||
| 34 | - with tf.device("/CPU:0"): | 39 | + output_dtype = np.asarray(gradients).dtype |
| 35 | - dy = tf.convert_to_tensor(np.asarray(gradients)) | 40 | + # Keep CPU reference arithmetic independent of the device's float32 path. |
| 36 | - x = tf.convert_to_tensor(np.asarray(features)) | 41 | + # NumPy conversion also accepts bfloat16 arrays unsupported by from_numpy. |
| 37 | - output_dtype = dy.dtype | 42 | + dy = torch.from_numpy(np.array(gradients, dtype=np.float64, copy=True)) |
| 38 | - promote = kwargs.get("golden_mode") == "Promote" | 43 | + x = torch.from_numpy(np.array(features, dtype=np.float64, copy=True)) |
| 39 | - if not promote and output_dtype in (tf.float16, tf.bfloat16): | 44 | + mask = (x > 0.0) & (x < 6.0) |
| 40 | - dy, x = tf.cast(dy, tf.float32), tf.cast(x, tf.float32) | 45 | + output = torch.where(mask, dy, torch.zeros_like(dy)).numpy() |
| 41 | - mask = tf.logical_and(x > 0.0, x < 6.0) | 46 | + if kwargs.get("golden_mode") == "Promote": |
| 42 | - # Kernel uses Select. Native TF Relu6Grad multiplies by the mask and | 47 | + return output |
| 43 | - # produces NaN for out-of-band NaN/Inf gradients; keep the paths distinct. | 48 | + return output.astype(output_dtype) |
| 44 | - output = tf.where(mask, dy, tf.zeros_like(dy)) | ||
| 45 | - if not promote: | ||
| 46 | - output = tf.cast(output, output_dtype) | ||
| 47 | - return np.asarray(output.numpy()) | ||
| 48 | 49 | ||
| 49 | 50 | ||
| 50 | # ---------------------------------------------------------------------------- | 51 | # ---------------------------------------------------------------------------- |
| @@ -108,6 +109,7 @@ class Relu6GradTensorFlowSpec: | |||
| 108 | 109 | ||
| 109 | __spec__ = { | 110 | __spec__ = { |
| 110 | "relu6_grad": "Relu6GradKernelSpec", | 111 | "relu6_grad": "Relu6GradKernelSpec", |
| 112 | + "Relu6Grad": "Relu6GradKernelSpec", | ||
| 111 | "tf.raw_ops.Relu6Grad": "Relu6GradTensorFlowSpec", | 113 | "tf.raw_ops.Relu6Grad": "Relu6GradTensorFlowSpec", |
| 112 | } | 114 | } |
| 113 | 115 | ||
| @@ -0,0 +1,11 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 2 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 3 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 4 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 6 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 7 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 8 | + | ||
| 9 | +if(UT_TEST_ALL OR OP_GRAPH_UT) | ||
| 10 | + add_modules_ut_sources(HOSTNAME ${OP_GRAPH_MODULE_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR}) | ||
| 11 | +endif() | ||
| @@ -0,0 +1,47 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include <gtest/gtest.h> | ||
| 12 | +#include "base/context_builder/op_infer_datatype_context_builder.h" | ||
| 13 | + | ||
| 14 | +namespace ops { | ||
| 15 | +ge::graphStatus InferDataType4Relu6Grad(gert::InferDataTypeContext* context); | ||
| 16 | +} // namespace ops | ||
| 17 | + | ||
| 18 | +namespace { | ||
| 19 | +void CheckDtype(ge::DataType gradients, ge::DataType features, ge::graphStatus expected) | ||
| 20 | +{ | ||
| 21 | + gert::OpInferDataTypeContextBuilder builder; | ||
| 22 | + builder.OpType("Relu6Grad").OpName("relu6_grad_dtype").IONum(2, 1); | ||
| 23 | + builder.InputTensorDesc(0, gradients, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 24 | + builder.InputTensorDesc(1, features, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 25 | + builder.OutputTensorDesc(0, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 26 | + auto holder = builder.Build(); | ||
| 27 | + ASSERT_NE(holder.GetContext(), nullptr); | ||
| 28 | + ASSERT_EQ(holder.GetContext()->SetOutputDataType(0, ge::DT_UNDEFINED), ge::GRAPH_SUCCESS); | ||
| 29 | + ASSERT_EQ(ops::InferDataType4Relu6Grad(holder.GetContext()), expected); | ||
| 30 | + if (expected == ge::GRAPH_SUCCESS) { | ||
| 31 | + EXPECT_EQ(holder.GetContext()->GetOutputDataType(0), gradients); | ||
| 32 | + } | ||
| 33 | +} | ||
| 34 | +} // namespace | ||
| 35 | + | ||
| 36 | +TEST(Relu6GradGraphInfer, PreservesAllSupportedDataTypes) | ||
| 37 | +{ | ||
| 38 | + for (auto dtype : {ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_BF16}) { | ||
| 39 | + CheckDtype(dtype, dtype, ge::GRAPH_SUCCESS); | ||
| 40 | + } | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +TEST(Relu6GradGraphInfer, RejectsMismatchedInputsWithoutLegacyVerifier) | ||
| 44 | +{ | ||
| 45 | + CheckDtype(ge::DT_FLOAT16, ge::DT_FLOAT, ge::GRAPH_FAILED); | ||
| 46 | + CheckDtype(ge::DT_BF16, ge::DT_FLOAT16, ge::GRAPH_FAILED); | ||
| 47 | +} | ||
| @@ -20,7 +20,7 @@ | |||
| 20 | #include "exe_graph/runtime/storage_shape.h" | 20 | #include "exe_graph/runtime/storage_shape.h" |
| 21 | #include "platform/platform_infos_def.h" | 21 | #include "platform/platform_infos_def.h" |
| 22 | #include "ut_op_util.h" | 22 | #include "ut_op_util.h" |
| 23 | -#include "atvoss/elewise/elewise_tiling.h" | 23 | +#include "atvoss/broadcast/broadcast_tiling.h" |
| 24 | #include "../../../../op_host/arch35/relu6_grad_tiling_arch35.h" | 24 | #include "../../../../op_host/arch35/relu6_grad_tiling_arch35.h" |
| 25 | 25 | ||
| 26 | using namespace ut_util; | 26 | using namespace ut_util; |
| @@ -62,7 +62,7 @@ static ge::graphStatus RunTiling(ge::DataType gradDtype, ge::DataType featDtype, | |||
| 62 | fe::PlatFormInfos platform_info; | 62 | fe::PlatFormInfos platform_info; |
| 63 | platform_info.Init(); | 63 | platform_info.Init(); |
| 64 | 64 | ||
| 65 | - optiling::ElewiseCompileInfo compile_info; | 65 | + Ops::Base::BroadcastCompileInfo compile_info; |
| 66 | compile_info.coreNum = 64; | 66 | compile_info.coreNum = 64; |
| 67 | compile_info.ubSize = 262144; | 67 | compile_info.ubSize = 262144; |
| 68 | 68 | ||
| @@ -337,28 +337,3 @@ TEST_F(relu6grad, relu6grad_infer_shape_dynamic) | |||
| 337 | 337 | ||
| 338 | ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); | 338 | ASSERT_EQ(inferShapeFunc(holder.GetContext<gert::InferShapeContext>()), ge::GRAPH_SUCCESS); |
| 339 | } | 339 | } |
| 340 | - | ||
| 341 | -TEST_F(relu6grad, relu6grad_infer_datatype) | ||
| 342 | -{ | ||
| 343 | - ASSERT_NE(gert::OpImplRegistry::GetInstance().GetOpImpl("Relu6Grad"), nullptr); | ||
| 344 | - auto dataTypeFunc = gert::OpImplRegistry::GetInstance().GetOpImpl("Relu6Grad")->infer_datatype; | ||
| 345 | - ASSERT_NE(dataTypeFunc, nullptr); | ||
| 346 | - // backprops 跟随 gradients,覆盖 def 中声明的 fp16/fp32/bf16 | ||
| 347 | - for (auto dtype : {ge::DT_FLOAT16, ge::DT_FLOAT, ge::DT_BF16}) { | ||
| 348 | - ge::DataType in0 = dtype; | ||
| 349 | - ge::DataType in1 = dtype; | ||
| 350 | - ge::DataType expOut0 = ge::DT_UNDEFINED; | ||
| 351 | - auto contextHolder = gert::InferDataTypeContextFaker() | ||
| 352 | - .NodeIoNum(2, 1) | ||
| 353 | - .NodeInputTd(0, dtype, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 354 | - .NodeInputTd(1, dtype, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 355 | - .NodeOutputTd(0, dtype, ge::FORMAT_ND, ge::FORMAT_ND) | ||
| 356 | - .InputDataTypes({&in0, &in1}) | ||
| 357 | - .OutputDataTypes({&expOut0}) | ||
| 358 | - .Build(); | ||
| 359 | - auto context = contextHolder.GetContext<gert::InferDataTypeContext>(); | ||
| 360 | - ASSERT_NE(context, nullptr); | ||
| 361 | - EXPECT_EQ(dataTypeFunc(context), ge::GRAPH_SUCCESS); | ||
| 362 | - EXPECT_EQ(context->GetOutputDataType(0), dtype); | ||
| 363 | - } | ||
| 364 | -} | ||
| @@ -5793,10 +5793,10 @@ | |||
| 5793 | <td><a href="../../vfusion/normalize_bbox/README.md">normalize_bbox</a></td> | 5793 | <td><a href="../../vfusion/normalize_bbox/README.md">normalize_bbox</a></td> |
| 5794 | <td>✓</td> | 5794 | <td>✓</td> |
| 5795 | <td>✓</td> | 5795 | <td>✓</td> |
| 5796 | - <td>✓</td> | 5796 | + <td>✗</td> |
| 5797 | <td>✓</td> | 5797 | <td>✓</td> |
| 5798 | <td>AI Core</td> | 5798 | <td>AI Core</td> |
| 5799 | - <td>将目标检测Prediction阶段筛选后的预选框绝对像素坐标,按对应图像的高与宽逐batch归一化到[0, 1]区间,得到相对坐标框。</td> | 5799 | + <td>将目标检测Prediction阶段筛选后的预选框绝对像素坐标,按对应图像的高与宽逐batch换算为相对坐标,结果不截断,输入框越界时输出可超出[0, 1]。</td> |
| 5800 | </tr> | 5800 | </tr> |
| 5801 | <tr> | 5801 | <tr> |
| 5802 | <td>vfusion</td> | 5802 | <td>vfusion</td> |
| @@ -13,58 +13,10 @@ | |||
| 13 | * \brief INTrainingReduceV2 graph infer registration. | 13 | * \brief INTrainingReduceV2 graph infer registration. |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | -#include <cstddef> | ||
| 17 | -#include <cstdint> | ||
| 18 | -#include <vector> | ||
| 19 | - | ||
| 20 | #include "../in_training_reduce_v2_infer_common.h" | 16 | #include "../in_training_reduce_v2_infer_common.h" |
| 21 | -#include "graph/operator_reg.h" | ||
| 22 | #include "log/log.h" | 17 | #include "log/log.h" |
| 23 | #include "register/op_impl_registry.h" | 18 | #include "register/op_impl_registry.h" |
| 24 | 19 | ||
| 25 | -namespace ge { | ||
| 26 | -IMPLEMT_COMMON_INFERFUNC(INTrainingReduceV2LegacyV1InferShape) | ||
| 27 | -{ | ||
| 28 | - const TensorDesc xDesc = op.GetInputDescByName("x"); | ||
| 29 | - const std::vector<int64_t> xDims = xDesc.GetShape().GetDims(); | ||
| 30 | - const Format originFormat = xDesc.GetOriginFormat(); | ||
| 31 | - const bool isChannelLast = (originFormat == FORMAT_NHWC || originFormat == FORMAT_NDHWC); | ||
| 32 | - if (xDims != UNKNOWN_RANK && !ops::IsINTrainingReduceV2RankValid(originFormat, xDims.size())) { | ||
| 33 | - OP_LOGE_FOR_INVALID_SHAPEDIM_WITH_REASON( | ||
| 34 | - "INTrainingReduceV2", "x", std::to_string(xDims.size()).c_str(), | ||
| 35 | - "NCHW/NHWC require rank 4, NCDHW/NDHWC require rank 5, and ND requires rank in [2, 8]"); | ||
| 36 | - return GRAPH_FAILED; | ||
| 37 | - } | ||
| 38 | - | ||
| 39 | - std::vector<int64_t> outputDims(xDims.size(), 1); | ||
| 40 | - const size_t channelIndex = (isChannelLast && !xDims.empty()) ? (xDims.size() - 1U) : 1U; | ||
| 41 | - for (size_t i = 0; i < xDims.size(); ++i) { | ||
| 42 | - if (i == 0U || i == channelIndex) { | ||
| 43 | - outputDims[i] = xDims[i]; | ||
| 44 | - } | ||
| 45 | - } | ||
| 46 | - | ||
| 47 | - TensorDesc sumDesc = op.GetOutputDescByName("sum"); | ||
| 48 | - TensorDesc squareSumDesc = op.GetOutputDescByName("square_sum"); | ||
| 49 | - sumDesc.SetShape(Shape(outputDims)); | ||
| 50 | - squareSumDesc.SetShape(Shape(outputDims)); | ||
| 51 | - sumDesc.SetOriginShape(Shape(outputDims)); | ||
| 52 | - squareSumDesc.SetOriginShape(Shape(outputDims)); | ||
| 53 | - sumDesc.SetDataType(DT_FLOAT); | ||
| 54 | - squareSumDesc.SetDataType(DT_FLOAT); | ||
| 55 | - if (op.UpdateOutputDesc("sum", sumDesc) != GRAPH_SUCCESS || | ||
| 56 | - op.UpdateOutputDesc("square_sum", squareSumDesc) != GRAPH_SUCCESS) { | ||
| 57 | - return GRAPH_FAILED; | ||
| 58 | - } | ||
| 59 | - return GRAPH_SUCCESS; | ||
| 60 | -} | ||
| 61 | - | ||
| 62 | -// Static GEIR graphs still invoke the legacy V1 registry for this built-in op. | ||
| 63 | -// The custom package is loaded before libopsproto.so, so register the corrected | ||
| 64 | -// ND channel-second contract here as well as in the runtime2.0 registry below. | ||
| 65 | -COMMON_INFER_FUNC_REG(INTrainingReduceV2, INTrainingReduceV2LegacyV1InferShape); | ||
| 66 | -} // namespace ge | ||
| 67 | - | ||
| 68 | namespace ops { | 20 | namespace ops { |
| 69 | ge::graphStatus InferShapeForINTrainingReduceV2(gert::InferShapeContext* context) | 21 | ge::graphStatus InferShapeForINTrainingReduceV2(gert::InferShapeContext* context) |
| 70 | { | 22 | { |
| @@ -113,7 +113,7 @@ protected: | |||
| 113 | ge::graphStatus PostTiling() override; | 113 | ge::graphStatus PostTiling() override; |
| 114 | 114 | ||
| 115 | private: | 115 | private: |
| 116 | - // sub-R 分块 tiling(R 超单次 UB 容量,DESIGN §6.3 路 A) | 116 | + // sub-R 分块 tiling(R 超单次 UB 容量) |
| 117 | bool DoSubRTiling(uint64_t rAlign, uint64_t binAddQuotient, int64_t elemSize); | 117 | bool DoSubRTiling(uint64_t rAlign, uint64_t binAddQuotient, int64_t elemSize); |
| 118 | // sub-R 路径 Kernel 侧 UB 精确占用(须与 op_kernel/arch35 的 InitSubR() 逐项一致) | 118 | // sub-R 路径 Kernel 侧 UB 精确占用(须与 op_kernel/arch35 的 InitSubR() 逐项一致) |
| 119 | uint64_t CalcSubRUbBytes(uint64_t rFactor, uint64_t chunksPerGroup, uint64_t numGroups, int64_t elemSize) const; | 119 | uint64_t CalcSubRUbBytes(uint64_t rFactor, uint64_t chunksPerGroup, uint64_t numGroups, int64_t elemSize) const; |
Mnorm/in_training_reduce_v2/op_host/arch35/in_training_reduce_v2_tiling_ar_full_reduce_arch35.cpp+2-2
| @@ -99,7 +99,7 @@ bool INTrainingReduceV2ARFullReduceTiling::IsCapable() | |||
| 99 | outputBytesPerRow > usable - alignedInputBytes * DOUBLE_BUFFER_COUNT || | 99 | outputBytesPerRow > usable - alignedInputBytes * DOUBLE_BUFFER_COUNT || |
| 100 | binAddBufferOneline > | 100 | binAddBufferOneline > |
| 101 | (usable - alignedInputBytes * DOUBLE_BUFFER_COUNT - outputBytesPerRow) / REDUCE_SCRATCH_COUNT) { | 101 | (usable - alignedInputBytes * DOUBLE_BUFFER_COUNT - outputBytesPerRow) / REDUCE_SCRATCH_COUNT) { |
| 102 | - // 单行 R 全载超单次 UB 容量 → sub-R 分块路径(DESIGN §6.3 路 A),同 TilingKey + isSubRTiling 标志。 | 102 | + // 单行 R 全载超单次 UB 容量 → sub-R 分块路径,同 TilingKey + isSubRTiling 标志。 |
| 103 | return DoSubRTiling(rAlign, binAddQuotient, elemSize); | 103 | return DoSubRTiling(rAlign, binAddQuotient, elemSize); |
| 104 | } | 104 | } |
| 105 | const uint64_t bytesPerRow = alignedInputBytes * DOUBLE_BUFFER_COUNT + outputBytesPerRow + | 105 | const uint64_t bytesPerRow = alignedInputBytes * DOUBLE_BUFFER_COUNT + outputBytesPerRow + |
| @@ -189,7 +189,7 @@ uint64_t INTrainingReduceV2ARFullReduceTiling::CalcSubRUbBytes(uint64_t rFactor, | |||
| 189 | return inBytes + partialBytes + outBytes; | 189 | return inBytes + partialBytes + outBytes; |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | -// sub-R 分块 tiling(DESIGN §6.3 路 A):R 超单次 UB 容量时,按 rFactor 分块搬入, | 192 | +// sub-R 分块 tiling:R 超单次 UB 容量时,按 rFactor 分块搬入, |
| 193 | // 每块归约累进独立 fp32 累加器,跨块固定顺序树归约。按行(N*C)切核。 | 193 | // 每块归约累进独立 fp32 累加器,跨块固定顺序树归约。按行(N*C)切核。 |
| 194 | // | 194 | // |
| 195 | // 两段式: | 195 | // 两段式: |
| @@ -98,7 +98,7 @@ public: | |||
| 98 | pipe_.InitBuffer(squareBinaryAddBuf_, scratchBufferBytes_); | 98 | pipe_.InitBuffer(squareBinaryAddBuf_, scratchBufferBytes_); |
| 99 | } | 99 | } |
| 100 | 100 | ||
| 101 | - // sub-R 分块路径 buffer 规划(DESIGN §6.3 路 A)。 | 101 | + // sub-R 分块路径 buffer 规划。 |
| 102 | __aicore__ inline void InitSubR() | 102 | __aicore__ inline void InitSubR() |
| 103 | { | 103 | { |
| 104 | // 部分和槽位只与 chunksPerGroup 有关,与 R 无关;分组时多留 1 格给组间 carry。 | 104 | // 部分和槽位只与 chunksPerGroup 有关,与 R 无关;分组时多留 1 格给组间 carry。 |
| @@ -10,7 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | /*! | 11 | /*! |
| 12 | * \file in_training_reduce_v2_sub_r.h | 12 | * \file in_training_reduce_v2_sub_r.h |
| 13 | - * \brief sub-R 分块路径(DESIGN §6.3 路 A):R 超单次 UB 容量时按 rFactor 分块搬入, | 13 | + * \brief sub-R 分块路径:R 超单次 UB 容量时按 rFactor 分块搬入, |
| 14 | * 每块归约得 Σx/Σx² 的高低双分量部分和,再做固定顺序补偿树归约。 | 14 | * 每块归约得 Σx/Σx² 的高低双分量部分和,再做固定顺序补偿树归约。 |
| 15 | * 部分和缓存按 chunksPerGroup 分组:每组折叠一次,高低两分量写入 carry 槽参与下一组折叠, | 15 | * 部分和缓存按 chunksPerGroup 分组:每组折叠一次,高低两分量写入 carry 槽参与下一组折叠, |
| 16 | * 避免全量部分和缓存限制 R;numGroups==1 时只需一次组内折叠。 | 16 | * 避免全量部分和缓存限制 R;numGroups==1 时只需一次组内折叠。 |
| @@ -28,7 +28,7 @@ struct INTrainingReduceV2ARFullReduceTilingData { | |||
| 28 | int64_t cTail; // C 轴尾块长度 | 28 | int64_t cTail; // C 轴尾块长度 |
| 29 | uint64_t binaryAddQuotient; // 小于 rAlign 的最大 2 的幂(pairwise 折叠点) | 29 | uint64_t binaryAddQuotient; // 小于 rAlign 的最大 2 的幂(pairwise 折叠点) |
| 30 | int64_t perCoreCnt; // 平均每个核处理的 tile 数 | 30 | int64_t perCoreCnt; // 平均每个核处理的 tile 数 |
| 31 | - // ---- sub-R 分块路径(DESIGN §6.3 路 A;R 超单次 UB 容量时启用)---- | 31 | + // ---- sub-R 分块路径(R 超单次 UB 容量时启用)---- |
| 32 | uint64_t isSubRTiling; // 0=R 全载路径;1=sub-R 分块路径 | 32 | uint64_t isSubRTiling; // 0=R 全载路径;1=sub-R 分块路径 |
| 33 | uint64_t rFactor; // sub-R 分块的块大小(元素数,VL_FP32 整数倍) | 33 | uint64_t rFactor; // sub-R 分块的块大小(元素数,VL_FP32 整数倍) |
| 34 | uint64_t numChunks; // ceil(numR / rFactor),每行分块数 | 34 | uint64_t numChunks; // ceil(numR / rFactor),每行分块数 |
| @@ -11,8 +11,8 @@ | |||
| 11 | message(STATUS "=== Debug: start ops.norm.in_training_reduce_v2.tests.CMakeLists.txt ") | 11 | message(STATUS "=== Debug: start ops.norm.in_training_reduce_v2.tests.CMakeLists.txt ") |
| 12 | 12 | ||
| 13 | # 仅纳入 ut 子目录参与主构建(UT 集成到 nn_op_host_ut)。 | 13 | # 仅纳入 ut 子目录参与主构建(UT 集成到 nn_op_host_ut)。 |
| 14 | -# st/ 是独立自包含的 C++ ST 工程(自带 project() + Real 模式 FATAL_ERROR), | 14 | +# 系统测试参考实现位于 tests/assets/golden.py,由 ops-test-kit 加载, |
| 15 | -# 通过 tests/st/run.sh 单独构建,不并入主 UT 构建树。 | 15 | +# 不作为 C++ ST 工程并入主 UT 构建树。 |
| 16 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ut/CMakeLists.txt") | 16 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/ut/CMakeLists.txt") |
| 17 | add_subdirectory(ut) | 17 | add_subdirectory(ut) |
| 18 | endif() | 18 | endif() |
| @@ -12,6 +12,7 @@ | |||
| 12 | 12 | ||
| 13 | #include "base/context_builder/op_infer_datatype_context_builder.h" | 13 | #include "base/context_builder/op_infer_datatype_context_builder.h" |
| 14 | #include "base/context_builder/op_infer_shape_context_builder.h" | 14 | #include "base/context_builder/op_infer_shape_context_builder.h" |
| 15 | +#include "base/registry/op_impl_space_registry_v2.h" | ||
| 15 | #include "exe_graph/runtime/runtime_tensor.h" | 16 | #include "exe_graph/runtime/runtime_tensor.h" |
| 16 | 17 | ||
| 17 | namespace ops { | 18 | namespace ops { |
| @@ -81,3 +82,92 @@ TEST(INTrainingReduceV2GraphInferTest, graph_infer_rejects_invalid_nd_rank_004) | |||
| 81 | ASSERT_NE(holder.GetContext(), nullptr); | 82 | ASSERT_NE(holder.GetContext(), nullptr); |
| 82 | EXPECT_EQ(ops::InferShapeForINTrainingReduceV2(holder.GetContext()), ge::GRAPH_FAILED); | 83 | EXPECT_EQ(ops::InferShapeForINTrainingReduceV2(holder.GetContext()), ge::GRAPH_FAILED); |
| 83 | } | 84 | } |
| 85 | + | ||
| 86 | +namespace { | ||
| 87 | +void CheckRegisteredInfer(const gert::Shape& inputShape, ge::Format format, const gert::Shape& expected, | ||
| 88 | + ge::graphStatus status = ge::GRAPH_SUCCESS) | ||
| 89 | +{ | ||
| 90 | + auto registry = gert::DefaultOpImplSpaceRegistryV2::GetInstance().GetSpaceRegistry(); | ||
| 91 | + ASSERT_NE(registry, nullptr); | ||
| 92 | + const auto* impl = registry->GetOpImpl("INTrainingReduceV2"); | ||
| 93 | + ASSERT_NE(impl, nullptr); | ||
| 94 | + ASSERT_NE(impl->infer_shape, nullptr); | ||
| 95 | + gert::StorageShape storage; | ||
| 96 | + storage.MutableOriginShape() = inputShape; | ||
| 97 | + storage.MutableStorageShape() = inputShape; | ||
| 98 | + gert::Tensor input(storage, gert::StorageFormat(format, format, {}), ge::DT_FLOAT16); | ||
| 99 | + gert::OpInferShapeContextBuilder builder; | ||
| 100 | + builder.OpType("INTrainingReduceV2").OpName("registered_reduce"); | ||
| 101 | + builder.IONum(1, 2).InputTensors({&input}); | ||
| 102 | + builder.OutputTensorDesc(0, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 103 | + builder.OutputTensorDesc(1, ge::DT_FLOAT, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 104 | + auto holder = builder.Build(); | ||
| 105 | + ASSERT_NE(holder.GetContext(), nullptr); | ||
| 106 | + auto* context = holder.GetContext(); | ||
| 107 | + ASSERT_NE(context->GetOutputShape(0), nullptr); | ||
| 108 | + ASSERT_NE(context->GetOutputShape(1), nullptr); | ||
| 109 | + *context->GetOutputShape(0) = gert::Shape({91, 92}); | ||
| 110 | + *context->GetOutputShape(1) = gert::Shape({93}); | ||
| 111 | + ASSERT_EQ(impl->infer_shape(context), status); | ||
| 112 | + EXPECT_EQ(*context->GetInputShape(0), inputShape); | ||
| 113 | + if (status == ge::GRAPH_SUCCESS) { | ||
| 114 | + EXPECT_EQ(*context->GetOutputShape(0), expected); | ||
| 115 | + EXPECT_EQ(*context->GetOutputShape(1), expected); | ||
| 116 | + ASSERT_EQ(impl->infer_shape(context), ge::GRAPH_SUCCESS); | ||
| 117 | + EXPECT_EQ(*context->GetOutputShape(0), expected); | ||
| 118 | + EXPECT_EQ(*context->GetOutputShape(1), expected); | ||
| 119 | + } | ||
| 120 | +} | ||
| 121 | +} // namespace | ||
| 122 | + | ||
| 123 | +TEST(INTrainingReduceV2GraphInferTest, RegisteredChannelFirstFormats) | ||
| 124 | +{ | ||
| 125 | + CheckRegisteredInfer({2, 3, 5, 7}, ge::FORMAT_NCHW, {2, 3, 1, 1}); | ||
| 126 | + CheckRegisteredInfer({2, 3, 5, 7, 11}, ge::FORMAT_NCDHW, {2, 3, 1, 1, 1}); | ||
| 127 | +} | ||
| 128 | + | ||
| 129 | +TEST(INTrainingReduceV2GraphInferTest, RegisteredChannelLastFormats) | ||
| 130 | +{ | ||
| 131 | + CheckRegisteredInfer({2, 5, 7, 3}, ge::FORMAT_NHWC, {2, 1, 1, 3}); | ||
| 132 | + CheckRegisteredInfer({2, 5, 7, 11, 3}, ge::FORMAT_NDHWC, {2, 1, 1, 1, 3}); | ||
| 133 | +} | ||
| 134 | + | ||
| 135 | +TEST(INTrainingReduceV2GraphInferTest, RegisteredNDRankBounds) | ||
| 136 | +{ | ||
| 137 | + CheckRegisteredInfer({2, 3}, ge::FORMAT_ND, {2, 3}); | ||
| 138 | + CheckRegisteredInfer({2, 3, 1, 2, 3, 4, 5, 6}, ge::FORMAT_ND, {2, 3, 1, 1, 1, 1, 1, 1}); | ||
| 139 | +} | ||
| 140 | + | ||
| 141 | +TEST(INTrainingReduceV2GraphInferTest, RegisteredDynamicAndEmptyShapes) | ||
| 142 | +{ | ||
| 143 | + CheckRegisteredInfer({-2}, ge::FORMAT_ND, {-2}); | ||
| 144 | + CheckRegisteredInfer({-1, -1, 5, 7}, ge::FORMAT_NCHW, {-1, -1, 1, 1}); | ||
| 145 | + CheckRegisteredInfer({2, 5, -1, -1}, ge::FORMAT_NHWC, {2, 1, 1, -1}); | ||
| 146 | + CheckRegisteredInfer({0, 3, 5}, ge::FORMAT_ND, {0, 3, 1}); | ||
| 147 | +} | ||
| 148 | + | ||
| 149 | +TEST(INTrainingReduceV2GraphInferTest, RegisteredRejectsInvalidFormatAndRank) | ||
| 150 | +{ | ||
| 151 | + CheckRegisteredInfer({3}, ge::FORMAT_ND, {}, ge::GRAPH_FAILED); | ||
| 152 | + CheckRegisteredInfer({2, 3, 4}, ge::FORMAT_NHWC, {}, ge::GRAPH_FAILED); | ||
| 153 | + CheckRegisteredInfer({2, 3, 4, 5}, ge::FORMAT_NDHWC, {}, ge::GRAPH_FAILED); | ||
| 154 | + CheckRegisteredInfer({2, 3, 4, 5}, ge::FORMAT_HWCN, {}, ge::GRAPH_FAILED); | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +TEST(INTrainingReduceV2GraphInferTest, DtypeOverwritesBothOutputs) | ||
| 158 | +{ | ||
| 159 | + for (auto dtype : {ge::DT_FLOAT16, ge::DT_FLOAT}) { | ||
| 160 | + gert::OpInferDataTypeContextBuilder builder; | ||
| 161 | + builder.OpType("INTrainingReduceV2").OpName("registered_reduce_dtype").IONum(1, 2); | ||
| 162 | + builder.InputTensorDesc(0, dtype, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 163 | + builder.OutputTensorDesc(0, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 164 | + builder.OutputTensorDesc(1, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 165 | + auto holder = builder.Build(); | ||
| 166 | + ASSERT_NE(holder.GetContext(), nullptr); | ||
| 167 | + ASSERT_EQ(holder.GetContext()->SetOutputDataType(0, ge::DT_FLOAT16), ge::GRAPH_SUCCESS); | ||
| 168 | + ASSERT_EQ(holder.GetContext()->SetOutputDataType(1, ge::DT_INT32), ge::GRAPH_SUCCESS); | ||
| 169 | + ASSERT_EQ(ops::InferDataType4INTrainingReduceV2(holder.GetContext()), ge::GRAPH_SUCCESS); | ||
| 170 | + EXPECT_EQ(holder.GetContext()->GetOutputDataType(0), ge::DT_FLOAT); | ||
| 171 | + EXPECT_EQ(holder.GetContext()->GetOutputDataType(1), ge::DT_FLOAT); | ||
| 172 | + } | ||
| 173 | +} | ||
| @@ -11,7 +11,7 @@ | |||
| 11 | /*! | 11 | /*! |
| 12 | * \file test_in_training_reduce_v2_tiling.cpp | 12 | * \file test_in_training_reduce_v2_tiling.cpp |
| 13 | * \brief arch35 核心路径 UT —— Tiling(AR_FULL_REDUCE / R 全载) | 13 | * \brief arch35 核心路径 UT —— Tiling(AR_FULL_REDUCE / R 全载) |
| 14 | - * 契约(spec.yaml / DESIGN §5.1/§6.3): | 14 | + * 切分约束(对应op_host/arch35中的Tiling实现): |
| 15 | * - TilingKey == 200000(AR_FULL_REDUCE) | 15 | * - TilingKey == 200000(AR_FULL_REDUCE) |
| 16 | * - IsCapable 对 NCHW / ND / NCDHW 返回 true(一期支持) | 16 | * - IsCapable 对 NCHW / ND / NCDHW 返回 true(一期支持) |
| 17 | * - 输入 1(x)/ 输出 2(sum, square_sum),无 attr | 17 | * - 输入 1(x)/ 输出 2(sum, square_sum),无 attr |
| @@ -11,7 +11,7 @@ | |||
| 11 | /*! | 11 | /*! |
| 12 | * \file test_in_training_reduce_v2_infershape.cpp | 12 | * \file test_in_training_reduce_v2_infershape.cpp |
| 13 | * \brief arch35 核心路径 UT —— InferShape | 13 | * \brief arch35 核心路径 UT —— InferShape |
| 14 | - * 契约(spec.yaml / DESIGN §6.2): | 14 | + * 推导约束(对应op_host/in_training_reduce_v2_infershape.cpp): |
| 15 | * - InferShape:输入 x [N,C,H,W] → sum/square_sum shape 均为 [N,C,1,1] | 15 | * - InferShape:输入 x [N,C,H,W] → sum/square_sum shape 均为 [N,C,1,1] |
| 16 | * (N,C 取自 x、空间轴置 1);5D [N,C,D,H,W] → [N,C,1,1,1]。 | 16 | * (N,C 取自 x、空间轴置 1);5D [N,C,D,H,W] → [N,C,1,1,1]。 |
| 17 | * - C 轴位置由 origin format 决定:channel-first(NCHW/NCDHW/ND)在 dim 1, | 17 | * - C 轴位置由 origin format 决定:channel-first(NCHW/NCDHW/ND)在 dim 1, |
| @@ -19,7 +19,7 @@ | |||
| 19 | * storage format,约束不到 origin format —— 后者由用户网络决定(TF 来源 | 19 | * storage format,约束不到 origin format —— 后者由用户网络决定(TF 来源 |
| 20 | * 天然是 NHWC),GE 会插 TransData 转排布,但 InferShape 跑在其之前。 | 20 | * 天然是 NHWC),GE 会插 TransData 转排布,但 InferShape 跑在其之前。 |
| 21 | * | 21 | * |
| 22 | - * InferDataType(输出恒 DT_FLOAT)和静态图 legacy V1 bridge 位于 | 22 | + * InferDataType(输出恒 DT_FLOAT)和图推导的runtime2.0注册位于 |
| 23 | * op_graph/in_training_reduce_v2_graph_infer.cpp,由 op_graph UT 与安装后的 | 23 | * op_graph/in_training_reduce_v2_graph_infer.cpp,由 op_graph UT 与安装后的 |
| 24 | * GEIR 用例验证;本文件只验证 runtime2.0 InferShape。 | 24 | * GEIR 用例验证;本文件只验证 runtime2.0 InferShape。 |
| 25 | */ | 25 | */ |
| @@ -0,0 +1,28 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 2 | +# This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 3 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 4 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 6 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 7 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 8 | + | ||
| 9 | +cmake_minimum_required(VERSION 3.18) | ||
| 10 | +project(GE_IR_EXAMPLE LANGUAGES CXX) | ||
| 11 | + | ||
| 12 | +set(ASCEND_PATH "$ENV{ASCEND_HOME_PATH}") | ||
| 13 | +if(NOT ASCEND_PATH) | ||
| 14 | + message(FATAL_ERROR "Source the CANN set_env.sh before configuring this example.") | ||
| 15 | +endif() | ||
| 16 | +find_library(GRAPH_LIBRARY NAMES libgraph.so PATHS "${ASCEND_PATH}/lib64" NO_DEFAULT_PATH REQUIRED) | ||
| 17 | +find_library(GE_RUNNER_LIBRARY NAMES libge_runner.so PATHS "${ASCEND_PATH}/lib64" NO_DEFAULT_PATH REQUIRED) | ||
| 18 | +find_library(GRAPH_BASE_LIBRARY NAMES libgraph_base.so PATHS "${ASCEND_PATH}/lib64" NO_DEFAULT_PATH REQUIRED) | ||
| 19 | +find_library(GE_COMPILER_LIBRARY NAMES libge_compiler.so PATHS "${ASCEND_PATH}/lib64" NO_DEFAULT_PATH REQUIRED) | ||
| 20 | + | ||
| 21 | +add_executable(test_geir_l2_normalize test_geir_l2_normalize.cpp) | ||
| 22 | +target_include_directories(test_geir_l2_normalize PRIVATE | ||
| 23 | + "${ASCEND_PATH}/include/graph" | ||
| 24 | + "${ASCEND_PATH}/include/ge" | ||
| 25 | + "${ASCEND_PATH}/opp/built-in/op_proto/inc" | ||
| 26 | + "${ASCEND_PATH}/include") | ||
| 27 | +target_link_libraries(test_geir_l2_normalize PRIVATE | ||
| 28 | + ${GRAPH_LIBRARY} ${GE_RUNNER_LIBRARY} ${GRAPH_BASE_LIBRARY} ${GE_COMPILER_LIBRARY}) | ||
| @@ -13,7 +13,9 @@ | |||
| 13 | * \brief GE graph construction sample for L2Normalize (arch35 ND format). | 13 | * \brief GE graph construction sample for L2Normalize (arch35 ND format). |
| 14 | */ | 14 | */ |
| 15 | #include <cstdint> | 15 | #include <cstdint> |
| 16 | +#include <cmath> | ||
| 16 | #include <cstdio> | 17 | #include <cstdio> |
| 18 | +#include <cstring> | ||
| 17 | #include <ctime> | 19 | #include <ctime> |
| 18 | #include <map> | 20 | #include <map> |
| 19 | #include <string> | 21 | #include <string> |
| @@ -121,6 +123,27 @@ int CreateGraph(DataType dt, std::vector<ge::Tensor>& input, std::vector<Operato | |||
| 121 | return SUCCESS; | 123 | return SUCCESS; |
| 122 | } | 124 | } |
| 123 | 125 | ||
| 126 | +bool CheckOutput(const std::vector<ge::Tensor>& output) | ||
| 127 | +{ | ||
| 128 | + constexpr size_t elementCount = 4 * 16; | ||
| 129 | + if (output.size() != 1 || output[0].GetTensorDesc().GetShape().GetDims() != std::vector<int64_t>{4, 16} || | ||
| 130 | + output[0].GetTensorDesc().GetDataType() != DT_FLOAT || output[0].GetSize() != elementCount * sizeof(float) || | ||
| 131 | + output[0].GetData() == nullptr) { | ||
| 132 | + LOG_PRINT("FAIL - unexpected output metadata or buffer\n"); | ||
| 133 | + return false; | ||
| 134 | + } | ||
| 135 | + // Each row contains sixteen ones: 1 / sqrt(16) = 0.25. | ||
| 136 | + for (size_t index = 0; index < elementCount; ++index) { | ||
| 137 | + float value = 0.0f; | ||
| 138 | + std::memcpy(&value, output[0].GetData() + index * sizeof(float), sizeof(float)); | ||
| 139 | + if (!std::isfinite(value) || std::fabs(value - 0.25f) > 1e-6f) { | ||
| 140 | + LOG_PRINT("FAIL - output[%zu]=%.9g, expected 0.25\n", index, static_cast<double>(value)); | ||
| 141 | + return false; | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | + return true; | ||
| 145 | +} | ||
| 146 | + | ||
| 124 | int main(int argc, char* argv[]) | 147 | int main(int argc, char* argv[]) |
| 125 | { | 148 | { |
| 126 | Graph graph("tc_ge_irrun_test"); | 149 | Graph graph("tc_ge_irrun_test"); |
| @@ -152,6 +175,8 @@ int main(int argc, char* argv[]) | |||
| 152 | } else if (session.RunGraph(gid, input, output) != SUCCESS) { | 175 | } else if (session.RunGraph(gid, input, output) != SUCCESS) { |
| 153 | LOG_PRINT("%s - FAIL - [XIR]: RunGraph failed\n", GetTime().c_str()); | 176 | LOG_PRINT("%s - FAIL - [XIR]: RunGraph failed\n", GetTime().c_str()); |
| 154 | result = FAILED; | 177 | result = FAILED; |
| 178 | + } else if (!CheckOutput(output)) { | ||
| 179 | + result = FAILED; | ||
| 155 | } else { | 180 | } else { |
| 156 | LOG_PRINT("%s - PASS - [XIR]: Session RunGraph succeeded, outputs=%zu\n", GetTime().c_str(), output.size()); | 181 | LOG_PRINT("%s - PASS - [XIR]: Session RunGraph succeeded, outputs=%zu\n", GetTime().c_str(), output.size()); |
| 157 | for (size_t i = 0; i < output.size(); i++) { | 182 | for (size_t i = 0; i < output.size(); i++) { |
| @@ -8,171 +8,76 @@ | |||
| 8 | * See LICENSE in the root of the software repository for the full text of the License. | 8 | * See LICENSE in the root of the software repository for the full text of the License. |
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | -// ============================================================================= | 11 | +/*! |
| 12 | -// norm/l2_normalize/op_graph/l2_normalize_graph_infer.cpp | 12 | + * \file l2_normalize_graph_infer.cpp |
| 13 | -// ============================================================================= | 13 | + * \brief Runtime 2.0 data type inference for L2Normalize. |
| 14 | -// | 14 | + */ |
| 15 | -// ROLE: Graph-level inference registration for the L2Normalize operator. | ||
| 16 | -// - V1(COMMON_INFER_FUNC_REG,ge::Operator 旧式推导):GE 图编译期 | ||
| 17 | -// (AddGraph/RunGraph 的 shape/type 推导 pass)实际调用的推导入口。本算子 IR | ||
| 18 | -// 与内置 nn_batch_norm_ops.h 的 L2Normalize 同名,内置 | ||
| 19 | -// .so 亦按同名注册 V1 推导(OneInOneOutDynamicInfer);本包在 custom 路径 | ||
| 20 | -// 先于内置包加载(AddSoToRegistry 顺序),按 insert-if-absent 语义以本实现 | ||
| 21 | -// 占据同名 V1 槽位,保证推导与校验语义来自本包(y.shape = x.shape、 | ||
| 22 | -// y.dtype = x.dtype)。 | ||
| 23 | -// - V2(IMPL_OP … InferDataType,gert 新式推导):动态图执行期 executor 直接 | ||
| 24 | -// 调用的 gert kernel 注册(InferShape 在 op_host/l2_normalize_infershape.cpp | ||
| 25 | -// 注册)。两条注册路径规则保持一致(恒等透传 + 同一套参数校验)。 | ||
| 26 | -// - 参数校验(GEIR 图模式):rank-0 标量拒绝(契约 rank 1–8,无合法 | ||
| 27 | -// axis);声明输出 shape/dtype 与 x 不一致拒绝(与 tiling 硬门禁保持一致; | ||
| 28 | -// 未声明(rank-0 空 shape / DT_UNDEFINED)与未知维 | ||
| 29 | -// (-1/-2)不校验);声明 format 仅 ND(FORMAT_RESERVED 未声明跳过)。 | ||
| 30 | -// dtype 合法域仍由 OpDef/REG_OP TensorType 声明承载。 | ||
| 31 | -// ============================================================================= | ||
| 32 | 15 | ||
| 33 | -#include "register/op_impl_registry.h" // IMPL_OP macro for operator registration | 16 | +#include "register/op_impl_registry.h" |
| 34 | -#include "graph/operator_reg.h" // IMPLEMT_COMMON_INFERFUNC / COMMON_INFER_FUNC_REG | 17 | +#include "op_common/log/log.h" |
| 35 | -#include "graph/utils/type_utils.h" // TypeUtils::DataTypeToSerialString | 18 | +#include "l2_normalize_proto.h" |
| 36 | -#include "op_common/log/log.h" // OP_LOGE / OP_LOGE_FOR_INVALID_* macros | ||
| 37 | -#include <string> | ||
| 38 | -#include <vector> | ||
| 39 | - | ||
| 40 | -using namespace ge; | ||
| 41 | 19 | ||
| 42 | namespace { | 20 | namespace { |
| 43 | - | 21 | +bool DeclaredShapeCompatible(const ge::Shape& declared, const ge::Shape& input) |
| 44 | -constexpr size_t INPUT_X_IDX = 0; | ||
| 45 | -constexpr size_t OUTPUT_Y_IDX = 0; | ||
| 46 | -constexpr size_t MAX_INPUT_RANK = 8; | ||
| 47 | - | ||
| 48 | -std::string ShapeToString(const ge::Shape& shape) | ||
| 49 | { | 22 | { |
| 50 | - std::string str = "["; | 23 | + if (declared.GetDimNum() == 0U || declared.GetDims() == ge::UNKNOWN_RANK || input.GetDims() == ge::UNKNOWN_RANK) { |
| 51 | - for (size_t i = 0; i < shape.GetDimNum(); ++i) { | 24 | + return true; |
| 52 | - if (i > 0) { | ||
| 53 | - str += ","; | ||
| 54 | - } | ||
| 55 | - str += std::to_string(shape.GetDim(i)); | ||
| 56 | } | 25 | } |
| 57 | - str += "]"; | 26 | + if (declared.GetDimNum() != input.GetDimNum()) { |
| 58 | - return str; | ||
| 59 | -} | ||
| 60 | - | ||
| 61 | -// 声明输出 shape 与 x 一致性:rank 相等且双侧均为已知维(≥0)时逐维相等。 | ||
| 62 | -// 任一侧未知维(<0,动态 shape -1/-2)不参与比对。 | ||
| 63 | -bool DeclaredShapeCompatible(const ge::Shape& declared, const ge::Shape& xShape) | ||
| 64 | -{ | ||
| 65 | - if (declared.GetDimNum() != xShape.GetDimNum()) { | ||
| 66 | return false; | 27 | return false; |
| 67 | } | 28 | } |
| 68 | - for (size_t i = 0; i < xShape.GetDimNum(); ++i) { | 29 | + for (size_t index = 0U; index < input.GetDimNum(); ++index) { |
| 69 | - const int64_t xDim = xShape.GetDim(i); | 30 | + if (input.GetDim(index) >= 0 && declared.GetDim(index) >= 0 && input.GetDim(index) != declared.GetDim(index)) { |
| 70 | - const int64_t yDim = declared.GetDim(i); | ||
| 71 | - if (xDim >= 0 && yDim >= 0 && xDim != yDim) { | ||
| 72 | return false; | 31 | return false; |
| 73 | } | 32 | } |
| 74 | } | 33 | } |
| 75 | return true; | 34 | return true; |
| 76 | } | 35 | } |
| 77 | - | ||
| 78 | } // namespace | 36 | } // namespace |
| 79 | 37 | ||
| 80 | -namespace ops { | 38 | +namespace ge { |
| 81 | - | 39 | +// Validate graph declarations before inference replaces the output descriptors. |
| 82 | -// --------------------------------------------------------------------------- | 40 | +// Runtime 2.0 output buffers contain inferred values, not the original declarations. |
| 83 | -// V1 图编译期推导(ge::Operator 旧式签名,ge::Operator& 入参): | 41 | +IMPLEMT_VERIFIER(L2Normalize, VerifyL2Normalize) |
| 84 | -// 0. 未知秩(rank=-2):GE 未知秩在 ge::Shape 中存为 dims={-2} 且 | ||
| 85 | -// GetDimNum()==0(与真标量取值相同,须以 GetDims() 区分,见 graph/tensor.h | ||
| 86 | -// ge::Shape 注释);零元素张量在动态图下即以该形态到达(FR-P1-011 | ||
| 87 | -// L0_empty_027/034:desc/ori_shape=[-2])。未知秩跳过步骤 1/2 的维数校验, | ||
| 88 | -// y 恒等复制未知秩;dtype/format 契约校验与维数无关,保留。 | ||
| 89 | -// 1. rank-0 标量拒绝(契约 rank 1–8;rank-0 无合法 axis); | ||
| 90 | -// 2. 声明输出 shape 一致性(y.shape = x.shape;未知维/未声明跳过); | ||
| 91 | -// 3. 声明输出 dtype 一致性(y.dtype = x.dtype;DT_UNDEFINED 跳过); | ||
| 92 | -// 4. 声明 format 校验(x/y 仅 ND;FORMAT_RESERVED 未声明跳过); | ||
| 93 | -// 5. 恒等推导:y ← x(shape + dtype,语义对齐内置 OneInOneOutDynamicInfer)。 | ||
| 94 | -// --------------------------------------------------------------------------- | ||
| 95 | -static ge::graphStatus L2NormalizeGraphInferFunc(ge::Operator& op) | ||
| 96 | { | 42 | { |
| 97 | - const ge::TensorDesc xDesc = op.GetInputDescByName("x"); | 43 | + const auto x = op.GetInputDesc("x"); |
| 98 | - const ge::Shape xShape = xDesc.GetShape(); | 44 | + const auto y = op.GetOutputDesc("y"); |
| 99 | - const ge::DataType xDtype = xDesc.GetDataType(); | 45 | + const auto shape = x.GetShape(); |
| 100 | - | 46 | + if (shape.GetDims() != UNKNOWN_RANK && (shape.GetDimNum() == 0U || shape.GetDimNum() > 8U)) { |
| 101 | - ge::TensorDesc yDesc = op.GetOutputDescByName("y"); | 47 | + OP_LOGE("L2Normalize", "x rank must be in [1, 8]."); |
| 102 | - const ge::Shape yShape = yDesc.GetShape(); | 48 | + return GRAPH_FAILED; |
| 103 | - const ge::DataType yDtype = yDesc.GetDataType(); | ||
| 104 | - | ||
| 105 | - // 未知秩标记(GE_UNKNOWN_RANK):步骤 1/2 的维数相关校验对 rank=-2 无意义,整体跳过 | ||
| 106 | - const bool isUnknownRank = (xShape.GetDims() == ge::UNKNOWN_RANK); | ||
| 107 | - | ||
| 108 | - // 1. rank-0 标量拒绝(契约 rank 1–8;GE 图管线会把 rank-0 规整为 (1) 后才到 | ||
| 109 | - // tiling,故该门禁须在推导期拦截)。标量判定按 ge::Shape 官方推荐 | ||
| 110 | - // GetDims().empty()(GetDimNum()==0 与未知秩取值冲突,见步骤 0)。 | ||
| 111 | - if (!isUnknownRank && (xShape.GetDims().empty() || xShape.GetDimNum() > MAX_INPUT_RANK)) { | ||
| 112 | - OP_LOGE_FOR_INVALID_SHAPEDIM("L2Normalize", "rank", std::to_string(xShape.GetDimNum()).c_str(), "[1, 8]"); | ||
| 113 | - return ge::GRAPH_FAILED; | ||
| 114 | } | 49 | } |
| 115 | - | 50 | + if (!DeclaredShapeCompatible(y.GetShape(), shape)) { |
| 116 | - // 2. 声明输出 shape 一致性(host 侧硬门禁;缺省会落设备写坏 out 缓冲); | 51 | + OP_LOGE("L2Normalize", "Declared y shape must match x shape."); |
| 117 | - // 未知秩无维可校,跳过(y 由步骤 5 恒等复制未知秩) | 52 | + return GRAPH_FAILED; |
| 118 | - if (!isUnknownRank && yShape.GetDimNum() != 0U && !DeclaredShapeCompatible(yShape, xShape)) { | ||
| 119 | - OP_LOGE_FOR_INVALID_SHAPE("L2Normalize", "y", ShapeToString(yShape).c_str(), ShapeToString(xShape).c_str()); | ||
| 120 | - return ge::GRAPH_FAILED; | ||
| 121 | } | 53 | } |
| 122 | - | 54 | + if (y.GetDataType() != DT_UNDEFINED && y.GetDataType() != x.GetDataType()) { |
| 123 | - // 3. 声明输出 dtype 一致性(y 与 x 恒一致,same_as_first_input) | 55 | + OP_LOGE("L2Normalize", "Declared y dtype must match x dtype."); |
| 124 | - if (yDtype != ge::DT_UNDEFINED && yDtype != xDtype) { | 56 | + return GRAPH_FAILED; |
| 125 | - OP_LOGE_FOR_INVALID_DTYPE("L2Normalize", "y", ge::TypeUtils::DataTypeToSerialString(yDtype).c_str(), | ||
| 126 | - ge::TypeUtils::DataTypeToSerialString(xDtype).c_str()); | ||
| 127 | - return ge::GRAPH_FAILED; | ||
| 128 | } | 57 | } |
| 129 | - | 58 | + if ((x.GetFormat() != FORMAT_ND && x.GetFormat() != FORMAT_RESERVED) || |
| 130 | - // 4. 声明 format 校验:x / y 仅支持 ND;未声明(FORMAT_RESERVED)不校验。y 侧 format 缺该 | 59 | + (y.GetFormat() != FORMAT_ND && y.GetFormat() != FORMAT_RESERVED)) { |
| 131 | - // 门禁时 FRACTAL_NZ 声明会落入图执行(tiling 仅校验 x 侧 format)。 | 60 | + OP_LOGE("L2Normalize", "x and y must use ND format."); |
| 132 | - if (xDesc.GetFormat() != ge::FORMAT_ND && xDesc.GetFormat() != ge::FORMAT_RESERVED) { | 61 | + return GRAPH_FAILED; |
| 133 | - OP_LOGE_FOR_INVALID_FORMAT("L2Normalize", "x", std::to_string(static_cast<int64_t>(xDesc.GetFormat())).c_str(), | ||
| 134 | - "ND"); | ||
| 135 | - return ge::GRAPH_FAILED; | ||
| 136 | } | 62 | } |
| 137 | - if (yDesc.GetFormat() != ge::FORMAT_ND && yDesc.GetFormat() != ge::FORMAT_RESERVED) { | 63 | + return GRAPH_SUCCESS; |
| 138 | - OP_LOGE_FOR_INVALID_FORMAT("L2Normalize", "y", std::to_string(static_cast<int64_t>(yDesc.GetFormat())).c_str(), | ||
| 139 | - "ND"); | ||
| 140 | - return ge::GRAPH_FAILED; | ||
| 141 | - } | ||
| 142 | - | ||
| 143 | - // 5. 恒等推导:y.shape = x.shape、y.dtype = x.dtype(format 不改写) | ||
| 144 | - yDesc.SetShape(xShape); | ||
| 145 | - yDesc.SetDataType(xDtype); | ||
| 146 | - if (op.UpdateOutputDesc("y", yDesc) != ge::GRAPH_SUCCESS) { | ||
| 147 | - OP_LOGE("L2Normalize", "Update output desc y failed."); | ||
| 148 | - return ge::GRAPH_FAILED; | ||
| 149 | - } | ||
| 150 | - return ge::GRAPH_SUCCESS; | ||
| 151 | } | 64 | } |
| 152 | - | 65 | +VERIFY_FUNC_REG(L2Normalize, VerifyL2Normalize); |
| 153 | -} // namespace ops | 66 | +} // namespace ge |
| 154 | - | ||
| 155 | -// V1 推导注册(与 canndev 内置 nn_batch_norm_ops.cc 的 COMMON_INFER_FUNC_REG 同机制; | ||
| 156 | -// 本包 custom 路径先加载,占据同名 V1 槽位) | ||
| 157 | -COMMON_INFER_FUNC_REG(L2Normalize, ops::L2NormalizeGraphInferFunc); | ||
| 158 | 67 | ||
| 159 | namespace ops { | 68 | namespace ops { |
| 69 | +namespace { | ||
| 70 | +constexpr size_t INPUT_X_IDX = 0; | ||
| 71 | +constexpr size_t OUTPUT_Y_IDX = 0; | ||
| 72 | +} // namespace | ||
| 160 | 73 | ||
| 161 | -// --------------------------------------------------------------------------- | 74 | +ge::graphStatus InferDataTypeForL2Normalize(gert::InferDataTypeContext* context) |
| 162 | -// V2 InferDataType(gert 新式推导,动态图执行期 executor 调用): | ||
| 163 | -// y.dtype = x.dtype(same_as_first_input 单输入恒等,无接口级 | ||
| 164 | -// promotion;fp16 内部 fp32 提升不改变输出 dtype,推导不感知)。dtype 合法域 | ||
| 165 | -// ({DT_FLOAT16, DT_FLOAT})由 OpDef/REG_OP TensorType 声明承载,本函数仅做 | ||
| 166 | -// 恒等透传,不做值域校验。 | ||
| 167 | -// --------------------------------------------------------------------------- | ||
| 168 | -static ge::graphStatus InferDataTypeForL2Normalize(gert::InferDataTypeContext* context) | ||
| 169 | { | 75 | { |
| 170 | - context->SetOutputDataType(OUTPUT_Y_IDX, context->GetInputDataType(INPUT_X_IDX)); | 76 | + if (context == nullptr) { |
| 171 | - return ge::GRAPH_SUCCESS; | 77 | + return ge::GRAPH_FAILED; |
| 78 | + } | ||
| 79 | + return context->SetOutputDataType(OUTPUT_Y_IDX, context->GetInputDataType(INPUT_X_IDX)); | ||
| 172 | } | 80 | } |
| 173 | 81 | ||
| 174 | -// IMPL_OP(L2Normalize).InferDataType(func): | ||
| 175 | -// Registers InferDataTypeForL2Normalize as the type inference function | ||
| 176 | -// for the L2Normalize operator type. | ||
| 177 | IMPL_OP(L2Normalize).InferDataType(InferDataTypeForL2Normalize); | 82 | IMPL_OP(L2Normalize).InferDataType(InferDataTypeForL2Normalize); |
| 178 | } // namespace ops | 83 | } // namespace ops |
| @@ -13,14 +13,13 @@ | |||
| 13 | // ============================================================================= | 13 | // ============================================================================= |
| 14 | // | 14 | // |
| 15 | // ROLE: V2 shape inference (gert) for the L2Normalize operator. | 15 | // ROLE: V2 shape inference (gert) for the L2Normalize operator. |
| 16 | -// 动态图执行期由 gert executor 直接调用(编译期图推导走 op_graph/ 的 V1 注册, | 16 | +// 图编译和动态图执行均使用 gert InferShape 注册。 |
| 17 | -// 两条路径规则一致)。 | ||
| 18 | // - y.shape = x.shape 恒等复制(动态 -1 维、空 0 维一并透传,纯结构级映射, | 17 | // - y.shape = x.shape 恒等复制(动态 -1 维、空 0 维一并透传,纯结构级映射, |
| 19 | // 不读属性); | 18 | // 不读属性); |
| 20 | // - rank-0 标量拒绝(契约 rank 1–8):GE 图管线会把 rank-0 规整为 (1) 后才到 | 19 | // - rank-0 标量拒绝(契约 rank 1–8):GE 图管线会把 rank-0 规整为 (1) 后才到 |
| 21 | // tiling,动态执行期须在此拦截(GEIR L2 负向 dimval 场景)。 | 20 | // tiling,动态执行期须在此拦截(GEIR L2 负向 dimval 场景)。 |
| 22 | -// - dtype/format/属性值域校验不在本函数职责内(OpDef TensorType / V1 推导 / | 21 | +// - 输入/输出 ND format 在此校验;原始图声明由 op_graph 的 Verify 校验。 |
| 23 | -// tiling 硬门禁承载)。 | 22 | +// dtype 推导位于 op_graph,属性值域校验由 tiling 承载。 |
| 24 | // | 23 | // |
| 25 | // CONTENTS: | 24 | // CONTENTS: |
| 26 | // - InferShape4L2Normalize() — rank-0 gate + identity shape copy | 25 | // - InferShape4L2Normalize() — rank-0 gate + identity shape copy |
| @@ -42,6 +41,12 @@ static constexpr size_t INPUT_X_IDX = 0; | |||
| 42 | static constexpr size_t OUTPUT_Y_IDX = 0; | 41 | static constexpr size_t OUTPUT_Y_IDX = 0; |
| 43 | static constexpr size_t MAX_INPUT_RANK = 8; | 42 | static constexpr size_t MAX_INPUT_RANK = 8; |
| 44 | 43 | ||
| 44 | +static bool IsSupportedFormat(const gert::CompileTimeTensorDesc& desc) | ||
| 45 | +{ | ||
| 46 | + const auto format = desc.GetStorageFormat(); | ||
| 47 | + return format == ge::FORMAT_ND || format == ge::FORMAT_RESERVED; | ||
| 48 | +} | ||
| 49 | + | ||
| 45 | // --------------------------------------------------------------------------- | 50 | // --------------------------------------------------------------------------- |
| 46 | // InferShape4L2Normalize(context) — shape inference function | 51 | // InferShape4L2Normalize(context) — shape inference function |
| 47 | // | 52 | // |
| @@ -56,6 +61,9 @@ static constexpr size_t MAX_INPUT_RANK = 8; | |||
| 56 | // --------------------------------------------------------------------------- | 61 | // --------------------------------------------------------------------------- |
| 57 | static ge::graphStatus InferShape4L2Normalize(gert::InferShapeContext* context) | 62 | static ge::graphStatus InferShape4L2Normalize(gert::InferShapeContext* context) |
| 58 | { | 63 | { |
| 64 | + if (context == nullptr) { | ||
| 65 | + return ge::GRAPH_FAILED; | ||
| 66 | + } | ||
| 59 | // GetInputShape(0): reads the shape of the first input (index 0 = x) | 67 | // GetInputShape(0): reads the shape of the first input (index 0 = x) |
| 60 | const gert::Shape* input_shape = context->GetInputShape(INPUT_X_IDX); | 68 | const gert::Shape* input_shape = context->GetInputShape(INPUT_X_IDX); |
| 61 | OP_CHECK_NULL_WITH_CONTEXT(context, input_shape); | 69 | OP_CHECK_NULL_WITH_CONTEXT(context, input_shape); |
| @@ -64,6 +72,12 @@ static ge::graphStatus InferShape4L2Normalize(gert::InferShapeContext* context) | |||
| 64 | gert::Shape* output_shape = context->GetOutputShape(OUTPUT_Y_IDX); | 72 | gert::Shape* output_shape = context->GetOutputShape(OUTPUT_Y_IDX); |
| 65 | OP_CHECK_NULL_WITH_CONTEXT(context, output_shape); | 73 | OP_CHECK_NULL_WITH_CONTEXT(context, output_shape); |
| 66 | 74 | ||
| 75 | + const auto* inputDesc = context->GetInputDesc(INPUT_X_IDX); | ||
| 76 | + const auto* outputDesc = context->GetOutputDesc(OUTPUT_Y_IDX); | ||
| 77 | + OP_CHECK_NULL_WITH_CONTEXT(context, inputDesc); | ||
| 78 | + OP_CHECK_NULL_WITH_CONTEXT(context, outputDesc); | ||
| 79 | + OP_CHECK_IF(!IsSupportedFormat(*inputDesc) || !IsSupportedFormat(*outputDesc), | ||
| 80 | + OP_LOGE(context, "x and y must use ND format."), return ge::GRAPH_FAILED); | ||
| 67 | // 动态 rank 使用框架标准标记透传,避免把 {-2} 当成普通的一维 shape 参与后续 rank 校验。 | 81 | // 动态 rank 使用框架标准标记透传,避免把 {-2} 当成普通的一维 shape 参与后续 rank 校验。 |
| 68 | if (Ops::Base::IsUnknownRank(*input_shape)) { | 82 | if (Ops::Base::IsUnknownRank(*input_shape)) { |
| 69 | Ops::Base::SetUnknownRank(*output_shape); | 83 | Ops::Base::SetUnknownRank(*output_shape); |
| @@ -0,0 +1,11 @@ | |||
| 1 | +testcase_name,network_name,op_name,input_shapes,input_dtypes,input_formats,output_shapes,output_dtypes,output_formats,input_ori_shapes,input_ori_formats,output_ori_shapes,output_ori_formats,attributes,input_data_ranges,precision_tolerances,absolute_precision,output_inplace_indexes,output_shape_unknown_indexes,is_enabled,remark,soc_series,priority,dump_file_prefix,manual_input_binaries,manual_golden_binaries | ||
| 2 | +l2_registry20_rank1_fp16,UNKNOWN,l2_normalize,"((17,),)","('float16',)","('ND',)","((17,),)","('float16',)","('ND',)","((17,),)","('ND',)","((17,),)","('ND',)","{'axis': [-1], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 3 | +l2_registry20_rank2_fp32,UNKNOWN,l2_normalize,"((3, 17),)","('float32',)","('ND',)","((3, 17),)","('float32',)","('ND',)","((3, 17),)","('ND',)","((3, 17),)","('ND',)","{'axis': [1], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 4 | +l2_registry20_multi_axis_fp32,UNKNOWN,l2_normalize,"((2, 3, 5),)","('float32',)","('ND',)","((2, 3, 5),)","('float32',)","('ND',)","((2, 3, 5),)","('ND',)","((2, 3, 5),)","('ND',)","{'axis': [0, 2], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 5 | +l2_registry20_empty_axis_fp16,UNKNOWN,l2_normalize,"((2, 3, 2, 5),)","('float16',)","('ND',)","((2, 3, 2, 5),)","('float16',)","('ND',)","((2, 3, 2, 5),)","('ND',)","((2, 3, 2, 5),)","('ND',)","{'axis': [], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 6 | +l2_registry20_rank8_fp32,UNKNOWN,l2_normalize,"((2, 1, 2, 1, 2, 1, 2, 3),)","('float32',)","('ND',)","((2, 1, 2, 1, 2, 1, 2, 3),)","('float32',)","('ND',)","((2, 1, 2, 1, 2, 1, 2, 3),)","('ND',)","((2, 1, 2, 1, 2, 1, 2, 3),)","('ND',)","{'axis': [-1], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 7 | +l2_registry20_strided_fp16,UNKNOWN,l2_normalize,"((3, 7, 5),)","('float16',)","('ND',)","((3, 7, 5),)","('float16',)","('ND',)","((3, 7, 5),)","('ND',)","((3, 7, 5),)","('ND',)","{'axis': [1], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 8 | +l2_registry20_eps_clamp_fp32,UNKNOWN,l2_normalize,"((2, 5),)","('float32',)","('ND',)","((2, 5),)","('float32',)","('ND',)","((2, 5),)","('ND',)","((2, 5),)","('ND',)","{'axis': [-1], 'eps': 100.0}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 9 | +l2_registry20_duplicate_axis_fp16,UNKNOWN,l2_normalize,"((2, 3, 5),)","('float16',)","('ND',)","((2, 3, 5),)","('float16',)","('ND',)","((2, 3, 5),)","('ND',)","((2, 3, 5),)","('ND',)","{'axis': [-1, 2, -1], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 10 | +l2_registry20_empty_rank1_fp32,UNKNOWN,l2_normalize,"((0,),)","('float32',)","('ND',)","((0,),)","('float32',)","('ND',)","((0,),)","('ND',)","((0,),)","('ND',)","{'axis': [0], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| 11 | +l2_registry20_empty_rank2_fp16,UNKNOWN,l2_normalize,"((0, 0),)","('float16',)","('ND',)","((0, 0),)","('float16',)","('ND',)","((0, 0),)","('ND',)","((0, 0),)","('ND',)","{'axis': [1], 'eps': 0.0001}","[[-1, 1]]","((0.0009765625, 0.0009765625),)",1.52587890625e-5,(),(),1,Runtime 2.0 inference regression; ND shape and dtype preservation,,,,(),() | ||
| @@ -0,0 +1,11 @@ | |||
| 1 | +# Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 2 | +# This program is free software; you can redistribute it and/or modify it under the terms and conditions of | ||
| 3 | +# CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 4 | +# Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 5 | +# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 6 | +# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 7 | +# See LICENSE in the root of the software repository for the full text of the License. | ||
| 8 | + | ||
| 9 | +if(UT_TEST_ALL OR OP_GRAPH_UT) | ||
| 10 | + add_modules_ut_sources(HOSTNAME ${OP_GRAPH_MODULE_NAME} MODE PRIVATE DIR ${CMAKE_CURRENT_SOURCE_DIR}) | ||
| 11 | +endif() | ||
| @@ -0,0 +1,114 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software; you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | +#include <gtest/gtest.h> | ||
| 12 | + | ||
| 13 | +#include "base/context_builder/op_infer_datatype_context_builder.h" | ||
| 14 | +#include "../../../op_graph/l2_normalize_proto.h" | ||
| 15 | + | ||
| 16 | +namespace ops { | ||
| 17 | +ge::graphStatus InferDataTypeForL2Normalize(gert::InferDataTypeContext* context); | ||
| 18 | +} // namespace ops | ||
| 19 | + | ||
| 20 | +namespace { | ||
| 21 | +gert::ContextHolder<gert::InferDataTypeContext> BuildContext(ge::DataType inputType) | ||
| 22 | +{ | ||
| 23 | + gert::OpInferDataTypeContextBuilder builder; | ||
| 24 | + builder.OpType("L2Normalize").OpName("L2Normalize"); | ||
| 25 | + builder.IONum(1, 1); | ||
| 26 | + builder.InputTensorDesc(0, inputType, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 27 | + builder.OutputTensorDesc(0, ge::FORMAT_ND, ge::FORMAT_ND); | ||
| 28 | + return builder.Build(); | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +ge::graphStatus RunVerify(const ge::Shape& inputShape, const ge::Shape& outputShape, | ||
| 32 | + ge::DataType inputType = ge::DT_FLOAT, ge::DataType outputType = ge::DT_FLOAT, | ||
| 33 | + ge::Format inputFormat = ge::FORMAT_ND, ge::Format outputFormat = ge::FORMAT_ND) | ||
| 34 | +{ | ||
| 35 | + ge::op::L2Normalize op("l2_normalize_verify_ut"); | ||
| 36 | + EXPECT_EQ(op.UpdateInputDesc("x", ge::TensorDesc(inputShape, inputFormat, inputType)), ge::GRAPH_SUCCESS); | ||
| 37 | + EXPECT_EQ(op.UpdateOutputDesc("y", ge::TensorDesc(outputShape, outputFormat, outputType)), ge::GRAPH_SUCCESS); | ||
| 38 | + return op.VerifyAllAttr(true); | ||
| 39 | +} | ||
| 40 | + | ||
| 41 | +} // namespace | ||
| 42 | + | ||
| 43 | +TEST(L2NormalizeGraphInfer, CopiesInputDataType) | ||
| 44 | +{ | ||
| 45 | + for (auto dtype : {ge::DT_FLOAT16, ge::DT_FLOAT}) { | ||
| 46 | + auto holder = BuildContext(dtype); | ||
| 47 | + auto* context = holder.GetContext(); | ||
| 48 | + ASSERT_NE(context, nullptr); | ||
| 49 | + ASSERT_EQ(context->SetOutputDataType(0, ge::DT_UNDEFINED), ge::GRAPH_SUCCESS); | ||
| 50 | + ASSERT_EQ(ops::InferDataTypeForL2Normalize(context), ge::GRAPH_SUCCESS); | ||
| 51 | + EXPECT_EQ(context->GetOutputDataType(0), dtype); | ||
| 52 | + EXPECT_EQ(ops::InferDataTypeForL2Normalize(context), ge::GRAPH_SUCCESS); | ||
| 53 | + } | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +TEST(L2NormalizeGraphInfer, OverwritesReusedOutputDataType) | ||
| 57 | +{ | ||
| 58 | + auto holder = BuildContext(ge::DT_FLOAT16); | ||
| 59 | + auto* context = holder.GetContext(); | ||
| 60 | + ASSERT_NE(context, nullptr); | ||
| 61 | + ASSERT_EQ(context->SetOutputDataType(0, ge::DT_FLOAT), ge::GRAPH_SUCCESS); | ||
| 62 | + ASSERT_EQ(ops::InferDataTypeForL2Normalize(context), ge::GRAPH_SUCCESS); | ||
| 63 | + EXPECT_EQ(context->GetOutputDataType(0), ge::DT_FLOAT16); | ||
| 64 | +} | ||
| 65 | + | ||
| 66 | +TEST(L2NormalizeGraphInfer, RejectsNullContext) | ||
| 67 | +{ | ||
| 68 | + EXPECT_EQ(ops::InferDataTypeForL2Normalize(nullptr), ge::GRAPH_FAILED); | ||
| 69 | +} | ||
| 70 | + | ||
| 71 | +TEST(L2NormalizeGraphVerify, RejectsConflictingDeclaredShape) | ||
| 72 | +{ | ||
| 73 | + EXPECT_EQ(RunVerify(ge::Shape({2, 3}), ge::Shape({2, 4})), ge::GRAPH_FAILED); | ||
| 74 | + EXPECT_EQ(RunVerify(ge::Shape({2, 3}), ge::Shape({6})), ge::GRAPH_FAILED); | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +TEST(L2NormalizeGraphVerify, RejectsConflictingDeclaredDataType) | ||
| 78 | +{ | ||
| 79 | + EXPECT_EQ(RunVerify(ge::Shape({17}), ge::Shape({17}), ge::DT_FLOAT16, ge::DT_FLOAT), ge::GRAPH_FAILED); | ||
| 80 | +} | ||
| 81 | + | ||
| 82 | +TEST(L2NormalizeGraphVerify, RejectsNonNDFormats) | ||
| 83 | +{ | ||
| 84 | + EXPECT_EQ(RunVerify(ge::Shape({2, 3}), ge::Shape({2, 3}), ge::DT_FLOAT, ge::DT_FLOAT, ge::FORMAT_NCHW), | ||
| 85 | + ge::GRAPH_FAILED); | ||
| 86 | + EXPECT_EQ(RunVerify(ge::Shape({2, 3}), ge::Shape({2, 3}), ge::DT_FLOAT, ge::DT_FLOAT, ge::FORMAT_ND, | ||
| 87 | + ge::FORMAT_FRACTAL_NZ), | ||
| 88 | + ge::GRAPH_FAILED); | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +TEST(L2NormalizeGraphVerify, RejectsScalarAndRankAboveEight) | ||
| 92 | +{ | ||
| 93 | + EXPECT_EQ(RunVerify(ge::Shape(std::vector<int64_t>{}), ge::Shape(std::vector<int64_t>{})), ge::GRAPH_FAILED); | ||
| 94 | + EXPECT_EQ(RunVerify(ge::Shape({1, 1, 1, 1, 1, 1, 1, 1, 1}), ge::Shape({1, 1, 1, 1, 1, 1, 1, 1, 1})), | ||
| 95 | + ge::GRAPH_FAILED); | ||
| 96 | +} | ||
| 97 | + | ||
| 98 | +TEST(L2NormalizeGraphVerify, AcceptsCompatibleAndUndeclaredOutputs) | ||
| 99 | +{ | ||
| 100 | + EXPECT_EQ(RunVerify(ge::Shape({2, 3}), ge::Shape({2, -1})), ge::GRAPH_SUCCESS); | ||
| 101 | + EXPECT_EQ(RunVerify(ge::Shape({17}), ge::Shape(std::vector<int64_t>{}), ge::DT_FLOAT16, ge::DT_UNDEFINED), | ||
| 102 | + ge::GRAPH_SUCCESS); | ||
| 103 | + EXPECT_EQ(RunVerify(ge::Shape({17}), ge::Shape({17}), ge::DT_FLOAT16, ge::DT_FLOAT16, ge::FORMAT_RESERVED, | ||
| 104 | + ge::FORMAT_RESERVED), | ||
| 105 | + ge::GRAPH_SUCCESS); | ||
| 106 | +} | ||
| 107 | + | ||
| 108 | +TEST(L2NormalizeGraphVerify, AcceptsUnknownRankAndEmptyInputs) | ||
| 109 | +{ | ||
| 110 | + EXPECT_EQ(RunVerify(ge::Shape(ge::UNKNOWN_RANK), ge::Shape({2, 3})), ge::GRAPH_SUCCESS); | ||
| 111 | + EXPECT_EQ(RunVerify(ge::Shape({2, 3}), ge::Shape(ge::UNKNOWN_RANK)), ge::GRAPH_SUCCESS); | ||
| 112 | + EXPECT_EQ(RunVerify(ge::Shape({0}), ge::Shape({0})), ge::GRAPH_SUCCESS); | ||
| 113 | + EXPECT_EQ(RunVerify(ge::Shape({0, 0}), ge::Shape({0, 0})), ge::GRAPH_SUCCESS); | ||
| 114 | +} | ||
| @@ -17,7 +17,8 @@ namespace { | |||
| 17 | 17 | ||
| 18 | constexpr const char* OP_TYPE = "L2Normalize"; | 18 | constexpr const char* OP_TYPE = "L2Normalize"; |
| 19 | 19 | ||
| 20 | -ge::graphStatus RunInferShape(gert::Shape& inputShape, gert::Shape& outputShape) | 20 | +ge::graphStatus RunInferShape(gert::Shape& inputShape, gert::Shape& outputShape, ge::Format inputFormat = ge::FORMAT_ND, |
| 21 | + ge::Format outputFormat = ge::FORMAT_ND) | ||
| 21 | { | 22 | { |
| 22 | const auto* opImpl = gert::OpImplRegistry::GetInstance().GetOpImpl(OP_TYPE); | 23 | const auto* opImpl = gert::OpImplRegistry::GetInstance().GetOpImpl(OP_TYPE); |
| 23 | if (opImpl == nullptr || opImpl->infer_shape == nullptr) { | 24 | if (opImpl == nullptr || opImpl->infer_shape == nullptr) { |
| @@ -28,10 +29,17 @@ ge::graphStatus RunInferShape(gert::Shape& inputShape, gert::Shape& outputShape) | |||
| 28 | .SetOpType(OP_TYPE) | 29 | .SetOpType(OP_TYPE) |
| 29 | .NodeIoNum(1, 1) | 30 | .NodeIoNum(1, 1) |
| 30 | .IrInstanceNum({1}, {1}) | 31 | .IrInstanceNum({1}, {1}) |
| 32 | + .NodeInputTd(0, ge::DT_FLOAT, inputFormat, inputFormat) | ||
| 33 | + .NodeOutputTd(0, ge::DT_FLOAT, outputFormat, outputFormat) | ||
| 31 | .InputShapes({&inputShape}) | 34 | .InputShapes({&inputShape}) |
| 32 | .OutputShapes({&outputShape}) | 35 | .OutputShapes({&outputShape}) |
| 33 | .Build(); | 36 | .Build(); |
| 34 | auto* context = holder.GetContext<gert::InferShapeContext>(); | 37 | auto* context = holder.GetContext<gert::InferShapeContext>(); |
| 38 | + if (context == nullptr || context->GetOutputShape(0) == nullptr) { | ||
| 39 | + return ge::GRAPH_FAILED; | ||
| 40 | + } | ||
| 41 | + // The context builder allocates output shapes; OutputShapes() does not initialize them. | ||
| 42 | + *context->GetOutputShape(0) = outputShape; | ||
| 35 | const ge::graphStatus status = opImpl->infer_shape(context); | 43 | const ge::graphStatus status = opImpl->infer_shape(context); |
| 36 | if (status == ge::GRAPH_SUCCESS && context != nullptr && context->GetOutputShape(0) != nullptr) { | 44 | if (status == ge::GRAPH_SUCCESS && context != nullptr && context->GetOutputShape(0) != nullptr) { |
| 37 | outputShape = *context->GetOutputShape(0); | 45 | outputShape = *context->GetOutputShape(0); |
| @@ -82,6 +90,41 @@ TEST(L2NormalizeInferShape, RejectsScalarAndRankAboveEight) | |||
| 82 | EXPECT_EQ(RunInferShape(rankNineShape, rankNineOutput), ge::GRAPH_FAILED); | 90 | EXPECT_EQ(RunInferShape(rankNineShape, rankNineOutput), ge::GRAPH_FAILED); |
| 83 | } | 91 | } |
| 84 | 92 | ||
| 93 | +TEST(L2NormalizeInferShape, OverwritesReusedOutputShape) | ||
| 94 | +{ | ||
| 95 | + gert::Shape inputShape = {2, 3}; | ||
| 96 | + gert::Shape previousOutput = {2, 4}; | ||
| 97 | + ASSERT_EQ(RunInferShape(inputShape, previousOutput), ge::GRAPH_SUCCESS); | ||
| 98 | + EXPECT_EQ(ShapeToVector(previousOutput), (std::vector<int64_t>{2, 3})); | ||
| 99 | + gert::Shape newInput = {6}; | ||
| 100 | + ASSERT_EQ(RunInferShape(newInput, previousOutput), ge::GRAPH_SUCCESS); | ||
| 101 | + EXPECT_EQ(ShapeToVector(previousOutput), (std::vector<int64_t>{6})); | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +TEST(L2NormalizeInferShape, OverwritesPartialAndUnknownOutputShapes) | ||
| 105 | +{ | ||
| 106 | + gert::Shape inputShape = {2, 3}; | ||
| 107 | + gert::Shape declaredShape = {2, -1}; | ||
| 108 | + ASSERT_EQ(RunInferShape(inputShape, declaredShape), ge::GRAPH_SUCCESS); | ||
| 109 | + EXPECT_EQ(ShapeToVector(declaredShape), (std::vector<int64_t>{2, 3})); | ||
| 110 | + gert::Shape unknownOutput = {-2}; | ||
| 111 | + ASSERT_EQ(RunInferShape(inputShape, unknownOutput), ge::GRAPH_SUCCESS); | ||
| 112 | + EXPECT_EQ(ShapeToVector(unknownOutput), (std::vector<int64_t>{2, 3})); | ||
| 113 | + gert::Shape unknownInput = {-2}; | ||
| 114 | + gert::Shape declaredOutput = {2, 3}; | ||
| 115 | + ASSERT_EQ(RunInferShape(unknownInput, declaredOutput), ge::GRAPH_SUCCESS); | ||
| 116 | + EXPECT_EQ(ShapeToVector(declaredOutput), (std::vector<int64_t>{-2})); | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +TEST(L2NormalizeInferShape, RejectsNonNDInputAndOutputFormats) | ||
| 120 | +{ | ||
| 121 | + gert::Shape inputShape = {2, 3}; | ||
| 122 | + gert::Shape outputShape = {}; | ||
| 123 | + EXPECT_EQ(RunInferShape(inputShape, outputShape, ge::FORMAT_NCHW), ge::GRAPH_FAILED); | ||
| 124 | + EXPECT_EQ(RunInferShape(inputShape, outputShape, ge::FORMAT_ND, ge::FORMAT_FRACTAL_NZ), ge::GRAPH_FAILED); | ||
| 125 | + EXPECT_EQ(RunInferShape(inputShape, outputShape, ge::FORMAT_RESERVED, ge::FORMAT_RESERVED), ge::GRAPH_SUCCESS); | ||
| 126 | +} | ||
| 127 | + | ||
| 85 | // FR-P1-011:零元素张量在 GE 动态图下以未知秩 desc(GE_UNKNOWN_RANK,dims={-2})到达 | 128 | // FR-P1-011:零元素张量在 GE 动态图下以未知秩 desc(GE_UNKNOWN_RANK,dims={-2})到达 |
| 86 | // 推导(L0_empty_027/034:data_shape=(0,)/(0,0)、desc/ori_shape=[-2]);未知秩须无条件 | 129 | // 推导(L0_empty_027/034:data_shape=(0,)/(0,0)、desc/ori_shape=[-2]);未知秩须无条件 |
| 87 | // 透传(不逐维校验),已知秩零元素 shape 同样恒等复制。 | 130 | // 透传(不逐维校验),已知秩零元素 shape 同样恒等复制。 |
| @@ -61,10 +61,10 @@ | |||
| 61 | | accum | 输入 | 动量累积量,必须为可写Tensor;shape、数据类型和数据格式与`parameters`一致。 | FLOAT、FLOAT16、BFLOAT16 | NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D | | 61 | | accum | 输入 | 动量累积量,必须为可写Tensor;shape、数据类型和数据格式与`parameters`一致。 | FLOAT、FLOAT16、BFLOAT16 | NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D | |
| 62 | | momentum | 输入 | 动量因子,元素个数必须为1,数据类型与`parameters`一致。 | FLOAT、FLOAT16、BFLOAT16 | ND | | 62 | | momentum | 输入 | 动量因子,元素个数必须为1,数据类型与`parameters`一致。 | FLOAT、FLOAT16、BFLOAT16 | ND | |
| 63 | | stat | 输入 | 逐元素首步标记,取值1表示不施加阻尼;必须为可写Tensor,shape、数据类型和数据格式与`parameters`一致。 | FLOAT、FLOAT16、BFLOAT16 | NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D | | 63 | | stat | 输入 | 逐元素首步标记,取值1表示不施加阻尼;必须为可写Tensor,shape、数据类型和数据格式与`parameters`一致。 | FLOAT、FLOAT16、BFLOAT16 | NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D | |
| 64 | +| parameters | 输出 | 更新后的权重,与输入`parameters`共享内存,shape、数据类型和数据格式保持一致。 | FLOAT、FLOAT16、BFLOAT16 | NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D | | ||
| 64 | | dampening | 可选属性 | 动量阻尼系数,默认值为0.0;当`nesterov=true`时必须为0。 | FLOAT | - | | 65 | | dampening | 可选属性 | 动量阻尼系数,默认值为0.0;当`nesterov=true`时必须为0。 | FLOAT | - | |
| 65 | | weight_decay | 可选属性 | 权重衰减系数,默认值为0.0,取值必须大于或等于0。 | FLOAT | - | | 66 | | weight_decay | 可选属性 | 权重衰减系数,默认值为0.0,取值必须大于或等于0。 | FLOAT | - | |
| 66 | | nesterov | 可选属性 | 是否启用Nesterov动量,默认值为false。 | BOOL | - | | 67 | | nesterov | 可选属性 | 是否启用Nesterov动量,默认值为false。 | BOOL | - | |
| 67 | -| parameters | 输出 | 更新后的权重,与输入`parameters`共享内存,shape、数据类型和数据格式保持一致。 | FLOAT、FLOAT16、BFLOAT16 | NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D | | ||
| 68 | 68 | ||
| 69 | ### 产品差异说明 | 69 | ### 产品差异说明 |
| 70 | 70 | ||
| @@ -78,6 +78,8 @@ | |||
| 78 | 78 | ||
| 79 | - 图上仅声明一个`parameters`输出,但`parameters`、`accum`和`stat`均可能被原地更新, | 79 | - 图上仅声明一个`parameters`输出,但`parameters`、`accum`和`stat`均可能被原地更新, |
| 80 | 调用方必须将这三个输入作为可写Tensor。 | 80 | 调用方必须将这三个输入作为可写Tensor。 |
| 81 | +- <term>Ascend 950PR&950DT系列产品</term>使用FLOAT进行中间计算,FLOAT16、BFLOAT16结果按就近偶数舍入转换回输入数据类型。 | ||
| 82 | +- `momentum=0`仅阻止`accum`和`stat`回写,不跳过权重更新中的动量乘法;当`accum`含Inf时,`0×Inf`产生的NaN会传播到权重结果。 | ||
| 81 | 83 | ||
| 82 | ## 调用说明 | 84 | ## 调用说明 |
| 83 | 85 | ||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include <vector> | 33 | #include <vector> |
| 34 | #include <string> | 34 | #include <string> |
| 35 | #include <map> | 35 | #include <map> |
| 36 | +#include <cmath> | ||
| 36 | #include "assert.h" | 37 | #include "assert.h" |
| 37 | #include "graph.h" | 38 | #include "graph.h" |
| 38 | #include "types.h" | 39 | #include "types.h" |
| @@ -53,7 +54,7 @@ using std::string; | |||
| 53 | using std::vector; | 54 | using std::vector; |
| 54 | #define ADD_INPUT(intputIndex, intputName, intputDtype, inputShape, inputValue) \ | 55 | #define ADD_INPUT(intputIndex, intputName, intputDtype, inputShape, inputValue) \ |
| 55 | vector<int64_t> placeholder##intputIndex##_shape = inputShape; \ | 56 | vector<int64_t> placeholder##intputIndex##_shape = inputShape; \ |
| 56 | - auto placeholder##intputIndex = op::Data("placeholder" + intputIndex).set_attr_index(0); \ | 57 | + auto placeholder##intputIndex = op::Data("placeholder" + intputIndex).set_attr_index(intputIndex - 1); \ |
| 57 | TensorDesc placeholder##intputIndex##_desc = TensorDesc(ge::Shape(placeholder##intputIndex##_shape), FORMAT_ND, \ | 58 | TensorDesc placeholder##intputIndex##_desc = TensorDesc(ge::Shape(placeholder##intputIndex##_shape), FORMAT_ND, \ |
| 58 | intputDtype); \ | 59 | intputDtype); \ |
| 59 | placeholder##intputIndex##_desc.SetPlacement(ge::kPlacementHost); \ | 60 | placeholder##intputIndex##_desc.SetPlacement(ge::kPlacementHost); \ |
| @@ -155,11 +156,15 @@ int32_t GenOnesDataFloat32(vector<int64_t> shapes, Tensor& input_tensor, TensorD | |||
| 155 | uint32_t byteSizeFloat32 = 4; | 156 | uint32_t byteSizeFloat32 = 4; |
| 156 | uint32_t data_len = size * byteSizeFloat32; | 157 | uint32_t data_len = size * byteSizeFloat32; |
| 157 | float* pData = new (std::nothrow) float[size]; | 158 | float* pData = new (std::nothrow) float[size]; |
| 159 | + if (pData == nullptr) { | ||
| 160 | + return FAILED; | ||
| 161 | + } | ||
| 158 | 162 | ||
| 159 | for (size_t i = 0; i < size; ++i) { | 163 | for (size_t i = 0; i < size; ++i) { |
| 160 | *(pData + i) = value; | 164 | *(pData + i) = value; |
| 161 | } | 165 | } |
| 162 | input_tensor = Tensor(input_tensor_desc, (uint8_t*)pData, data_len); | 166 | input_tensor = Tensor(input_tensor_desc, (uint8_t*)pData, data_len); |
| 167 | + delete[] pData; | ||
| 163 | return SUCCESS; | 168 | return SUCCESS; |
| 164 | } | 169 | } |
| 165 | 170 | ||
| @@ -173,19 +178,26 @@ int32_t GenOnesData(vector<int64_t> shapes, Tensor& input_tensor, TensorDesc& in | |||
| 173 | } | 178 | } |
| 174 | uint32_t data_len = size * GetDataTypeSize(data_type); | 179 | uint32_t data_len = size * GetDataTypeSize(data_type); |
| 175 | int32_t* pData = new (std::nothrow) int32_t[data_len]; | 180 | int32_t* pData = new (std::nothrow) int32_t[data_len]; |
| 181 | + if (pData == nullptr) { | ||
| 182 | + return FAILED; | ||
| 183 | + } | ||
| 176 | for (size_t i = 0; i < size; ++i) { | 184 | for (size_t i = 0; i < size; ++i) { |
| 177 | *(pData + i) = value; | 185 | *(pData + i) = value; |
| 178 | } | 186 | } |
| 179 | input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(pData), data_len); | 187 | input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(pData), data_len); |
| 188 | + delete[] pData; | ||
| 180 | return SUCCESS; | 189 | return SUCCESS; |
| 181 | } | 190 | } |
| 182 | 191 | ||
| 183 | int32_t WriteDataToFile(string bin_file, uint64_t data_size, uint8_t* inputData) | 192 | int32_t WriteDataToFile(string bin_file, uint64_t data_size, uint8_t* inputData) |
| 184 | { | 193 | { |
| 185 | - FILE* fp = fopen(bin_file.c_str(), "w"); | 194 | + FILE* fp = fopen(bin_file.c_str(), "wb"); |
| 186 | - fwrite(inputData, sizeof(uint8_t), data_size, fp); | 195 | + if (fp == nullptr) { |
| 187 | - fclose(fp); | 196 | + return FAILED; |
| 188 | - return SUCCESS; | 197 | + } |
| 198 | + const size_t written = fwrite(inputData, sizeof(uint8_t), data_size, fp); | ||
| 199 | + const int closeStatus = fclose(fp); | ||
| 200 | + return written == data_size && closeStatus == 0 ? SUCCESS : FAILED; | ||
| 189 | } | 201 | } |
| 190 | 202 | ||
| 191 | int CreateOppInGraph(DataType inDtype, float momentumValue, std::vector<ge::Tensor>& input, | 203 | int CreateOppInGraph(DataType inDtype, float momentumValue, std::vector<ge::Tensor>& input, |
| @@ -210,9 +222,41 @@ int CreateOppInGraph(DataType inDtype, float momentumValue, std::vector<ge::Tens | |||
| 210 | ADD_INPUT_ATTR(nesterov, false); | 222 | ADD_INPUT_ATTR(nesterov, false); |
| 211 | 223 | ||
| 212 | outputs.push_back(sgd1); | 224 | outputs.push_back(sgd1); |
| 225 | + // Expose the two input slots after SGD has completed its in-place writes. | ||
| 226 | + auto accumOut = op::Identity("accum_out").set_input_x(placeholder4); | ||
| 227 | + auto statOut = op::Identity("stat_out").set_input_x(placeholder6); | ||
| 228 | + accumOut.AddControlInput(sgd1); | ||
| 229 | + statOut.AddControlInput(sgd1); | ||
| 230 | + outputs.push_back(accumOut); | ||
| 231 | + outputs.push_back(statOut); | ||
| 213 | return SUCCESS; | 232 | return SUCCESS; |
| 214 | } | 233 | } |
| 215 | 234 | ||
| 235 | +bool CheckOutputs(const std::vector<ge::Tensor>& output, float momentum) | ||
| 236 | +{ | ||
| 237 | + if (output.size() != 3) { | ||
| 238 | + return false; | ||
| 239 | + } | ||
| 240 | + const double accumT = 3.5 * static_cast<double>(momentum) + 0.5; | ||
| 241 | + const double expected[] = {1.0 - accumT * 0.1, momentum != 0.0f ? accumT : 3.5, momentum != 0.0f ? 0.0 : 1.0}; | ||
| 242 | + for (size_t index = 0; index < output.size(); ++index) { | ||
| 243 | + const auto& tensor = output[index]; | ||
| 244 | + if (tensor.GetTensorDesc().GetDataType() != DT_FLOAT || | ||
| 245 | + tensor.GetTensorDesc().GetShape().GetDims() != std::vector<int64_t>({2, 30}) || | ||
| 246 | + tensor.GetSize() != 60 * sizeof(float) || tensor.GetData() == nullptr) { | ||
| 247 | + return false; | ||
| 248 | + } | ||
| 249 | + const float* values = reinterpret_cast<const float*>(tensor.GetData()); | ||
| 250 | + for (size_t i = 0; i < 60; ++i) { | ||
| 251 | + if (!std::isfinite(values[i]) || std::abs(values[i] - expected[index]) > 1e-6) { | ||
| 252 | + printf("output[%zu][%zu] = %f, expected %f\n", index, i, values[i], expected[index]); | ||
| 253 | + return false; | ||
| 254 | + } | ||
| 255 | + } | ||
| 256 | + } | ||
| 257 | + return true; | ||
| 258 | +} | ||
| 259 | + | ||
| 216 | int main(int argc, char* argv[]) | 260 | int main(int argc, char* argv[]) |
| 217 | { | 261 | { |
| 218 | const char* graph_name = "tc_ge_irrun_test"; | 262 | const char* graph_name = "tc_ge_irrun_test"; |
| @@ -246,6 +290,7 @@ int main(int argc, char* argv[]) | |||
| 246 | ret = CreateOppInGraph(inDtype, momentumValue, input, inputs, outputs, graph); | 290 | ret = CreateOppInGraph(inDtype, momentumValue, input, inputs, outputs, graph); |
| 247 | if (ret != SUCCESS) { | 291 | if (ret != SUCCESS) { |
| 248 | printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str()); | 292 | printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str()); |
| 293 | + GEFinalize(); | ||
| 249 | return FAILED; | 294 | return FAILED; |
| 250 | } | 295 | } |
| 251 | 296 | ||
| @@ -257,10 +302,11 @@ int main(int argc, char* argv[]) | |||
| 257 | 302 | ||
| 258 | }; | 303 | }; |
| 259 | printf("%s - INFO - [XIR]: Start to create ir session using build options\n", GetTime().c_str()); | 304 | printf("%s - INFO - [XIR]: Start to create ir session using build options\n", GetTime().c_str()); |
| 260 | - ge::Session* session = new Session(build_options); | 305 | + ge::Session* session = new (std::nothrow) Session(build_options); |
| 261 | 306 | ||
| 262 | if (session == nullptr) { | 307 | if (session == nullptr) { |
| 263 | printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str()); | 308 | printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str()); |
| 309 | + GEFinalize(); | ||
| 264 | return FAILED; | 310 | return FAILED; |
| 265 | } | 311 | } |
| 266 | printf("%s - INFO - [XIR]: Create ir session using build options success\n", GetTime().c_str()); | 312 | printf("%s - INFO - [XIR]: Create ir session using build options success\n", GetTime().c_str()); |
| @@ -271,6 +317,12 @@ int main(int argc, char* argv[]) | |||
| 271 | }; | 317 | }; |
| 272 | uint32_t graph_id = 0; | 318 | uint32_t graph_id = 0; |
| 273 | ret = session->AddGraph(graph_id, graph, graph_options); | 319 | ret = session->AddGraph(graph_id, graph, graph_options); |
| 320 | + if (ret != SUCCESS) { | ||
| 321 | + printf("%s - ERROR - [XIR]: AddGraph failed\n", GetTime().c_str()); | ||
| 322 | + delete session; | ||
| 323 | + GEFinalize(); | ||
| 324 | + return FAILED; | ||
| 325 | + } | ||
| 274 | 326 | ||
| 275 | printf("%s - INFO - [XIR]: Session add ir compute graph to ir session success\n", GetTime().c_str()); | 327 | printf("%s - INFO - [XIR]: Session add ir compute graph to ir session success\n", GetTime().c_str()); |
| 276 | printf("%s - INFO - [XIR]: dump graph to txt\n", GetTime().c_str()); | 328 | printf("%s - INFO - [XIR]: dump graph to txt\n", GetTime().c_str()); |
| @@ -286,6 +338,12 @@ int main(int argc, char* argv[]) | |||
| 286 | return FAILED; | 338 | return FAILED; |
| 287 | } | 339 | } |
| 288 | printf("%s - INFO - [XIR]: Session run ir compute graph success\n", GetTime().c_str()); | 340 | printf("%s - INFO - [XIR]: Session run ir compute graph success\n", GetTime().c_str()); |
| 341 | + if (!CheckOutputs(output, momentumValue)) { | ||
| 342 | + printf("SGD result verification failed\n"); | ||
| 343 | + delete session; | ||
| 344 | + GEFinalize(); | ||
| 345 | + return FAILED; | ||
| 346 | + } | ||
| 289 | 347 | ||
| 290 | int input_num = input.size(); | 348 | int input_num = input.size(); |
| 291 | for (int i = 0; i < input_num; i++) { | 349 | for (int i = 0; i < input_num; i++) { |
| @@ -295,7 +353,11 @@ int main(int argc, char* argv[]) | |||
| 295 | int64_t input_shape = input[i].GetTensorDesc().GetShape().GetShapeSize(); | 353 | int64_t input_shape = input[i].GetTensorDesc().GetShape().GetShapeSize(); |
| 296 | std::cout << "this is " << i << "th input, input shape size =" << input_shape << std::endl; | 354 | std::cout << "this is " << i << "th input, input shape size =" << input_shape << std::endl; |
| 297 | uint32_t data_size = input_shape * GetDataTypeSize(input[i].GetTensorDesc().GetDataType()); | 355 | uint32_t data_size = input_shape * GetDataTypeSize(input[i].GetTensorDesc().GetDataType()); |
| 298 | - WriteDataToFile((const char*)input_file.c_str(), data_size, input_data_i); | 356 | + if (WriteDataToFile(input_file, data_size, input_data_i) != SUCCESS) { |
| 357 | + delete session; | ||
| 358 | + GEFinalize(); | ||
| 359 | + return FAILED; | ||
| 360 | + } | ||
| 299 | } | 361 | } |
| 300 | 362 | ||
| 301 | int output_num = output.size(); | 363 | int output_num = output.size(); |
| @@ -306,7 +368,11 @@ int main(int argc, char* argv[]) | |||
| 306 | int64_t output_shape = output[i].GetTensorDesc().GetShape().GetShapeSize(); | 368 | int64_t output_shape = output[i].GetTensorDesc().GetShape().GetShapeSize(); |
| 307 | std::cout << "this is " << i << "th output, output shape size =" << output_shape << std::endl; | 369 | std::cout << "this is " << i << "th output, output shape size =" << output_shape << std::endl; |
| 308 | uint32_t data_size = output_shape * GetDataTypeSize(output[i].GetTensorDesc().GetDataType()); | 370 | uint32_t data_size = output_shape * GetDataTypeSize(output[i].GetTensorDesc().GetDataType()); |
| 309 | - WriteDataToFile((const char*)output_file.c_str(), data_size, output_data_i); | 371 | + if (WriteDataToFile(output_file, data_size, output_data_i) != SUCCESS) { |
| 372 | + delete session; | ||
| 373 | + GEFinalize(); | ||
| 374 | + return FAILED; | ||
| 375 | + } | ||
| 310 | float* resultData = (float*)output_data_i; | 376 | float* resultData = (float*)output_data_i; |
| 311 | for (int64_t j = 0; j < output_shape; j++) { | 377 | for (int64_t j = 0; j < output_shape; j++) { |
| 312 | LOG_PRINT("result[%ld] is: %f\n", j, resultData[j]); | 378 | LOG_PRINT("result[%ld] is: %f\n", j, resultData[j]); |
| @@ -319,16 +385,6 @@ int main(int argc, char* argv[]) | |||
| 319 | ge::AscendString warning_msg = ge::GEGetWarningMsgV2(); | 385 | ge::AscendString warning_msg = ge::GEGetWarningMsgV2(); |
| 320 | std::string warning_str(warning_msg.GetString()); | 386 | std::string warning_str(warning_msg.GetString()); |
| 321 | std::cout << "Warning message: " << warning_str << std::endl; | 387 | std::cout << "Warning message: " << warning_str << std::endl; |
| 322 | - // 在 GEFinalize() 之前 delete session,与上游样例 | ||
| 323 | - // /workspace/ops-nn/activation/celu/examples/test_geir_celu.cpp:241-243 对齐。 | ||
| 324 | - // 本文件原先只在 RunGraph 失败分支删 session、成功路径漏删(session 泄漏)。 | ||
| 325 | - // | ||
| 326 | - // ⚠ 澄清:补这两行【不能】消除进程收尾时的 `corrupted size vs. prev_size` 核心转储。 | ||
| 327 | - // 实测该崩溃在 GEFinalize() 内部发生,且用**未经改动的上游样例 test_geir_celu | ||
| 328 | - // 打内置算子 Celu** 可等价复现(同样的报错、同样的位置、同样 exit 134), | ||
| 329 | - // 与本算子无关,属环境/GE 侧收尾问题。 | ||
| 330 | - // 崩溃点在全部结果打印完毕之后,不影响计算结果的正确性;但也【不得】因此把它 | ||
| 331 | - // 当成"跑通了"——排障时先跑一遍 celu 对照,再判断是不是自己的问题。 | ||
| 332 | delete session; | 388 | delete session; |
| 333 | session = nullptr; | 389 | session = nullptr; |
| 334 | printf("%s - INFO - [XIR]: Start to finalize ir graph session\n", GetTime().c_str()); | 390 | printf("%s - INFO - [XIR]: Start to finalize ir graph session\n", GetTime().c_str()); |
| @@ -76,6 +76,10 @@ namespace ge { | |||
| 76 | * (the TBE implementation declares reuse=('accum', 'parameters', 'stat')). | 76 | * (the TBE implementation declares reuse=('accum', 'parameters', 'stat')). |
| 77 | *@li When "momentum" is 0 (including -0.0), "accum" and "stat" are NOT written at all | 77 | *@li When "momentum" is 0 (including -0.0), "accum" and "stat" are NOT written at all |
| 78 | * and keep their input values bit-for-bit; "parameters" is still updated as usual. | 78 | * and keep their input values bit-for-bit; "parameters" is still updated as usual. |
| 79 | + * The momentum multiplication is still evaluated: 0 * inf can propagate NaN | ||
| 80 | + * into "parameters" even when the writeback mask is zero. | ||
| 81 | + *@li On <term>Ascend 950PR&950DT系列产品</term>, intermediate arithmetic uses float32. | ||
| 82 | + * Float16 and bfloat16 results use round-to-nearest, ties-to-even conversion. | ||
| 79 | *@li The rank of "parameters" must be in the range [1, 8]; rank-0 (scalar) is rejected. | 83 | *@li The rank of "parameters" must be in the range [1, 8]; rank-0 (scalar) is rejected. |
| 80 | * Empty tensors (any axis being 0) are rejected as well. | 84 | * Empty tensors (any axis being 0) are rejected as well. |
| 81 | * | 85 | * |
| @@ -147,8 +147,7 @@ ge::graphStatus SgdRegbaseTiling::CheckShapeAndType() | |||
| 147 | OP_CHECK_NULL_WITH_CONTEXT(tilingContext_, inputParamDesc); | 147 | OP_CHECK_NULL_WITH_CONTEXT(tilingContext_, inputParamDesc); |
| 148 | auto inputDtype = inputParamDesc->GetDataType(); | 148 | auto inputDtype = inputParamDesc->GetDataType(); |
| 149 | 149 | ||
| 150 | - // ascend910b 只校验了 parameters 的 rank,其余 5 个输入的同形 / 同 dtype 完全未校验 | 150 | + // 检查全部输入的shape与dtype;不能仅依赖parameters的rank校验。 |
| 151 | - // (canndev CheckSgdDimension 仅看 parameters)。ascend950 侧补齐,见 01 §6.5。 | ||
| 152 | // 内层 CheckRank 已用 OP_LOGE_FOR_INVALID_SHAPEDIM 结构化上报,此处只作调用链留痕, | 151 | // 内层 CheckRank 已用 OP_LOGE_FOR_INVALID_SHAPEDIM 结构化上报,此处只作调用链留痕, |
| 153 | // 不重复打 ERROR(R6 要求的是"校验必须走结构化宏",不是"每层都再报一次")。 | 152 | // 不重复打 ERROR(R6 要求的是"校验必须走结构化宏",不是"每层都再报一次")。 |
| 154 | OP_CHECK_IF(CheckRank(inputStorageShape) != ge::GRAPH_SUCCESS, OP_LOGD(tilingContext_, "rank check failed"), | 153 | OP_CHECK_IF(CheckRank(inputStorageShape) != ge::GRAPH_SUCCESS, OP_LOGD(tilingContext_, "rank check failed"), |
| @@ -17,11 +17,11 @@ | |||
| 17 | * FRACTAL_Z_3D 五种,本算子只做 ND。依据是 ops-nn 仓内 arch35 全族 optim 算子 | 17 | * FRACTAL_Z_3D 五种,本算子只做 ND。依据是 ops-nn 仓内 arch35 全族 optim 算子 |
| 18 | * (apply_momentum / apply_ftrl / apply_adam_w_v2 / apply_adamax / | 18 | * (apply_momentum / apply_ftrl / apply_adam_w_v2 / apply_adamax / |
| 19 | * apply_centered_rms_prop)的 def.cpp 一律只声明 ge::FORMAT_ND,无一例声明私有 | 19 | * apply_centered_rms_prop)的 def.cpp 一律只声明 ge::FORMAT_ND,无一例声明私有 |
| 20 | - * format —— 跟随本仓 arch35 既定约定,非自行设计。已于 CP1 批准。 | 20 | + * format。当前支持范围及产品差异见算子 README。 |
| 21 | * | 21 | * |
| 22 | * R2(ascend950 不碰 ascend910b)天然满足:ops-nn 仓内不存在 SGD 算子(optim/fused_sgd 是语义 | 22 | * R2(ascend950 不碰 ascend910b)天然满足:ops-nn 仓内不存在 SGD 算子(optim/fused_sgd 是语义 |
| 23 | * 不同的另一个算子,且无 arch35 目录),本算子为净新增,只 AddConfig("ascend950"), | 23 | * 不同的另一个算子,且无 arch35 目录),本算子为净新增,只 AddConfig("ascend950"), |
| 24 | - * 不触碰任何 ascend910b 配置(先例:apply_momentum、apply_adamax 亦只有 ascend950 config)。 | 24 | + * 不触碰任何 ascend910b 配置;apply_momentum 另有 ascend350 等配置,不代表本算子的注册范围。 |
| 25 | * | 25 | * |
| 26 | * accum / stat 【不声明为 Output】—— 与 ascend910b 形态一致(canndev proto | 26 | * accum / stat 【不声明为 Output】—— 与 ascend910b 形态一致(canndev proto |
| 27 | * nn_training_ops.h:1431 亦只有一个 OUTPUT),两者靠覆写输入 GM 原地回写。 | 27 | * nn_training_ops.h:1431 亦只有一个 OUTPUT),两者靠覆写输入 GM 原地回写。 |
| @@ -18,9 +18,8 @@ | |||
| 18 | * - UNKNOWN_RANK(-2) 透传 —— GE 下 dims = {-2}(DimNum == 1),不触发 rank 拒绝; | 18 | * - UNKNOWN_RANK(-2) 透传 —— GE 下 dims = {-2}(DimNum == 1),不触发 rank 拒绝; |
| 19 | * - nesterov == true 时 dampening 必须为 0;weight_decay >= 0。 | 19 | * - nesterov == true 时 dampening 必须为 0;weight_decay >= 0。 |
| 20 | * | 20 | * |
| 21 | - * 注:ascend910b 把这两条属性校验放在 InferShape 里而非 Verify(其 SGDVerify 只调 | 21 | + * InferShape与op_host/arch35/sgd_tiling.cpp分别校验属性,保证图推导和 |
| 22 | - * CheckSgdDimension),属职责错位(见 01 §6.5)。ascend950 侧在 InferShape 与 Tiling | 22 | + * 独立Tiling入口均拒绝非法属性。 |
| 23 | - * 两个独立入口都做,保证任一入口进来都拦得住。 | ||
| 24 | */ | 23 | */ |
| 25 | #include "log/log.h" | 24 | #include "log/log.h" |
| 26 | #include "register/op_impl_registry.h" | 25 | #include "register/op_impl_registry.h" |
| @@ -60,7 +59,7 @@ static ge::graphStatus InferShapeForSgd(gert::InferShapeContext* context) | |||
| 60 | OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context->GetNodeName(), "dampening", std::to_string(dampeningVal).c_str(), | 59 | OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context->GetNodeName(), "dampening", std::to_string(dampeningVal).c_str(), |
| 61 | "attr dampening must be 0 when attr nesterov is true"), | 60 | "attr dampening must be 0 when attr nesterov is true"), |
| 62 | return ge::GRAPH_FAILED); | 61 | return ge::GRAPH_FAILED); |
| 63 | - OP_CHECK_IF(weightDecayVal < 0.0f, | 62 | + OP_CHECK_IF(!(weightDecayVal >= 0.0f), |
| 64 | OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context->GetNodeName(), "weight_decay", | 63 | OP_LOGE_FOR_INVALID_VALUE_WITH_REASON(context->GetNodeName(), "weight_decay", |
| 65 | std::to_string(weightDecayVal).c_str(), | 64 | std::to_string(weightDecayVal).c_str(), |
| 66 | "attr weight_decay must be more than or equal to 0"), | 65 | "attr weight_decay must be more than or equal to 0"), |
| @@ -31,9 +31,10 @@ parenthesization: arch35 ``sgd_dag.h`` computes | |||
| 31 | slot constraint. Cross-check must reproduce that actual device operation | 31 | slot constraint. Cross-check must reproduce that actual device operation |
| 32 | order, then cast all three writeback values to the NPU input dtype. | 32 | order, then cast all three writeback values to the NPU input dtype. |
| 33 | 33 | ||
| 34 | -Promote contract: half/bfloat16 inputs may be lifted to float32 for CPU support; | 34 | +The CPU oracle evaluates intermediate arithmetic in float64 to avoid sharing |
| 35 | -float32 and float64 inputs are never narrowed. TTK promotes both inputs and | 35 | +float32 overflow with the device. The independent third-party composition keeps |
| 36 | -``output_dtypes`` before invoking this Spec when ``cross_check`` is active. | 36 | +the device's float32 operation order. Output conversion still follows TTK's |
| 37 | +requested dtypes, and masked accum/stat writeback preserves the original bits. | ||
| 37 | """ | 38 | """ |
| 38 | 39 | ||
| 39 | import numpy as np | 40 | import numpy as np |
| @@ -95,13 +96,6 @@ def _numpy_to_torch(array): | |||
| 95 | return torch.from_numpy(value) | 96 | return torch.from_numpy(value) |
| 96 | 97 | ||
| 97 | 98 | ||
| 98 | -def _f32_floor(tensor): | ||
| 99 | - """Lift only fp16/bf16; preserve fp32/fp64 supplied by Promote.""" | ||
| 100 | - if tensor.dtype in (torch.float16, torch.bfloat16): | ||
| 101 | - return tensor.to(torch.float32) | ||
| 102 | - return tensor | ||
| 103 | - | ||
| 104 | - | ||
| 105 | def _scalar_value(tensor): | 99 | def _scalar_value(tensor): |
| 106 | """Read an already-quantized scalar without rebuilding it from a literal.""" | 100 | """Read an already-quantized scalar without rebuilding it from a literal.""" |
| 107 | return float(tensor.reshape(-1)[0].item()) | 101 | return float(tensor.reshape(-1)[0].item()) |
| @@ -114,21 +108,22 @@ def _compute( | |||
| 114 | accum, | 108 | accum, |
| 115 | momentum, | 109 | momentum, |
| 116 | stat, | 110 | stat, |
| 117 | - dampening=0.0, | 111 | + dampening=None, |
| 118 | - weight_decay=0.0, | 112 | + weight_decay=None, |
| 119 | - nesterov=False, | 113 | + nesterov=None, |
| 120 | **kwargs, | 114 | **kwargs, |
| 121 | ): | 115 | ): |
| 122 | - """Torch competitor composition shared by TestSpec and legacy entry. | 116 | + """CPU reference shared by TestSpec and the legacy entry. |
| 123 | 117 | ||
| 124 | Inputs and outputs are Torch tensors. This is the high-precision CPU | 118 | Inputs and outputs are Torch tensors. This is the high-precision CPU |
| 125 | true-value path, so it keeps the legacy expanded Nesterov formula; the | 119 | true-value path, so it keeps the legacy expanded Nesterov formula; the |
| 126 | independent ``_SgdCompose`` below follows the grouped arch35 DAG order. | 120 | independent ``_SgdCompose`` below follows the grouped arch35 DAG order. |
| 127 | """ | 121 | """ |
| 128 | - if kwargs.get("golden_mode") == "Promote": | 122 | + # Keep the CPU oracle independent from the device's float32 intermediates. |
| 129 | - p, g, a, s = parameters, gradient, accum, stat | 123 | + # Products may exceed float32 even when the final mathematical result fits. |
| 130 | - else: | 124 | + p, g, a, s = ( |
| 131 | - p, g, a, s = map(_f32_floor, (parameters, gradient, accum, stat)) | 125 | + value.to(torch.float64) for value in (parameters, gradient, accum, stat) |
| 126 | + ) | ||
| 132 | learning_rate_value = _scalar_value(learning_rate) | 127 | learning_rate_value = _scalar_value(learning_rate) |
| 133 | momentum_value = _scalar_value(momentum) | 128 | momentum_value = _scalar_value(momentum) |
| 134 | 129 | ||
| @@ -238,9 +233,9 @@ def _kernel_golden( | |||
| 238 | accum, | 233 | accum, |
| 239 | momentum, | 234 | momentum, |
| 240 | stat, | 235 | stat, |
| 241 | - dampening=0.0, | 236 | + dampening=None, |
| 242 | - weight_decay=0.0, | 237 | + weight_decay=None, |
| 243 | - nesterov=False, | 238 | + nesterov=None, |
| 244 | **kwargs, | 239 | **kwargs, |
| 245 | ): | 240 | ): |
| 246 | """Kernel/GEIR container shell: NumPy in, NumPy out.""" | 241 | """Kernel/GEIR container shell: NumPy in, NumPy out.""" |
| @@ -276,9 +271,9 @@ def sgd_golden( | |||
| 276 | accum, | 271 | accum, |
| 277 | momentum, | 272 | momentum, |
| 278 | stat, | 273 | stat, |
| 279 | - dampening=0.0, | 274 | + dampening=None, |
| 280 | - weight_decay=0.0, | 275 | + weight_decay=None, |
| 281 | - nesterov=False, | 276 | + nesterov=None, |
| 282 | **kwargs, | 277 | **kwargs, |
| 283 | ): | 278 | ): |
| 284 | """Compatibility ``__golden__`` entry; delegates to the shared core.""" | 279 | """Compatibility ``__golden__`` entry; delegates to the shared core.""" |
| @@ -301,9 +296,9 @@ class _SgdCompose: | |||
| 301 | 296 | ||
| 302 | def __init__( | 297 | def __init__( |
| 303 | self, | 298 | self, |
| 304 | - dampening=0.0, | 299 | + dampening=None, |
| 305 | - weight_decay=0.0, | 300 | + weight_decay=None, |
| 306 | - nesterov=False, | 301 | + nesterov=None, |
| 307 | **kwargs, | 302 | **kwargs, |
| 308 | ): | 303 | ): |
| 309 | attrs = { | 304 | attrs = { |
| @@ -397,9 +392,9 @@ class SgdKernelSpec: | |||
| 397 | accum, | 392 | accum, |
| 398 | momentum, | 393 | momentum, |
| 399 | stat, | 394 | stat, |
| 400 | - dampening=0.0, | 395 | + dampening=None, |
| 401 | - weight_decay=0.0, | 396 | + weight_decay=None, |
| 402 | - nesterov=False, | 397 | + nesterov=None, |
| 403 | **kwargs, | 398 | **kwargs, |
| 404 | ): | 399 | ): |
| 405 | return _kernel_golden( | 400 | return _kernel_golden( |
| @@ -421,5 +416,4 @@ class SgdKernelSpec: | |||
| 421 | 416 | ||
| 422 | # 【不存在】ACLNN 通路:optim/sgd/CMakeLists.txt 使用 ACLNNTYPE | 417 | # 【不存在】ACLNN 通路:optim/sgd/CMakeLists.txt 使用 ACLNNTYPE |
| 423 | # aclnn_exclude,且算子目录没有 op_api/aclnn_sgd 实现;因此 __spec__ 不注册 aclnnSgd。 | 418 | # aclnn_exclude,且算子目录没有 op_api/aclnn_sgd 实现;因此 __spec__ 不注册 aclnnSgd。 |
| 424 | -# 【不存在】e2e 通路:需求明确不提供本算子的 torch_npu 绑定,也没有可调用的 | 419 | +# 本目录未交付 torch_npu 绑定或可调用的 aclnnSgd 实现,因此不注册 TorchSpec。 |
| 425 | -# aclnnSgd 底层接口;因此不保留 TorchSpec 空壳。 | ||
| @@ -25,6 +25,7 @@ | |||
| 25 | 25 | ||
| 26 | #include <gtest/gtest.h> // NOLINT | 26 | #include <gtest/gtest.h> // NOLINT |
| 27 | #include <iostream> | 27 | #include <iostream> |
| 28 | +#include <limits> | ||
| 28 | #include <vector> | 29 | #include <vector> |
| 29 | #include "infershape_test_util.h" // NOLINT | 30 | #include "infershape_test_util.h" // NOLINT |
| 30 | #include "ut_op_common.h" | 31 | #include "ut_op_common.h" |
| @@ -139,3 +140,28 @@ TEST_F(SGD, sgd_infershape_negative_weight_decay_rejected) | |||
| 139 | gert::StorageShape scalarShape = {{1}, {1}}; | 140 | gert::StorageShape scalarShape = {{1}, {1}}; |
| 140 | ASSERT_EQ(RunSgdInferShape(bigShape, scalarShape, 0.0f, -0.01f, false), ge::GRAPH_FAILED); | 141 | ASSERT_EQ(RunSgdInferShape(bigShape, scalarShape, 0.0f, -0.01f, false), ge::GRAPH_FAILED); |
| 141 | } | 142 | } |
| 143 | + | ||
| 144 | +TEST_F(SGD, sgd_infershape_nan_weight_decay_rejected) | ||
| 145 | +{ | ||
| 146 | + gert::StorageShape bigShape = {{96, 256}, {96, 256}}; | ||
| 147 | + gert::StorageShape scalarShape = {{1}, {1}}; | ||
| 148 | + ASSERT_EQ(RunSgdInferShape(bigShape, scalarShape, 0.0f, std::numeric_limits<float>::quiet_NaN(), false), | ||
| 149 | + ge::GRAPH_FAILED); | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +TEST_F(SGD, sgd_infershape_negative_infinite_weight_decay_rejected) | ||
| 153 | +{ | ||
| 154 | + gert::StorageShape bigShape = {{96, 256}, {96, 256}}; | ||
| 155 | + gert::StorageShape scalarShape = {{1}, {1}}; | ||
| 156 | + ASSERT_EQ(RunSgdInferShape(bigShape, scalarShape, 0.0f, -std::numeric_limits<float>::infinity(), false), | ||
| 157 | + ge::GRAPH_FAILED); | ||
| 158 | +} | ||
| 159 | + | ||
| 160 | +TEST_F(SGD, sgd_infershape_nonnegative_weight_decay_boundaries) | ||
| 161 | +{ | ||
| 162 | + gert::StorageShape bigShape = {{96, 256}, {96, 256}}; | ||
| 163 | + gert::StorageShape scalarShape = {{1}, {1}}; | ||
| 164 | + for (float weightDecay : {-0.0f, 0.5f, std::numeric_limits<float>::infinity()}) { | ||
| 165 | + EXPECT_EQ(RunSgdInferShape(bigShape, scalarShape, 0.0f, weightDecay, false), ge::GRAPH_SUCCESS); | ||
| 166 | + } | ||
| 167 | +} | ||
| @@ -34,8 +34,8 @@ | |||
| 34 | | --- | --- | --- | --- | --- | | 34 | | --- | --- | --- | --- | --- | |
| 35 | | boxes | 输入 | 目标检测框的绝对坐标,对应公式中的`boxes`。 | FLOAT、FLOAT16 | ND | | 35 | | boxes | 输入 | 目标检测框的绝对坐标,对应公式中的`boxes`。 | FLOAT、FLOAT16 | ND | |
| 36 | | shape_hw | 输入 | 每个batch的图像尺寸,shape为`[batch, 3]`;前两个元素依次为高度和宽度,第3个元素为保留位。 | INT32 | ND | | 36 | | shape_hw | 输入 | 每个batch的图像尺寸,shape为`[batch, 3]`;前两个元素依次为高度和宽度,第3个元素为保留位。 | INT32 | ND | |
| 37 | -| reversed_box | 可选属性 | 指定`boxes`的坐标轴位置,默认值为false。 | BOOL | - | | ||
| 38 | | y | 输出 | 换算后的相对坐标,shape和数据类型与`boxes`一致。 | FLOAT、FLOAT16 | ND | | 37 | | y | 输出 | 换算后的相对坐标,shape和数据类型与`boxes`一致。 | FLOAT、FLOAT16 | ND | |
| 38 | +| reversed_box | 可选属性 | 指定`boxes`的坐标轴位置,默认值为false。 | BOOL | - | | ||
| 39 | 39 | ||
| 40 | ### 产品差异说明 | 40 | ### 产品差异说明 |
| 41 | 41 | ||
| @@ -58,4 +58,4 @@ | |||
| 58 | | 调用方式 | 调用样例 | 说明 | | 58 | | 调用方式 | 调用样例 | 说明 | |
| 59 | | --- | --- | --- | | 59 | | --- | --- | --- | |
| 60 | | GE图模式 | [test_geir_normalize_bbox.cpp](examples/arch35/test_geir_normalize_bbox.cpp) | 通过[NormalizeBBox算子IR](op_graph/normalize_bbox_proto.h)构图并调用算子。 | | 60 | | GE图模式 | [test_geir_normalize_bbox.cpp](examples/arch35/test_geir_normalize_bbox.cpp) | 通过[NormalizeBBox算子IR](op_graph/normalize_bbox_proto.h)构图并调用算子。 | |
| 61 | -| TensorFlow图导入 | - | 通过[TensorFlow适配定义](framework/normalize_bbox_tf_plugin.cpp)将TensorFlow的`NormalizeBBox`节点解析为GE算子。 | | 61 | +| TensorFlow图导入 | - | 通过[TensorFlow适配定义](framework/normalize_bbox_tf_plugin.cpp)接收作用域融合生成的NormalizeBBox算子,`reversed_box`保持默认值false。 | |
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <vector> | 15 | #include <vector> |
| 16 | #include <string> | 16 | #include <string> |
| 17 | #include <map> | 17 | #include <map> |
| 18 | +#include <cmath> | ||
| 18 | #include "assert.h" | 19 | #include "assert.h" |
| 19 | 20 | ||
| 20 | #include "graph.h" | 21 | #include "graph.h" |
| @@ -25,7 +26,7 @@ | |||
| 25 | #include "ge_api.h" | 26 | #include "ge_api.h" |
| 26 | #include "array_ops.h" | 27 | #include "array_ops.h" |
| 27 | #include "ge_ir_build.h" | 28 | #include "ge_ir_build.h" |
| 28 | -#include "nn_detect_ops.h" | 29 | +#include "../../op_graph/normalize_bbox_proto.h" |
| 29 | 30 | ||
| 30 | #define FAILED -1 | 31 | #define FAILED -1 |
| 31 | #define SUCCESS 0 | 32 | #define SUCCESS 0 |
| @@ -146,6 +147,7 @@ int main(int argc, char* argv[]) | |||
| 146 | ret = GenFloatData(boxesShape, tensor_boxes, boxesDesc, boxesDtype); | 147 | ret = GenFloatData(boxesShape, tensor_boxes, boxesDesc, boxesDtype); |
| 147 | if (ret != SUCCESS) { | 148 | if (ret != SUCCESS) { |
| 148 | printf("GenFloatData boxes failed\n"); | 149 | printf("GenFloatData boxes failed\n"); |
| 150 | + GEFinalize(); | ||
| 149 | return FAILED; | 151 | return FAILED; |
| 150 | } | 152 | } |
| 151 | boxesData.update_input_desc_x(boxesDesc); | 153 | boxesData.update_input_desc_x(boxesDesc); |
| @@ -163,6 +165,7 @@ int main(int argc, char* argv[]) | |||
| 163 | ret = GenInt32Data(shapeHwShape, tensor_shape_hw, shapeHwDesc); | 165 | ret = GenInt32Data(shapeHwShape, tensor_shape_hw, shapeHwDesc); |
| 164 | if (ret != SUCCESS) { | 166 | if (ret != SUCCESS) { |
| 165 | printf("GenInt32Data shape_hw failed\n"); | 167 | printf("GenInt32Data shape_hw failed\n"); |
| 168 | + GEFinalize(); | ||
| 166 | return FAILED; | 169 | return FAILED; |
| 167 | } | 170 | } |
| 168 | shapeHwData.update_input_desc_x(shapeHwDesc); | 171 | shapeHwData.update_input_desc_x(shapeHwDesc); |
| @@ -182,9 +185,10 @@ int main(int argc, char* argv[]) | |||
| 182 | 185 | ||
| 183 | std::map<AscendString, AscendString> build_options = {}; | 186 | std::map<AscendString, AscendString> build_options = {}; |
| 184 | printf("%s - INFO - [XIR]: Create session\n", GetTime().c_str()); | 187 | printf("%s - INFO - [XIR]: Create session\n", GetTime().c_str()); |
| 185 | - ge::Session* session = new Session(build_options); | 188 | + ge::Session* session = new (std::nothrow) Session(build_options); |
| 186 | if (session == nullptr) { | 189 | if (session == nullptr) { |
| 187 | printf("Create session failed\n"); | 190 | printf("Create session failed\n"); |
| 191 | + GEFinalize(); | ||
| 188 | return FAILED; | 192 | return FAILED; |
| 189 | } | 193 | } |
| 190 | 194 | ||
| @@ -212,28 +216,41 @@ int main(int argc, char* argv[]) | |||
| 212 | } | 216 | } |
| 213 | printf("%s - INFO - [XIR]: RunGraph success\n", GetTime().c_str()); | 217 | printf("%s - INFO - [XIR]: RunGraph success\n", GetTime().c_str()); |
| 214 | 218 | ||
| 215 | - // verify output | 219 | + bool valid = output.size() == 1; |
| 216 | - int output_num = output.size(); | 220 | + if (valid) { |
| 217 | - printf("output_num=%d\n", output_num); | 221 | + const auto& tensor = output[0]; |
| 218 | - for (int i = 0; i < output_num; i++) { | 222 | + valid = tensor.GetTensorDesc().GetDataType() == boxesDtype && |
| 219 | - DataType outDtype = output[i].GetTensorDesc().GetDataType(); | 223 | + tensor.GetTensorDesc().GetShape().GetDims() == boxesShape && |
| 220 | - int64_t outSize = output[i].GetTensorDesc().GetShape().GetShapeSize(); | 224 | + tensor.GetSize() == 8 * GetDataTypeSize(boxesDtype) && tensor.GetData() != nullptr; |
| 221 | - printf("output[%d] dtype=%d shapeSize=%ld\n", i, outDtype, outSize); | 225 | + for (size_t j = 0; valid && j < 8; ++j) { |
| 222 | - uint8_t* outData = output[i].GetData(); | 226 | + float actual; |
| 223 | - if (outData == nullptr) { | 227 | + if (boxesDtype == DT_FLOAT) { |
| 224 | - printf("output[%d] GetData() returned nullptr\n", i); | 228 | + memcpy(&actual, tensor.GetData() + j * sizeof(float), sizeof(float)); |
| 225 | - continue; | 229 | + } else { |
| 226 | - } | 230 | + uint16_t bits; |
| 227 | - if (outDtype == ge::DT_FLOAT) { | 231 | + memcpy(&bits, tensor.GetData() + j * sizeof(bits), sizeof(bits)); |
| 228 | - float* p = (float*)outData; | 232 | + const int exponent = (bits >> 10) & 0x1f; |
| 229 | - // boxes[i] = (i*10+5)%1000, divisor = [100, 200, 100, 200] (h=100, w=200) | 233 | + const int mantissa = bits & 0x3ff; |
| 230 | - float divisors[4] = {100.0f, 200.0f, 100.0f, 200.0f}; | 234 | + actual = exponent == 0 ? std::ldexp(static_cast<float>(mantissa), -24) : |
| 231 | - for (int64_t j = 0; j < outSize; j++) { | 235 | + std::ldexp(static_cast<float>(1024 + mantissa), exponent - 25); |
| 232 | - float expect = static_cast<float>((j * 10 + 5) % 1000) / divisors[j % 4]; | 236 | + actual = (bits & 0x8000) ? -actual : actual; |
| 233 | - printf(" result[%ld] = %f (expect %f)\n", j, p[j], expect); | 237 | + if (exponent == 31) { |
| 238 | + valid = false; | ||
| 239 | + break; | ||
| 240 | + } | ||
| 234 | } | 241 | } |
| 242 | + const float expected = static_cast<float>((j * 10 + 5) % 1000) / (j % 2 == 0 ? 100.0f : 200.0f); | ||
| 243 | + const float tolerance = boxesDtype == DT_FLOAT16 ? 1e-4f + 1e-3f * std::abs(expected) : 1e-6f; | ||
| 244 | + printf("result[%zu] = %f (expect %f)\n", j, actual, expected); | ||
| 245 | + valid = std::isfinite(actual) && std::abs(actual - expected) <= tolerance; | ||
| 235 | } | 246 | } |
| 236 | } | 247 | } |
| 248 | + if (!valid) { | ||
| 249 | + printf("NormalizeBBox result verification failed\n"); | ||
| 250 | + delete session; | ||
| 251 | + GEFinalize(); | ||
| 252 | + return FAILED; | ||
| 253 | + } | ||
| 237 | 254 | ||
| 238 | delete session; | 255 | delete session; |
| 239 | session = nullptr; | 256 | session = nullptr; |