下载
中文
注册

aclnnAvgPool2dBackward

接口原型

每个算子有两段接口,必须先调用“aclnnXxxGetWorkspaceSize”接口获取入参并根据计算流程计算所需workspace大小,再调用“aclnnXxx”接口执行计算。两段式接口如下:

  • 第一段接口:aclnnStatus aclnnAvgPool2dBackwardGetWorkspaceSize(const aclTensor *gradOutput, const aclTensor *self, const aclIntArray *kernelSize, const aclIntArray *stride, const aclIntArray *padding, bool ceilMode, bool countIncludePad, int64_t divisorOverride, int8_t cubeMathType, aclTensor *gradInput , uint64_t &workspaceSize, aclOpExecutor **executor)
  • 第二段接口:aclnnStatus aclnnAvgPool2dBackward(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)

功能描述

  • 算子功能:二维平均池化(aclnnAvgPool2d)的反向传播,计算二维平均池化正向传播的输入梯度。
  • 计算公式:

    假设二维平均池化正向的输入特征张量为X,输出特征张量为Y,池化窗口大小为k∗k,步长为s,则X的梯度∂L/∂X​计算公式为

    各参数含义如下:
    • L为损失函数,⌊⋅⌋表示向下取整。
    • Xi, j​:输入特征图中第i行,第j列的像素值。
    • Y⌊(i∗s+m)/k​⌋, ⌊(j∗s+n)/k​⌋​:输出特征图中第⌊(i∗s+m)/k​⌋行,第⌊(j∗s+n)/k​⌋列的像素值。

aclnnAvgPool2dBackwardGetWorkspaceSize

  • 接口定义:

    aclnnStatus aclnnAvgPool2dBackwardGetWorkspaceSize(const aclTensor *gradOutput, const aclTensor *self, const aclIntArray *kernelSize, const aclIntArray *stride, const aclIntArray *padding, bool ceilMode, bool countIncludePad, int64_t divisorOverride, int8_t cubeMathType, aclTensor *gradInput , uint64_t &workspaceSize, aclOpExecutor **executor)

  • 参数说明:
    • gradOutput:Device侧的aclTensor,公式中的∂L/∂Y​,数据类型支持FLOAT16和FLOAT。支持非连续的Tensor。数据格式支持NCHW和NCL。
    • self:Device侧的aclTensor,平均池化正向过程中的输入张量X,数据类型支持FLOAT16和FLOAT。支持非连续的Tensor。数据格式支持NCHW和NCL。
    • kernelSize:Host侧的aclIntArray,INT64的数组,长度为1(kH​=kW​)或2(kH​, kW​),表示池化窗口大小。数值必须大于0。
    • stride:Host侧的aclIntArray,INT64的数组,长度为为1(sH​=sW​)或2(sH​, sW​),表示步长大小。数值必须大于0。
    • padding:Host侧的aclIntArray,平均池化正向传播过程中对于输入的填充,数据类型为INT64,长度为1(padH​=padW)或2(padH, padW​),表示输入的H、W方向上padding补0的层数。支持padding小于0,padding的上限不能超过kernelSize对应位置的一半,比如kernelSize的H为2,padding的H最大只能为1。
    • ceilMode:Host侧的布尔型,表示平均池化正向过程中推导输出shape是否向上取整。数据类型支持BOOL。
    • countIncludePad:Host侧的布尔型,计算平均池化时是否包括padding填充。数据类型支持BOOL。
    • divisorOverride:Host侧的整型,表示取平均的除数。数据类型支持INT64。支持范围为[-255, 255]。如果该值为0,表示不影响padding是否参与平均计算。
    • cubeMathType:Host侧的整型,判断Cube单元应该使用哪种计算逻辑进行运算,支持INT8类型的枚举值,枚举值如下:
      • 0:KEPP_DTYPE,保持输入的数据类型进行计算。
      • 1:ALLOW_FP32_DOWN_PRECISION,允许转换输入数据类型降低精度计算。
    • gradInput:Device侧的aclTensor,公式中的∂L/∂X​。数据类型支持FLOAT16和FLOAT。支持非连续的Tensor。支持数据格式为NCHW和NCL。数据类型、数据格式需要与self一致。
    • workspaceSize:返回用户需要在Device侧申请的workspace大小。
    • executor:返回op执行器,包含了算子计算流程。
  • 返回值:

    返回aclnnStatus状态码,具体参见aclnn返回码

    第一段接口完成入参校验,出现以下场景时报错:

    • 返回161001(ACLNN_ERR_PARAM_NULLPTR):传入的gradOutput、self、kernelSize、stride、padding或gradInput是空指针。
    • 返回161002(ACLNN_ERR_PARAM_INVALID):
      • 传入的gradOutput或gradInput的数据类型或数据格式不在支持的范围之内。
      • 传入的self和gradInput数据类型或数据格式不一致。
      • 传入的gradOutput、self、kernelSize、stride或gradInput维度不在支持的范围内。
      • 传入的gradOutput、self、kernelSize、stride或gradInput在某维度的值小于0。
      • 传入的cubeMathType取值不在支持范围内。
      • padding不能超过kernelSize对应位置的1/2,比如paddingH=2, kerneSizeH=2,paddingH>1/2*kerneSizeH。

aclnnAvgPool2dBackward

  • 接口定义:

    aclnnStatus aclnnAvgPool2dBackward(void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)

  • 参数说明:
    • workspace:在Device侧申请的workspace内存起址。
    • workspaceSize:在Device侧申请的workspace大小,由第一段接口aclnnAvgPool2dBackwardGetWorkspaceSize获取。
    • executor:op执行器,包含了算子计算流程。
    • stream:指定执行任务的AscendCL stream流。
  • 返回值:

    返回aclnnStatus状态码,具体参见aclnn返回码

调用示例

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_avgpool2dbackward.h"

#define CHECK_RET(cond, return_expr) \
  do {                               \
    if (!(cond)) {                   \
      return_expr;                   \
    }                                \
  } while (0)

#define LOG_PRINT(message, ...)     \
  do {                              \
    printf(message, ##__VA_ARGS__); \
  } while (0)

int64_t GetShapeSize(const std::vector<int64_t>& shape) {
  int64_t shapeSize = 1;
  for (auto i : shape) {
    shapeSize *= i;
  }
  return shapeSize;
}

int Init(int32_t deviceId, aclrtContext* context, aclrtStream* stream) {
  // 固定写法,AscendCL初始化
  auto ret = aclInit(nullptr);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclInit failed. ERROR: %d\n", ret); return ret);
  ret = aclrtSetDevice(deviceId);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetDevice failed. ERROR: %d\n", ret); return ret);
  ret = aclrtCreateContext(context, deviceId);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateContext failed. ERROR: %d\n", ret); return ret);
  ret = aclrtSetCurrentContext(*context);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSetCurrentContext failed. ERROR: %d\n", ret); return ret);
  ret = aclrtCreateStream(stream);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtCreateStream failed. ERROR: %d\n", ret); return ret);
  return 0;
}

template <typename T>
int CreateAclTensor(const std::vector<T>& hostData, const std::vector<int64_t>& shape, void** deviceAddr,
                    aclDataType dataType, aclTensor** tensor) {
  auto size = GetShapeSize(shape) * sizeof(T);
  // 调用aclrtMalloc申请device侧内存
  auto ret = aclrtMalloc(deviceAddr, size, ACL_MEM_MALLOC_HUGE_FIRST);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMalloc failed. ERROR: %d\n", ret); return ret);
  // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上
  ret = aclrtMemcpy(*deviceAddr, size, hostData.data(), size, ACL_MEMCPY_HOST_TO_DEVICE);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtMemcpy failed. ERROR: %d\n", ret); return ret);

  // 计算连续tensor的strides
  std::vector<int64_t> stride(shape.size(), 1);
  for (int64_t i = shape.size() - 2; i >= 0; i--) {
    stride[i] = shape[i + 1] * stride[i + 1];
  }

  // 调用aclCreateTensor接口创建aclTensor
  *tensor = aclCreateTensor(shape.data(), shape.size(), dataType, stride.data(), 0, aclFormat::ACL_FORMAT_ND,
                            shape.data(), shape.size(), *deviceAddr);
  return 0;
}

int main() {
  // 1. (固定写法)device/context/stream初始化,参考AscendCL对外接口列表
  // 根据自己的实际device填写deviceId
  int32_t deviceId = 0;
  aclrtContext context;
  aclrtStream stream;
  auto ret = Init(deviceId, &context, &stream);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

  // 2. 构造输入与输出,需要根据API的接口自定义构造
  std::vector<int64_t> gradOutputShape = {1, 16, 1, 1};
  std::vector<int64_t> selfShape = {1, 16, 4, 4};
  std::vector<int64_t> kernelDims = {4, 4};
  std::vector<int64_t> strideDims = {1, 1};
  std::vector<int64_t> paddingDims = {0, 0};
  bool ceilMode = false;
  int64_t divisorOverride = 0;
  bool countIncludePad = true;
  int8_t cubeMathType = 1;
  std::vector<int64_t> gradInputShape = {1, 16, 4, 4};
  void* gradOutputDeviceAddr = nullptr;
  void* selfDeviceAddr = nullptr;
  void* gradInputDeviceAddr = nullptr;
  aclTensor* gradOutput = nullptr;
  aclTensor* self = nullptr;
  aclTensor* gradInput = nullptr;
  std::vector<float> gradOutputHostData(GetShapeSize(gradOutputShape) * 2, 1);
  std::vector<float> selfHostData(GetShapeSize(self) * 2, 1);
  std::vector<float> gradInputHostData(GetShapeSize(gradInputShape) * 2, 1);

  // gradOutput aclTensor
  ret = CreateAclTensor(gradOutputHostData, gradOutputShape, &gradOutputDeviceAddr, aclDataType::ACL_FLOAT, &gradOutput);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  // 创建self aclTensor
  ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  // 创建 gradInput aclTensor
  ret = CreateAclTensor(gradInputputHostData, gradInputShape, &gradInputDeviceAddr, aclDataType::ACL_FLOAT, &gradInput);
  CHECK_RET(ret == ACL_SUCCESS, return ret);
  // 创建kernel aclIntArray
  aclIntArray *kernelSize = aclCreateIntArray(kernelDims.data(), 2);
  CHECK_RET(kernelSize != nullptr, return ACLNN_ERR_PARAM_NULLPTR);
  // 创建stride aclIntArray
  aclIntArray *stride = aclCreateIntArray(strideDims.data(), 2);
  CHECK_RET(stride != nullptr, return ACLNN_ERR_PARAM_NULLPTR);
  // 创建paddings aclIntArray
  aclIntArray *padding = aclCreateIntArray(paddingDims.data(), 2);
  CHECK_RET(padding != nullptr, return ACLNN_ERR_PARAM_NULLPTR);


  // 3. 调用CANN算子库API,需要修改为具体的API名称
  uint64_t workspaceSize = 0;
  aclOpExecutor* executor;
  // 调用aclnnAvgPool2dBackward第一段接口
  ret = aclnnAvgPool2dBackwardGetWorkspaceSize(gradOutput,self,kernelSize,stride,padding,ceilMode,
        countIncludePad,divisorOverride,cubeMathType,gradInput,&workspaceSize, &executor);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAvgPool2dBackwardGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
  // 根据第一段接口计算出的workspaceSize申请device内存
  void* workspaceAddr = nullptr;
  if (workspaceSize > 0) {
    ret = aclrtMalloc(&workspaceAddr, workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("allocate workspace failed. ERROR: %d\n", ret); return ret);
  }
  // 调用aclnnAvgPool2dBackward第二段接口
  ret = aclnnAvgPool2dBackward(workspaceAddr, workspaceSize, executor, stream);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnAvgPool2dBackward failed. ERROR: %d\n", ret); return ret);

  // 4. (固定写法)同步等待任务执行结束
  ret = aclrtSynchronizeStream(stream);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);

  // 5. 获取输出的值,将device侧内存上的结果拷贝至host侧,需要根据具体API的接口定义修改
  auto size = GetShapeSize(gradInputShape);
  std::vector<float> outData(size, 0);
  ret = aclrtMemcpy(outData.data(), outData.size() * sizeof(outData[0]), gradInputDeviceAddr,
                    size * sizeof(outData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
  CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy result from device to host failed. ERROR: %d\n", ret); return ret);
  for (int64_t i = 0; i < size; i++) {
    LOG_PRINT("out result[%ld] is: %f\n", i, outData[i]);
  }

  // 6. 释放aclTensor和aclScalar,需要根据具体API的接口定义修改
  aclDestroyTensor(gradOutput);
  aclDestroyTensor(self);
  aclDestroyTensor(gradInput);
  aclDestroyIntArray(kernelSize);
  aclDestroyIntArray(stride);
  aclDestroyIntArray(padding);
  return 0;
}