已合并
docs: add README and example for max_pool_grad_with_argmax operator #4829
docs: add README and example for max_pool_grad_with_argmax operator #4829
已合并
liuchuangdev创建于 5月14日
3 个文件变更+439-0
@@ -3347,6 +3347,16 @@
3347 <td>AI Core</td>3347 <td>AI Core</td>
3348 <td>对于输入数据计算2维最大池化操作。</td>3348 <td>对于输入数据计算2维最大池化操作。</td>
3349 </tr>3349 </tr>
3350+ <tr>
3351+ <td>pooling</td>
3352+ <td><a href="../../pooling/max_pool_grad_with_argmax/README.md">max_pool_grad_with_argmax</a></td>
3353+ <td>✓</td>
3354+ <td>✓</td>
3355+ <td>✗</td>
3356+ <td>✓</td>
3357+ <td>AI Core</td>
3358+ <td>正向最大池化MaxPoolWithArgmax的反向梯度计算。</td>
3359+ </tr>
3350 <tr>3360 <tr>
3351 <td>pooling</td>3361 <td>pooling</td>
3352 <td><a href="../../pooling/max_pool_grad_with_argmax_v3/README.md">max_pool_grad_with_argmax_v3</a></td>3362 <td><a href="../../pooling/max_pool_grad_with_argmax_v3/README.md">max_pool_grad_with_argmax_v3</a></td>
@@ -0,0 +1,109 @@
1+# MaxPoolGradWithArgmax
2+ 
3+## 产品支持情况
4+ 
5+|产品 | 是否支持 |
6+|:-------------------------|:----------:|
7+| <term>Ascend 950PR/Ascend 950DT</term> | √ |
8+| <term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term> | × |
9+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | × |
10+| <term>Atlas 200I/500 A2 推理产品</term> | × |
11+| <term>Atlas 推理系列产品</term> | × |
12+| <term>Atlas 训练系列产品</term> | × |
13+ 
14+## 功能说明
15+ 
16+- 算子功能:正向最大池化MaxPoolWithArgmax的反向梯度计算。
17+ 
18+## 参数说明
19+ 
20+<table style="undefined;table-layout: fixed; width: 980px"><colgroup>
21+ <col style="width: 100px">
22+ <col style="width: 150px">
23+ <col style="width: 280px">
24+ <col style="width: 330px">
25+ <col style="width: 120px">
26+ </colgroup>
27+ <thead>
28+ <tr>
29+ <th>参数名</th>
30+ <th>输入/输出/属性</th>
31+ <th>描述</th>
32+ <th>数据类型</th>
33+ <th>数据格式</th>
34+ </tr></thead>
35+ <tbody>
36+ <tr>
37+ <td>x</td>
38+ <td>输入</td>
39+ <td>输入x,形状为[NCHW]或[NHWC]</td>
40+ <td>FLOAT16、BFLOAT16、FLOAT</td>
41+ <td>NCHW、NHWC</td>
42+ </tr>
43+ <tr>
44+ <td>grad</td>
45+ <td>输入</td>
46+ <td>梯度Tensor,形状为[NCHW]或[NHWC]</td>
47+ <td>FLOAT16、BFLOAT16、FLOAT</td>
48+ <td>NCHW、NHWC</td>
49+ </tr>
50+ <tr>
51+ <td>argmax</td>
52+ <td>输入</td>
53+ <td>正向最大池化输出的索引,形状与grad相同</td>
54+ <td>INT32、INT64</td>
55+ <td>NCHW、NHWC</td>
56+ </tr>
57+ <tr>
58+ <td>ksize</td>
59+ <td>属性(必选)</td>
60+ <td>池化窗口大小,长度为4的列表。NCHW格式时ksize[0]=1且ksize[1]=1;NHWC格式时ksize[0]=1且ksize[3]=1</td>
61+ <td>ListInt</td>
62+ <td>ND</td>
63+ </tr>
64+ <tr>
65+ <td>strides</td>
66+ <td>属性(必选)</td>
67+ <td>窗口移动步长,长度为4的列表。NCHW格式时strides[0]=1且strides[1]=1;NHWC格式时strides[0]=1且strides[3]=1</td>
68+ <td>ListInt</td>
69+ <td>ND</td>
70+ </tr>
71+ <tr>
72+ <td>padding</td>
73+ <td>属性(必选)</td>
74+ <td>填充算法,取值为"SAME"或"VALID"</td>
75+ <td>String</td>
76+ <td>ND</td>
77+ </tr>
78+ <tr>
79+ <td>include_batch_in_index</td>
80+ <td>属性(可选)</td>
81+ <td>是否在计算argmax索引时包含batch维度。当前仅支持false。默认值:false</td>
82+ <td>Bool</td>
83+ <td>ND</td>
84+ </tr>
85+ <tr>
86+ <td>data_format</td>
87+ <td>属性(可选)</td>
88+ <td>数据布局格式,取值为"NHWC"或"NCHW"。默认值:"NHWC"</td>
89+ <td>String</td>
90+ <td>ND</td>
91+ </tr>
92+ <tr>
93+ <td>y</td>
94+ <td>输出</td>
95+ <td>输出梯度,形状与x相同</td>
96+ <td>FLOAT16、BFLOAT16、FLOAT</td>
97+ <td>NCHW、NHWC</td>
98+ </tr>
99+ </tbody></table>
100+ 
101+## 约束说明
102+ 
103+- include_batch_in_index:当前仅支持false
104+ 
105+## 调用说明
106+ 
107+| 调用方式 | 样例代码 | 说明 |
108+| ---------------- | --------------------------- | --------------------------------------------------- |
109+| 图模式接口 | [test_geir_max_pool_grad_with_argmax](examples/arch35/test_geir_max_pool_grad_with_argmax.cpp) | 通过IR [MaxPoolGradWithArgmax](./op_graph/max_pool_grad_with_argmax_proto.h)构图方式调用MaxPoolGradWithArgmax算子。 |
@@ -0,0 +1,320 @@
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 <iostream>
12+#include <fstream>
13+#include <string.h>
14+#include <stdint.h>
15+#include <vector>
16+#include <string>
17+#include <map>
18+#include "assert.h"
19+ 
20+#include "graph.h"
21+#include "types.h"
22+#include "tensor.h"
23+#include "ge_error_codes.h"
24+#include "ge_api_types.h"
25+#include "ge_api.h"
26+#include "array_ops.h"
27+#include "ge_ir_build.h"
28+ 
29+#include "experiment_ops.h"
30+#include "nn_other.h"
31+#include "../../op_graph/max_pool_grad_with_argmax_proto.h"
32+ 
33+#define FAILED -1
34+#define SUCCESS 0
35+ 
36+using namespace ge;
37+using std::map;
38+using std::string;
39+using std::vector;
40+#define ADD_INPUT(inputIndex, inputName, inputDtype, inputShape) \
41+ vector<int64_t> placeholder##inputIndex##_shape = inputShape; \
42+ auto placeholder##inputIndex = op::Data("placeholder" + inputIndex).set_attr_index(0); \
43+ TensorDesc placeholder##inputIndex##_desc = \
44+ TensorDesc(ge::Shape(placeholder##inputIndex##_shape), FORMAT_ND, inputDtype); \
45+ placeholder##inputIndex##_desc.SetPlacement(ge::kPlacementHost); \
46+ placeholder##inputIndex##_desc.SetFormat(FORMAT_ND); \
47+ Tensor tensor_placeholder##inputIndex; \
48+ ret = GenOnesData( \
49+ placeholder##inputIndex##_shape, tensor_placeholder##inputIndex, placeholder##inputIndex##_desc, inputDtype, \
50+ 2); \
51+ if (ret != SUCCESS) { \
52+ printf("%s - ERROR - [XIR]: Generate input data failed\n", GetTime().c_str()); \
53+ return FAILED; \
54+ } \
55+ placeholder##inputIndex.update_input_desc_x(placeholder##inputIndex##_desc); \
56+ input.push_back(tensor_placeholder##inputIndex); \
57+ graph.AddOp(placeholder##inputIndex); \
58+ add1.set_input_##inputName(placeholder##inputIndex); \
59+ inputs.push_back(placeholder##inputIndex)
60+ 
61+#define ADD_OUTPUT(outputIndex, outputName, outputDtype, outputShape) \
62+ TensorDesc outputName##outputIndex##_desc = TensorDesc(ge::Shape(outputShape), FORMAT_ND, outputDtype); \
63+ add1.update_output_desc_##outputName(outputName##outputIndex##_desc)
64+ 
65+#define LOG_PRINT(message, ...) \
66+ do { \
67+ printf(message, ##__VA_ARGS__); \
68+ } while (0)
69+ 
70+#define ADD_INPUT_ATTR(attrName, attrValue) add1.set_attr_##attrName(attrValue)
71+ 
72+string GetTime()
73+{
74+ time_t timep;
75+ time(&timep);
76+ char tmp[64];
77+ strftime(tmp, sizeof(tmp), "%Y-%m-%d %H:%M:%S,000", localtime(&timep));
78+ return tmp;
79+}
80+ 
81+uint32_t GetDataTypeSize(DataType dt)
82+{
83+ uint32_t dilation = 1;
84+ uint32_t oneByte = 1;
85+ uint32_t twoByte = 2;
86+ uint32_t fourByte = 4;
87+ uint32_t eightByte = 8;
88+ 
89+ if (dt == ge::DT_FLOAT) {
90+ dilation = fourByte;
91+ } else if (dt == ge::DT_FLOAT16) {
92+ dilation = twoByte;
93+ } else if (dt == ge::DT_BF16) {
94+ dilation = twoByte;
95+ } else if (dt == ge::DT_INT16) {
96+ dilation = twoByte;
97+ } else if (dt == ge::DT_UINT16) {
98+ dilation = twoByte;
99+ } else if (dt == ge::DT_INT32) {
100+ dilation = fourByte;
101+ } else if (dt == ge::DT_UINT32) {
102+ dilation = fourByte;
103+ } else if (dt == ge::DT_INT64) {
104+ dilation = eightByte;
105+ } else if (dt == ge::DT_UINT64) {
106+ dilation = eightByte;
107+ } else if (dt == ge::DT_INT8) {
108+ dilation = oneByte;
109+ }
110+ return dilation;
111+}
112+ 
113+int32_t GenOnesData(
114+ vector<int64_t> shapes, Tensor& input_tensor, TensorDesc& input_tensor_desc, DataType data_type, int value)
115+{
116+ input_tensor_desc.SetRealDimCnt(shapes.size());
117+ size_t size = 1;
118+ for (uint32_t i = 0; i < shapes.size(); i++) {
119+ size *= shapes[i];
120+ }
121+ uint32_t data_len = size * GetDataTypeSize(data_type);
122+ int32_t* pData = new (std::nothrow) int32_t[data_len];
123+ for (uint32_t i = 0; i < size; ++i) {
124+ *(pData + i) = value;
125+ }
126+ input_tensor = Tensor(input_tensor_desc, reinterpret_cast<uint8_t*>(pData), data_len);
127+ return SUCCESS;
128+}
129+ 
130+int32_t WriteDataToFile(string bin_file, uint64_t data_size, uint8_t* inputData)
131+{
132+ FILE* fp = fopen(bin_file.c_str(), "w");
133+ fwrite(inputData, sizeof(uint8_t), data_size, fp);
134+ fclose(fp);
135+ return SUCCESS;
136+}
137+ 
138+int CreateOppInGraph(
139+ DataType inDtype, std::vector<ge::Tensor>& input, std::vector<Operator>& inputs, std::vector<Operator>& outputs,
140+ Graph& graph)
141+{
142+ Status ret = SUCCESS;
143+ auto add1 = op::MaxPoolGradWithArgmax("max_pool_grad_with_argmax");
144+ vector<vector<int64_t>> shapes = {
145+ {4, 1, 4, 4}, // x shape (input)
146+ {4, 1, 2, 2}, // grad shape (gradient input)
147+ {4, 1, 2, 2} // argmax shape (indices from forward)
148+ };
149+ vector<vector<int64_t>> attrs = {
150+ {1, 1, 2, 2}, // ksize
151+ {1, 1, 2, 2} // strides
152+ };
153+ 
154+ ADD_INPUT(1, x, inDtype, shapes[0]);
155+ ADD_INPUT(2, grad, inDtype, shapes[1]);
156+ ADD_INPUT(3, argmax, DT_INT32, shapes[2]);
157+ ADD_OUTPUT(4, y, inDtype, shapes[0]);
158+ ADD_INPUT_ATTR(ksize, attrs[0]);
159+ ADD_INPUT_ATTR(strides, attrs[1]);
160+ ADD_INPUT_ATTR(padding, "VALID");
161+ ADD_INPUT_ATTR(include_batch_in_index, false);
162+ ADD_INPUT_ATTR(data_format, "NCHW");
163+ outputs.push_back(add1);
164+ return SUCCESS;
165+}
166+ 
167+bool InitEnv()
168+{
169+ printf("%s - INFO - [XIR]: Start to initialize ge using ge global options\n", GetTime().c_str());
170+ std::map<AscendString, AscendString> global_options = {{"ge.exec.deviceId", "0"}, {"ge.graphRunMode", "1"}};
171+ Status ret = ge::GEInitialize(global_options);
172+ if (ret != SUCCESS) {
173+ printf("%s - INFO - [XIR]: Initialize ge using ge global options failed\n", GetTime().c_str());
174+ return false;
175+ }
176+ printf("%s - INFO - [XIR]: Initialize ge using ge global options success\n", GetTime().c_str());
177+ return true;
178+}
179+ 
180+bool CreateAndConfigGraph(Graph& graph, std::vector<ge::Tensor>& input)
181+{
182+ printf("%s - INFO - [XIR]: Start to CreateAndConfigGraph\n", GetTime().c_str());
183+ std::vector<Operator> inputs{};
184+ std::vector<Operator> outputs{};
185+ 
186+ DataType inDtype = DT_FLOAT16;
187+ std::cout << inDtype << std::endl;
188+ 
189+ Status ret = CreateOppInGraph(inDtype, input, inputs, outputs, graph);
190+ if (ret != SUCCESS) {
191+ printf("%s - ERROR - [XIR]: Create ir session using build options failed\n", GetTime().c_str());
192+ return false;
193+ }
194+ 
195+ if (!inputs.empty() && !outputs.empty()) {
196+ graph.SetInputs(inputs).SetOutputs(outputs);
197+ }
198+ return true;
199+}
200+ 
201+bool AddGraphToSession(ge::Session* session, Graph& graph, uint32_t graph_id)
202+{
203+ printf("%s - INFO - [XIR]: Create ir session using build options success\n", GetTime().c_str());
204+ 
205+ printf("%s - INFO - [XIR]: Start to add compute graph to ir session\n", GetTime().c_str());
206+ 
207+ std::map<AscendString, AscendString> graph_options = {
208+ 
209+ };
210+ 
211+ Status ret = session->AddGraph(graph_id, graph, graph_options);
212+ if (ret != SUCCESS) {
213+ printf("%s - ERROR - [XIR]: Add graph to session failed\n", GetTime().c_str());
214+ return false;
215+ }
216+ return true;
217+}
218+ 
219+bool RunGraph(ge::Session* session, uint32_t graph_id, std::vector<ge::Tensor>& input, std::vector<ge::Tensor>& output)
220+{
221+ printf("%s - INFO - [XIR]: Start to run graph\n", GetTime().c_str());
222+ 
223+ Status ret = session->RunGraph(graph_id, input, output);
224+ if (ret != SUCCESS) {
225+ printf("%s - ERROR - [XIR]: Run graph failed\n", GetTime().c_str());
226+ return false;
227+ }
228+ return true;
229+}
230+ 
231+bool FinalizeEnv()
232+{
233+ printf("%s - INFO - [XIR]: Start to finalize ge\n", GetTime().c_str());
234+ Status ret = ge::GEFinalize();
235+ if (ret != SUCCESS) {
236+ printf("%s - INFO - [XIR]: Finalize ge failed\n", GetTime().c_str());
237+ return false;
238+ }
239+ printf("%s - INFO - [XIR]: Finalize ge success\n", GetTime().c_str());
240+ return true;
241+}
242+ 
243+int main(int argc, char* argv[])
244+{
245+ std::vector<ge::Tensor> input{};
246+ std::vector<ge::Tensor> output{};
247+ Graph graph("max_pool_grad_with_argmax_graph");
248+ 
249+ if (!InitEnv()) {
250+ return FAILED;
251+ }
252+ 
253+ if (!CreateAndConfigGraph(graph, input)) {
254+ return FAILED;
255+ }
256+ 
257+ std::map<ge::AscendString, ge::AscendString> session_options = {};
258+ ge::Session* session = new ge::Session(session_options);
259+ if (session == nullptr) {
260+ return FAILED;
261+ }
262+ 
263+ if (!AddGraphToSession(session, graph, 0)) {
264+ return FAILED;
265+ }
266+ 
267+ if (!RunGraph(session, 0, input, output)) {
268+ return FAILED;
269+ }
270+ printf("%s - INFO - [XIR]: Session run ir compute graph success\n", GetTime().c_str());
271+ 
272+ int input_num = input.size();
273+ for (int i = 0; i < input_num; i++) {
274+ std::cout << "input " << i << " dtype : " << input[i].GetTensorDesc().GetDataType() << std::endl;
275+ string input_file = "./max_pool_grad_with_argmax_npu_input_" + std::to_string(i) + ".bin";
276+ uint8_t* input_data_i = input[i].GetData();
277+ int64_t input_shape = input[i].GetTensorDesc().GetShape().GetShapeSize();
278+ std::cout << "this is " << i << "th input, input shape size =" << input_shape << std::endl;
279+ uint32_t data_size = input_shape * GetDataTypeSize(input[i].GetTensorDesc().GetDataType());
280+ WriteDataToFile((const char*)input_file.c_str(), data_size, input_data_i);
281+ }
282+ 
283+ int output_num = output.size();
284+ for (int i = 0; i < output_num; i++) {
285+ std::cout << "output " << i << " dtype : " << output[i].GetTensorDesc().GetDataType() << std::endl;
286+ string output_file = "./max_pool_grad_with_argmax_npu_output_" + std::to_string(i) + ".bin";
287+ uint8_t* output_data_i = output[i].GetData();
288+ int64_t output_shape = output[i].GetTensorDesc().GetShape().GetShapeSize();
289+ std::cout << "this is " << i << "th output, output shape size =" << output_shape << std::endl;
290+ uint32_t data_size = output_shape * GetDataTypeSize(output[i].GetTensorDesc().GetDataType());
291+ WriteDataToFile((const char*)output_file.c_str(), data_size, output_data_i);
292+ 
293+ std::cout << "output y (gradient result):" << std::endl;
294+ if (output[i].GetTensorDesc().GetDataType() == DT_FLOAT16) {
295+ uint16_t* data = reinterpret_cast<uint16_t*>(output_data_i);
296+ for (int j = 0; j < std::min(output_shape, (int64_t)16); j++) {
297+ printf(" y[%d] = %u (float16)\n", j, data[j]);
298+ }
299+ }
300+ }
301+ 
302+ ge::AscendString error_msg = ge::GEGetErrorMsgV2();
303+ std::string error_str(error_msg.GetString());
304+ std::cout << "Error message: " << error_str << std::endl;
305+ ge::AscendString warning_msg = ge::GEGetWarningMsgV2();
306+ std::string warning_str(warning_msg.GetString());
307+ std::cout << "Warning message: " << warning_str << std::endl;
308+ printf("%s - INFO - [XIR]: Precision is ok\n", GetTime().c_str());
309+ 
310+ if (!FinalizeEnv()) {
311+ return FAILED;
312+ }
313+ 
314+ if (session != nullptr) {
315+ delete session;
316+ }
317+ 
318+ printf("%s - INFO - [XIR]: Test case passed successfully\n", GetTime().c_str());
319+ return SUCCESS;
320+}