已关闭
【社区任务】ApplyAdamW算子设计文档 #4058
StarLightOn创建于 4月21日关闭于 5月15日
【社区任务】ApplyAdamW算子设计文档 #4058
已关闭
StarLightOn创建于 4月21日关闭于 5月15日
共 2 个文件变更+492-0
@@ -0,0 +1,71 @@
1+# Quantize
2+ 
3+## 产品支持情况
4+ 
5+| 产品 | 是否支持 |
6+| :----------------------------------------------------------- | :------: |
7+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
8+ 
9+## 功能说明
10+ 
11+- 算子功能:对输入张量x进行量化处理。
12+ 
13+- 计算公式:
14+ 
15+ $$
16+ out=round((x/scales)+zeroPoints)
17+ $$
18+ 
19+## 参数说明
20+ 
21+<table style="undefined;table-layout: fixed; width: 820px"><colgroup>
22+ <col style="width: 100px">
23+ <col style="width: 150px">
24+ <col style="width: 190px">
25+ <col style="width: 260px">
26+ <col style="width: 120px">
27+ </colgroup>
28+ <thead>
29+ <tr>
30+ <th>参数名</th>
31+ <th>输入/输出/属性</th>
32+ <th>描述</th>
33+ <th>数据类型</th>
34+ <th>数据格式</th>
35+ </tr></thead>
36+ <tbody>
37+ <tr>
38+ <td>x</td>
39+ <td>输入</td>
40+ <td>公式中的x</td>
41+ <td>FLOAT、FLOAT16、BFLOAT16</td>
42+ <td>ND</td>
43+ </tr>
44+ <tr>
45+ <td>scales</td>
46+ <td>输入</td>
47+ <td>公式中的scales</td>
48+ <td>FLOAT、BFLOAT16</td>
49+ <td>ND</td>
50+ </tr>
51+ <tr>
52+ <td>zero_points</td>
53+ <td>输入</td>
54+ <td>公式中的zeroPoints</td>
55+ <td>FLOAT、INT32、INT8、UINT8、BFLOAT16</td>
56+ <td>-</td>
57+ </tr>
58+ <tr>
59+ <td>y</td>
60+ <td>输出</td>
61+ <td>公式中的输出张量out</td>
62+ <td>INT8、UINT8、INT32、HIFLOAT8、FLOAT8_E5M2、FLOAT8_E4M3FN</td>
63+ <td>ND</td>
64+ </tr>
65+ </tbody></table>
66+ 
67+## 调用说明
68+ 
69+| 调用方式 | 样例代码 | 说明 |
70+| ---------------- | --------------------------- | --------------------------------------------------- |
71+| aclnn接口 | [test_aclnn_quantize.cpp](examples/test_aclnn_quantize.cpp) | 通过[aclnnQuantize.md](docs/aclnnQuantize.md)接口方式调用算子。 |
@@ -0,0 +1,421 @@
1+# aclnnQuantize
2+ 
3+## 产品支持情况
4+ 
5+|产品 | 是否支持 |
6+|:-------------------------|:----------:|
7+| <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term> | √ |
8+ 
9+## 功能说明
10+ 
11+- 接口功能:对输入张量x进行量化处理。
12+- 计算公式:
13+
14+ $$
15+ out=round((x/scales)+zeroPoints)
16+ $$
17+
18+## 函数原型
19+ 
20+每个算子分为[两段式接口](../../../../docs/zh/context/两段式接口.md),必须先调用“aclnnQuantizeGetWorkspaceSize”接口获取计算所需workspace大小以及包含了算子计算流程的执行器,再调用“aclnnQuantize”接口执行计算。
21+ 
22+```Cpp
23+aclnnStatus aclnnQuantizeGetWorkspaceSize(
24+ const aclTensor* x,
25+ const aclTensor* scales,
26+ const aclTensor* zeroPoints,
27+ aclDataType dtype,
28+ int32_t axis,
29+ aclTensor* out,
30+ uint64_t* workspaceSize,
31+ aclOpExecutor** executor)
32+```
33+ 
34+```Cpp
35+aclnnStatus aclnnQuantize(
36+ void *workspace,
37+ uint64_t workspaceSize,
38+ aclOpExecutor *executor,
39+ aclrtStream stream)
40+```
41+ 
42+## aclnnQuantizeGetWorkspaceSize
43+ 
44+- **参数说明**
45+ 
46+ <table style="undefined;table-layout: fixed; width: 1550px"><colgroup>
47+ <col style="width: 170px">
48+ <col style="width: 120px">
49+ <col style="width: 271px">
50+ <col style="width: 330px">
51+ <col style="width: 223px">
52+ <col style="width: 101px">
53+ <col style="width: 190px">
54+ <col style="width: 145px">
55+ </colgroup>
56+ <thead>
57+ <tr>
58+ <th>参数名</th>
59+ <th>输入/输出</th>
60+ <th>描述</th>
61+ <th>使用说明</th>
62+ <th>数据类型</th>
63+ <th>数据格式</th>
64+ <th>维度(shape)</th>
65+ <th>非连续Tensor</th>
66+ </tr></thead>
67+ <tbody>
68+ <tr>
69+ <td>x(aclTensor*)</td>
70+ <td>输入</td>
71+ <td>表示需要进行量化的源数据张量。对应公式中的`x`。</td>
72+ <td>支持空Tensor。</td>
73+ <td>FLOAT32、FLOAT16、BFLOAT16</td>
74+ <td>ND</td>
75+ <td>1-8</td>
76+ <td>√</td>
77+ </tr>
78+ <tr>
79+ <td>scales(aclTensor*)</td>
80+ <td>输入</td>
81+ <td>表示量化过程中对x进行scales的张量。对应公式中的`scales`。</td>
82+ <td><ul><li>支持空Tensor。</li><li>size需要为1或和输入x中axis轴的size相等。</li><li>如果`x`的dtype不是FLOAT32,需要和`x`的dtype一致。</li></ul></td>
83+ <td>FLOAT32、FLOAT16、BFLOAT16</td>
84+ <td>ND</td>
85+ <td>1</td>
86+ <td>√</td>
87+ </tr>
88+ <tr>
89+ <td>zeroPoints(aclTensor*)</td>
90+ <td>输入</td>
91+ <td>表示量化过程中对x进行offset的张量,对应公式中的`zeroPoints`。</td>
92+ <td><ul><li>支持空Tensor。</li><li>支持传入空指针。</li><li>size需要为1或和输入x中axis轴的size相等,并与scales的size相等。</li></ul></td>
93+ <td>INT32、INT8、UINT8、FLOAT32、BFLOAT16</td>
94+ <td>ND</td>
95+ <td>1</td>
96+ <td>√</td>
97+ </tr>
98+ <tr>
99+ <td>dstType(aclDataType)</td>
100+ <td>输入</td>
101+ <td>指定输出的数据类型。</td>
102+ <td>支持配置为ACL_INT8、ACL_UINT8、ACL_INT32、ACL_HIFLOAT8、ACL_FLOAT8_E4M3FN、ACL_FLOAT8_E5M2。</td>
103+ <td>-</td>
104+ <td>-</td>
105+ <td>-</td>
106+ <td>-</td>
107+ </tr>
108+ <tr>
109+ <td>axis(int32_t)</td>
110+ <td>输入</td>
111+ <td>表示需要进行量化的element-wise轴,其他的轴做broadcast。</td>
112+ <td><ul><li>当输入的scales和zeroPoints的size均为1时,该参数实际不使用。</li><li>支持范围为小于输入x的维度数且大于等于x维度数的负值。</li></ul></td>
113+ <td>-</td>
114+ <td>-</td>
115+ <td>-</td>
116+ <td>-</td>
117+ </tr>
118+ <tr>
119+ <td>out(aclTensor*)</td>
120+ <td>输出</td>
121+ <td>表示量化输出Tensor。对应公式中的`out`。</td>
122+ <td><ul><li>支持空Tensor。</li><li>其shape需要与`x`一致,数据类型由入参`dstType`指定。</li></ul></td>
123+ <td>INT8、UINT8、INT32、HIFLOAT8、FLOAT8_E4M3FN、FLOAT8_E5M2</td>
124+ <td>ND</td>
125+ <td>1-8</td>
126+ <td>√</td>
127+ </tr>
128+ <tr>
129+ <td>workspaceSize(uint64_t*)</td>
130+ <td>输出</td>
131+ <td>返回需要在Device侧申请的workspace大小。</td>
132+ <td>-</td>
133+ <td>-</td>
134+ <td>-</td>
135+ <td>-</td>
136+ <td>-</td>
137+ </tr>
138+ <tr>
139+ <td>executor(aclOpExecutor**)</td>
140+ <td>输出</td>
141+ <td>返回op执行器,包含了算子计算流程。</td>
142+ <td>-</td>
143+ <td>-</td>
144+ <td>-</td>
145+ <td>-</td>
146+ <td>-</td>
147+ </tr>
148+ </tbody>
149+ </table>
150+
151+ - <term>Atlas 推理系列产品</term>:
152+ - 数据类型:
153+ - 入参`x`、`scales`不支持BFLOAT16、FLOAT32。
154+ - 入参`zeroPoints`不支持FLOAT32。且当数据类型为BFLOAT16时,`x`、`scales`数据类型均为BFLOAT16。
155+ - 出参`out`仅支持支持INT8、UINT8、INT32。
156+ - 入参`dstType`仅支持取值ACL_INT8、ACL_UINT8、ACL_INT32。
157+ 
158+ - <term>Atlas A2 训练系列产品/Atlas A2 推理系列产品</term>、<term>Atlas A3 训练系列产品/Atlas A3 推理系列产品</term>:
159+ - 数据类型:
160+ - 入参`zeroPoints`不支持FLOAT32。且当数据类型为BFLOAT16时,`x`、`scales`数据类型均为BFLOAT16。
161+ - 出参`out`仅支持INT8、UINT8、INT32。
162+ - 入参`dstType`仅支持取值ACL_INT8、ACL_UINT8、ACL_INT32。
163+
164+- **返回值**
165+ 
166+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
167+
168+ 第一段接口完成入参校验,出现以下场景时报错:
169+ 
170+ <table style="undefined;table-layout: fixed;width: 1170px"><colgroup>
171+ <col style="width: 268px">
172+ <col style="width: 140px">
173+ <col style="width: 762px">
174+ </colgroup>
175+ <thead>
176+ <tr>
177+ <th>返回码</th>
178+ <th>错误码</th>
179+ <th>描述</th>
180+ </tr>
181+ </thead>
182+ <tbody>
183+ <tr>
184+ <td>ACLNN_ERR_PARAM_NULLPTR</td>
185+ <td>161001</td>
186+ <td>传入的x、scales或out是空指针。</td>
187+ </tr>
188+ <tr>
189+ <td rowspan="7">ACLNN_ERR_PARAM_INVALID</td>
190+ <td rowspan="7">161002</td>
191+ <td>x、scales、zeroPoints或out的数据类型/数据格式/维度不在支持的范围之内。</td>
192+ </tr>
193+ <tr>
194+ <td>输入axis指定的轴超出输入x的维度数。</td>
195+ </tr>
196+ <tr>
197+ <td>dstType不在有效取值范围。</td>
198+ </tr>
199+ <tr>
200+ <td>输入scales和zeroPoints的size不相等。</td>
201+ </tr>
202+ <tr>
203+ <td>输入scales和zeroPoints的size不为1时,与输入axis指定轴的size不相等。</td>
204+ </tr>
205+ <tr>
206+ <td>输入out的数据类型与输入dstTyped的取值不一致。</td>
207+ </tr>
208+ </tbody></table>
209+ 
210+## aclnnQuantize
211+ 
212+- **参数说明**
213+ 
214+ <table style="undefined;table-layout: fixed; width: 953px"><colgroup>
215+ <col style="width: 173px">
216+ <col style="width: 112px">
217+ <col style="width: 668px">
218+ </colgroup>
219+ <thead>
220+ <tr>
221+ <th>参数名</th>
222+ <th>输入/输出</th>
223+ <th>描述</th>
224+ </tr></thead>
225+ <tbody>
226+ <tr>
227+ <td>workspace</td>
228+ <td>输入</td>
229+ <td>在Device侧申请的workspace内存地址。</td>
230+ </tr>
231+ <tr>
232+ <td>workspaceSize</td>
233+ <td>输入</td>
234+ <td>在Device侧申请的workspace大小,由第一段接口aclnnQuantizeGetWorkspaceSize获取。</td>
235+ </tr>
236+ <tr>
237+ <td>executor</td>
238+ <td>输入</td>
239+ <td>op执行器,包含了算子计算流程。</td>
240+ </tr>
241+ <tr>
242+ <td>stream</td>
243+ <td>输入</td>
244+ <td>指定执行任务的Stream。</td>
245+ </tr>
246+ </tbody>
247+ </table>
248+ 
249+- **返回值**
250+ 
251+ aclnnStatus:返回状态码,具体参见[aclnn返回码](../../../../docs/zh/context/aclnn返回码.md)。
252+ 
253+## 约束说明
254+ 
255+- 确定性计算:
256+ - aclnnQuantize默认确定性实现。
257+ 
258+## 调用示例
259+ 
260+示例代码如下,仅供参考,具体编译和执行过程请参考[编译与运行样例](../../../../docs/zh/context/编译与运行样例.md)。
261+ 
262+```Cpp
263+#include <iostream>
264+#include <vector>
265+#include "acl/acl.h"
266+#include "aclnnop/aclnn_quantize.h"
267+ 
268+#define CHECK_RET(cond, return_expr) \
269+ do { \
270+ if (!(cond)) { \
271+ return_expr; \
272+ } \
273+ } while (0)
274+ 
275+#define LOG_PRINT(message, ...) \
276+ do { \
277+ printf(message, ##__VA_ARGS__); \
278+ } while (0)
279+ 
280+int64_t GetShapeSize(const std::vector<int64_t>& shape)
281+{
282+ int64_t shapeSize = 1;
283+ for (auto i : shape) {
284+ shapeSize *= i;
285+ }
286+ return shapeSize;
287+}
288+ 
289+int Init(int32_t deviceId, aclrtStream* stream)
290+{
291+ // 固定写法,资源初始化
292+ auto ret = aclInit(nullptr);
293+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
294+ ret = aclrtSetDevice(deviceId);
295+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
296+ ret = aclrtCreateStream(stream);
297+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
298+ return 0;
299+}
300+ 
301+template <typename T>
302+int CreateAclTensor(
303+ const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr, aclDataType dataType,
304+ aclTensor** tensor)
305+{
306+ auto size = GetShapeSize(shape) * sizeof(T);
307+ // 调用aclrtMalloc申请device侧内存
308+ auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
309+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
310+ // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
311+ ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
312+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);
313+ 
314+ // 计算连续tensor的strides
315+ std::vector<int64_t> strides(shape.size(), 1);
316+ for (int64_t i = shape.size() - 2; i >= 0; i--) {
317+ strides[i] = shape[i + 1] * strides[i + 1];
318+ }
319+ 
320+ // 调用aclCreateTensor接口创建aclTensor
321+ *tensor = aclCreateTensor(
322+ shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
323+ *deviceAddr);
324+ return 0;
325+}
326+ 
327+int main()
328+{
329+ // 1. (固定写法)device/stream初始化,参考acl API手册
330+ // 根据自己的实际device填写deviceId
331+ int32_t deviceId = 0;
332+ aclrtStream stream;
333+ auto ret = Init(deviceId, &stream);
334+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
335+ 
336+ // 2. 构造输入与输出,需要根据API的接口自定义构造
337+ std::vector<int64_t> xShape = {4, 2};
338+ std::vector<int64_t> scalesShape = {2};
339+ std::vector<int64_t> zeroPointsShape = {2};
340+ std::vector<int64_t> outShape = {4, 2};
341+ void* xDeviceAddr = nullptr;
342+ void* scalesDeviceAddr = nullptr;
343+ void* zeroPointsDeviceAddr = nullptr;
344+ void* outDeviceAddr = nullptr;
345+ aclTensor* x = nullptr;
346+ aclTensor* scales = nullptr;
347+ aclTensor* zeroPoints = nullptr;
348+ aclTensor* out = nullptr;
349+ 
350+ aclDataType dataType = ACL_INT32;
351+ int32_t axis = 1;
352+ std::vector<float> scalesHostData = {1.0, -3.0};
353+ std::vector<int32_t> zeroPointsData = {2, 10};
354+ std::vector<float> xHostData = {0.3382, -0.0919, 0.7564, 0.0234, 3.1024, 1.0761, 0.4228, 1.4621};
355+ std::vector<int32_t> outHostData = {8, 0};
356+ 
357+ // 创建x aclTensor
358+ ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT, &x);
359+ CHECK_RET(ret == ACL_SUCCESS, return ret);
360+ // 创建scales aclTensor
361+ ret = CreateAclTensor(scalesHostData, scalesShape, &scalesDeviceAddr, aclDataType::ACL_FLOAT, &scales);
362+ CHECK_RET(ret == ACL_SUCCESS, return ret);
363+ // 创建zeroPoints aclTensor
364+ ret = CreateAclTensor(zeroPointsData, zeroPointsShape, &zeroPointsDeviceAddr, aclDataType::ACL_INT32, &zeroPoints);
365+ CHECK_RET(ret == ACL_SUCCESS, return ret);
366+ // 创建out aclTensor
367+ ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, dataType, &out);
368+ CHECK_RET(ret == ACL_SUCCESS, return ret);
369+ 
370+ // 3. 调用CANN算子库API,需要修改为具体的API名称
371+ uint64_t workspaceSize = 0;
372+ aclOpExecutor* executor;
373+ // 调用aclnnQuantize第一段接口
374+ ret = aclnnQuantizeGetWorkspaceSize(x, scales, zeroPoints, dataType, axis, out, &workspaceSize, &executor);
375+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantizeGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
376+ // 根据第一段接口计算出的workspaceSize申请device内存
377+ void* workspaceAddr = nullptr;
378+ if (workspaceSize > 0) {
379+ ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
380+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
381+ }
382+ // 调用aclnnQuantize第二段接口
383+ ret = aclnnQuantize(workspaceAddr, workspaceSize, executor, stream);
384+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnQuantize failed. ERROR: %d\n", ret); return ret);
385+ 
386+ // 4. (固定写法)同步等待任务执行结束
387+ ret = aclrtSynchronizeStream(stream);
388+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);
389+ 
390+ // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改
391+ auto outSize = GetShapeSize(outShape);
392+ std::vector<int32_t> outData(outSize, 0);
393+ ret = aclrtMemcpy(
394+ outData.data(), outData.size() * sizeof(outData[0]), outDeviceAddr, outSize * sizeof(outData[0]),
395+ ACL_MEMCPY_DEVICE_TO_HOST);
396+ CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
397+ for (int64_t i = 0; i < outSize; i++) {
398+ LOG_PRINT("out[%ld] is: %d\n", i, outData[i]);
399+ }
400+ 
401+ // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
402+ aclDestroyTensor(x);
403+ aclDestroyTensor(scales);
404+ aclDestroyTensor(zeroPoints);
405+ aclDestroyTensor(out);
406+ 
407+ // 7. 释放device资源,需要根据具体API的接口定义修改
408+ aclrtFree(xDeviceAddr);
409+ aclrtFree(scalesDeviceAddr);
410+ aclrtFree(zeroPointsDeviceAddr);
411+ aclrtFree(outDeviceAddr);
412+ 
413+ if (workspaceSize > 0) {
414+ aclrtFree(workspaceAddr);
415+ }
416+ aclrtDestroyStream(stream);
417+ aclrtResetDevice(deviceId);
418+ aclFinalize();
419+ return 0;
420+}
421+```