Merged
fix: harden SGD, Relu6Grad and NormalizeBBox #12376
raoliang_sacAdd on 21 days ago
fix: harden SGD, Relu6Grad and NormalizeBBox #12376
Merged
Total: 24 files changed+460-190
| @@ -57,14 +57,14 @@ | |||
| 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> |
| @@ -73,8 +73,13 @@ | |||
| 73 | ## 约束说明 | 73 | ## 约束说明 |
| 74 | 74 | ||
| 75 | - gradients、features、backprops的数据类型须保持一致,不支持混合数据类型。 | 75 | - gradients、features、backprops的数据类型须保持一致,不支持混合数据类型。 |
| 76 | -- 不支持广播:features的shape须与gradients完全一致,或为标量[1]。 | ||
| 77 | - 支持动态shape与动态rank。 | 76 | - 支持动态shape与动态rank。 |
| 77 | +- <term>Ascend 950PR&950DT系列产品</term>: | ||
| 78 | + - 输入shape约束:gradients与features支持双向广播,各自rank不超过8;GE图模式支持满足广播关系的空Tensor。 | ||
| 79 | + - 输出shape约束:输出为两个输入广播后的shape;空Tensor的输出元素数为0。 | ||
| 80 | +- <term>Atlas A3系列产品</term>、<term>Atlas A2系列产品</term>: | ||
| 81 | + - 输入shape约束:features的shape须与gradients一致,或为[1]。 | ||
| 82 | + - 输出shape约束:与gradients一致。 | ||
| 78 | 83 | ||
| 79 | ## 实现方案 | 84 | ## 实现方案 |
| 80 | 85 | ||
| @@ -83,6 +88,7 @@ | |||
| 83 | | 计算图原型 | `op_graph/relu6_grad_proto.h` | `REG_OP(Relu6Grad)`,二输入一输出 | | 88 | | 计算图原型 | `op_graph/relu6_grad_proto.h` | `REG_OP(Relu6Grad)`,二输入一输出 | |
| 84 | | 算子定义 | `op_host/relu6_grad_def.cpp` | `OpDef::AddConfig("ascend950", ...)` | | 89 | | 算子定义 | `op_host/relu6_grad_def.cpp` | `OpDef::AddConfig("ascend950", ...)` | |
| 85 | | InferShape | `op_host/relu6_grad_infershape.cpp` | 复用`Ops::Base::InferShape4Broadcast` | | 90 | | InferShape | `op_host/relu6_grad_infershape.cpp` | 复用`Ops::Base::InferShape4Broadcast` | |
| 91 | +| 图模式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>` | | 92 | | 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中间精度 | | 93 | | 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` | | 94 | | Struct | `op_kernel/arch35/relu6_grad_struct.h` | `BRC_TEMP_SCH_MODE_KEY_DECL/SEL` | |
| @@ -90,7 +96,7 @@ | |||
| 90 | 96 | ||
| 91 | ### fp32通路 | 97 | ### fp32通路 |
| 92 | 98 | ||
| 93 | -```Cpp | 99 | +```text |
| 94 | In0(dy) ─CopyInBrc─+ | 100 | In0(dy) ─CopyInBrc─+ |
| 95 | +──────────────────────────────+ | 101 | +──────────────────────────────+ |
| 96 | | | 102 | | |
| @@ -102,13 +108,13 @@ In1(x) ─CopyInBrc─+ | | |||
| 102 | Const(0,fp32) ─Duplicate─ Zero ─────────────── ┴ Select(mask, dy, Zero) ─ CopyOut ─ Out0 | 108 | Const(0,fp32) ─Duplicate─ Zero ─────────────── ┴ Select(mask, dy, Zero) ─ CopyOut ─ Out0 |
| 103 | ``` | 109 | ``` |
| 104 | 110 | ||
| 105 | -```Cpp | ||
| 106 | `Vec::Compare<u8, fp32, GT/LT>`输出位掩码,`Vec::And<u8>`合成`(0 < x < 6)` | 111 | `Vec::Compare<u8, fp32, GT/LT>`输出位掩码,`Vec::And<u8>`合成`(0 < x < 6)` |
| 107 | 区间mask;`Vec::Select<u8, fp32, TENSOR_TENSOR>`按`mask ? dy : 0`逐元素 | 112 | 区间mask;`Vec::Select<u8, fp32, TENSOR_TENSOR>`按`mask ? dy : 0`逐元素 |
| 108 | 选择,避免分支,硬件原生vsel实现。 | 113 | 选择,避免分支,硬件原生vsel实现。 |
| 109 | 114 | ||
| 110 | ### fp16 / bf16通路 | 115 | ### fp16 / bf16通路 |
| 111 | 116 | ||
| 117 | +```text | ||
| 112 | In0/In1 ─CopyInBrc─ Cast(->fp32) ─+ | 118 | In0/In1 ─CopyInBrc─ Cast(->fp32) ─+ |
| 113 | +─ Compare ── And ─ Select ── Cast(->T,RINT) ─ CopyOut ─ Out0 | 119 | +─ Compare ── And ─ Select ── Cast(->T,RINT) ─ CopyOut ─ Out0 |
| 114 | Const(0,fp32) ── Zero ─+ | 120 | Const(0,fp32) ── Zero ─+ |
| @@ -126,4 +132,4 @@ In0/In1 ─CopyInBrc─ Cast(->fp32) ─+ | |||
| 126 | 132 | ||
| 127 | | 调用方式 | 样例代码 | 说明 | | 133 | | 调用方式 | 样例代码 | 说明 | |
| 128 | | ---------------- | --------------------------- |-----------------------------------------------| | 134 | | ---------------- | --------------------------- |-----------------------------------------------| |
| 129 | -| 图模式 | [test_geir_relu6_grad.cpp](examples/arch35/test_geir_relu6_grad.cpp) | 通过[算子IR](op_graph/relu6_grad_proto.h)构图方式调用Relu6Grad算子。 | | 135 | +| 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> |
| @@ -61,23 +61,32 @@ | |||
| 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 | - | ||
| 69 | -### 产品差异说明 | ||
| 70 | - | ||
| 71 | -| 产品 | 数据类型 | 静态shape格式 | 动态shape格式 | shape/rank及空Tensor限制 | | ||
| 72 | -| --- | --- | --- | --- | --- | | ||
| 73 | -| <term>Ascend 950PR&950DT系列产品</term> | 各输入、输出支持FLOAT、FLOAT16、BFLOAT16 | 各输入、输出均为ND | 支持动态shape和动态rank,各输入、输出均为ND | `parameters`的rank为1~8,不支持空Tensor | | ||
| 74 | -| <term>Atlas A3系列产品</term><br><term>Atlas A2系列产品</term> | 各输入、输出支持FLOAT、FLOAT16、BFLOAT16 | `parameters`、`gradient`、`accum`、`stat`及输出支持NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D;`learning_rate`和`momentum`为ND | 不支持 | `parameters`的rank为1~8,不支持空Tensor | | ||
| 75 | -| <term>Atlas 200I/500 A2推理产品</term><br><term>Atlas推理系列产品</term><br><term>Atlas训练系列产品</term> | 各输入、输出支持FLOAT、FLOAT16 | `parameters`、`gradient`、`accum`、`stat`及输出支持NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D;`learning_rate`和`momentum`为ND | 不支持 | `parameters`的rank为1~8,不支持空Tensor | | ||
| 76 | 68 | ||
| 77 | ## 约束说明 | 69 | ## 约束说明 |
| 78 | 70 | ||
| 79 | - 图上仅声明一个`parameters`输出,但`parameters`、`accum`和`stat`均可能被原地更新, | 71 | - 图上仅声明一个`parameters`输出,但`parameters`、`accum`和`stat`均可能被原地更新, |
| 80 | 调用方必须将这三个输入作为可写Tensor。 | 72 | 调用方必须将这三个输入作为可写Tensor。 |
| 73 | +- `momentum=0`仅阻止`accum`和`stat`回写,不跳过权重更新中的动量乘法;当`accum`含Inf时,`0×Inf`产生的NaN会传播到权重结果。 | ||
| 74 | +- <term>Ascend 950PR&950DT系列产品</term>: | ||
| 75 | + - 数据类型:各输入、输出支持FLOAT、FLOAT16、BFLOAT16。 | ||
| 76 | + - 数据格式:静态shape和动态shape的各输入、输出均为ND。 | ||
| 77 | + - 动态shape能力:支持动态shape和动态rank。 | ||
| 78 | + - 输入shape约束:`parameters`的rank为1~8,不支持空Tensor。 | ||
| 79 | + - 中间计算精度:使用FLOAT进行中间计算,FLOAT16、BFLOAT16结果按就近偶数舍入转换回输入数据类型。 | ||
| 80 | +- <term>Atlas A3系列产品</term>、<term>Atlas A2系列产品</term>: | ||
| 81 | + - 数据类型:各输入、输出支持FLOAT、FLOAT16、BFLOAT16。 | ||
| 82 | + - 静态shape数据格式:`parameters`、`gradient`、`accum`、`stat`及输出支持NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D;`learning_rate`和`momentum`为ND。 | ||
| 83 | + - 动态shape能力:不支持动态shape。 | ||
| 84 | + - 输入shape约束:`parameters`的rank为1~8,不支持空Tensor。 | ||
| 85 | +- <term>Atlas 200I/500 A2推理产品</term>、<term>Atlas推理系列产品</term>、<term>Atlas训练系列产品</term>: | ||
| 86 | + - 数据类型:各输入、输出支持FLOAT、FLOAT16。 | ||
| 87 | + - 静态shape数据格式:`parameters`、`gradient`、`accum`、`stat`及输出支持NC1HWC0、NDC1HWC0、ND、FRACTAL_Z、FRACTAL_Z_3D;`learning_rate`和`momentum`为ND。 | ||
| 88 | + - 动态shape能力:不支持动态shape。 | ||
| 89 | + - 输入shape约束:`parameters`的rank为1~8,不支持空Tensor。 | ||
| 81 | 90 | ||
| 82 | ## 调用说明 | 91 | ## 调用说明 |
| 83 | 92 | ||
| @@ -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,15 +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 | |
| 39 | - | 38 | +| reversed_box | 可选属性 | 指定`boxes`的坐标轴位置,默认值为false。 | BOOL | - | |
| 40 | -### 产品差异说明 | ||
| 41 | - | ||
| 42 | -| 产品 | 静态shape能力 | 动态shape能力 | shape/rank及空Tensor限制 | | ||
| 43 | -| --- | --- | --- | --- | | ||
| 44 | -| <term>Ascend 950PR&950DT系列产品</term> | 输入、输出均为ND | 支持动态shape和动态rank,输入、输出均为ND | `boxes`的rank为2~8;支持batch或非坐标维为0的空Tensor,坐标维必须为4 | | ||
| 45 | -| <term>Atlas A3系列产品</term><br><term>Atlas A2系列产品</term><br><term>Atlas 200I/500 A2推理产品</term><br><term>Atlas推理系列产品</term><br><term>Atlas训练系列产品</term> | 输入、输出均为ND | 不支持 | `boxes`仅支持3维,不支持空Tensor | | ||
| 46 | 39 | ||
| 47 | ## 约束说明 | 40 | ## 约束说明 |
| 48 | 41 | ||
| @@ -50,12 +43,19 @@ | |||
| 50 | - 当`reversed_box=false`时,`boxes`的最后一维必须为4;当`reversed_box=true`时, | 43 | - 当`reversed_box=false`时,`boxes`的最后一维必须为4;当`reversed_box=true`时, |
| 51 | `boxes`的第1维必须为4。 | 44 | `boxes`的第1维必须为4。 |
| 52 | - `shape_hw`中的高度和宽度直接作为除数,算子不校验其取值范围。 | 45 | - `shape_hw`中的高度和宽度直接作为除数,算子不校验其取值范围。 |
| 46 | +- <term>Ascend 950PR&950DT系列产品</term>: | ||
| 47 | + - 数据格式:静态shape和动态shape的输入、输出均为ND。 | ||
| 48 | + - 动态shape能力:支持动态shape和动态rank。 | ||
| 49 | + - 输入shape约束:`boxes`的rank为2~8;支持batch或非坐标维为0的空Tensor,坐标维必须为4。 | ||
| 50 | +- <term>Atlas A3系列产品</term>、<term>Atlas A2系列产品</term>、<term>Atlas 200I/500 A2推理产品</term>、<term>Atlas推理系列产品</term>、<term>Atlas训练系列产品</term>: | ||
| 51 | + - 数据格式:静态shape的输入、输出均为ND。 | ||
| 52 | + - 动态shape能力:不支持动态shape。 | ||
| 53 | + - 输入shape约束:`boxes`仅支持3维,不支持空Tensor。 | ||
| 53 | 54 | ||
| 54 | ## 调用说明 | 55 | ## 调用说明 |
| 55 | 56 | ||
| 56 | -本算子不提供aclnn单算子接口,支持图引擎(Graph Engine,GE)图模式及TensorFlow图导入。 | 57 | +本算子不提供aclnn单算子接口,通过图引擎(Graph Engine,GE)图模式调用。 |
| 57 | 58 | ||
| 58 | | 调用方式 | 调用样例 | 说明 | | 59 | | 调用方式 | 调用样例 | 说明 | |
| 59 | | --- | --- | --- | | 60 | | --- | --- | --- | |
| 60 | | GE图模式 | [test_geir_normalize_bbox.cpp](examples/arch35/test_geir_normalize_bbox.cpp) | 通过[NormalizeBBox算子IR](op_graph/normalize_bbox_proto.h)构图并调用算子。 | | 61 | | 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算子。 | | ||
| @@ -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; |