aclnnGridSampler2DBackward

📄 View Source Code

Applicable Products

ProductWhether Support
Ascend 950PR/Ascend 950DT√
Atlas A3 training products/Atlas A3 inference products√
Atlas A2 training products/Atlas A2 inference products√
Atlas 200I/500 A2 inference products×
Atlas inference products×
Atlas training products√

Description

  • Description: Performs backward propagation of aclnnGridSampler2D, computing the gradients of the input tensor and the grid tensor.
  • Formula:
    • Calculation Process:

      1. Based on the (x, y) values stored in grid, compute the coordinates mapped onto input. These coordinates are related to alignCorners and paddingMode.
      2. Based on the input interpolationMode, bilinear or nearest interpolation mode is selected to calculate the weight values assigned to the gradient for the points around the coordinate.
      3. The gradient values stored in grad are multiplied by the weight values of the corresponding points to compute the final results of dx and dgrid.
    • Where: The dimensions of grad, input, grid, dx, and dgrid are as follows:

      grad:(N,C,Hout,Wout)input:(N,C,Hin,Win)grid:(N,Hout,Wout,2)dx:(N,C,Hin,Win)dgrid:(N,Hout,Wout,2)grad: (N, C, H_{out}, W_{out})\\ input: (N, C, H_{in}, W_{in})\\ grid: (N, H_{out}, W_{out}, 2)\\ dx: (N, C, H_{in}, W_{in})\\ dgrid: (N, H_{out}, W_{out}, 2)

Where N in grad, input, grid, dx, and dgrid are all the same; C in grad, input, and dx are the same; HinH_{in} and WinW_{in} in input and dx are the same; HoutH_{out} and WoutW_{out} in grad, grid, and dgrid are the same; and the last dimension of grid has a size of 2, indicating that the input pixel position information is (x, y). The value ranges of x and y are normalized to [-1, 1], where (-1, 1) represents the top-left corner coordinate and (1, -1) represents the bottom-right corner coordinate.

- For out-of-range coordinates, different processing is performed based on paddingMode:

  - paddingMode="zeros": fills out-of-bounds positions with 0.
  - paddingMode="border": fills out-of-bounds positions with the boundary value.
  - paddingMode="reflection": fills out-of-bounds positions with the symmetric value of the boundary value.

- When sampling from input, different processing is performed based on interpolationMode:

  - interpolationMode="bilinear" means taking the weighted average of the four coordinates around (x, y) in input.
  - interpolationMode="nearest" means taking the coordinate value in input that is closest to (x, y).

Function Prototype

Each operator is divided into a two-phase API. You must first call the "aclnnGridSampler2DBackwardGetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnGridSampler2DBackward" API to perform the computation.

aclnnStatus aclnnGridSampler2DBackwardGetWorkspaceSize(
  const aclTensor*     gradOutput,
  const aclTensor*     input,
  const aclTensor*     grid,
  int64_t              interpolationMode,
  int64_t              paddingMode,
  bool                 alignCorners,
  const aclBoolArray*  outputMask,
  aclTensor*           inputGrad,
  aclTensor*           gridGrad,
  uint64_t*            workspaceSize,
  aclOpExecutor**      executor)
aclnnStatus aclnnGridSampler2DBackward(
  void*          workspace,
  uint64_t       workspaceSize,
  aclOpExecutor* executor,
  aclrtStream    stream)

aclnnGridSampler2DBackwardGetWorkspaceSize

  • Parameters

    Parameter Name Input/Output Description Usage Description Data Type Data Format Dimension (shape) Non-contiguous Tensor
    gradOutput (aclTensor*) Input Represents the output gradient from the previous layer during backward propagation, corresponding to `grad` in the formula description.
    • Supports empty Tensor.
    • The data type is the same as that of `input`.
    • Supports shape (N, C, Hout, Wout).
    BFLOAT16, FLOAT16, FLOAT32, DOUBLE ND 4 √
    input (aclTensor*) Input Represents the input tensor for backward propagation, corresponding to `input` in the formula description.
    • Supports empty tensor.
    • The N and C axes of `input` and `gradOutput` must have the same values, and the dimension values of the last two dimensions of `input` cannot be 0.
    • Supports shape (N, C, Hin, Win).
    BFLOAT16, FLOAT16, FLOAT32, DOUBLE ND 4 √
    grid (aclTensor*) Input Represents the tensor of pixel positions, corresponding to `grid` in the formula description.
    • Supports empty tensor.
    • The data type must be consistent with `input`.
    • The values of the N, H, and W axes of `grid` and `gradOutput` must be consistent, and the value of the last dimension of `grid` must be 2.
    • Supports shape (N, Hout, Wout, 2).
    BFLOAT16, FLOAT16, FLOAT32, DOUBLE ND 4 √
    interpolationMode (int64_t) Input Indicates the interpolation mode, corresponding to `interpolationMode` in the formula description. Supports two modes: 0: bilinear (bilinear interpolation), 1: nearest (nearest interpolation). - - - -
    paddingMode (int64_t) Input Indicates the padding mode, i.e., when (x, y) exceeds the sampling range of the input feature map, a specific value is returned. Corresponds to `paddingMode` in the formula description. Supports three modes: 0: zeros, 1: border, 2: reflection. - - - -
    alignCorners (bool) Input Specifies the correspondence between feature map coordinates and feature values, corresponding to `alignCorners` in the formula description. When set to true, feature values are located at pixel centers. When set to false, feature values are located at pixel corners. - - - -
    outputMask (aclBoolArray*) Input Mask used to indicate the output. outputMask[0] being true/false indicates whether to obtain the output `inputGrad`; outputMask[1] being true/false indicates whether to obtain the output `gridGrad`. BOOLARRAY - - -
    inputGrad (aclTensor*) Output Represents the output gradient of backward propagation, corresponding to `dx` in the formula description.
    • Supports empty tensor.
    • The data type is consistent with the data type of `input`.
    • The shape is consistent with `input`.
    BFLOAT16, FLOAT16, FLOAT32, DOUBLE ND 4 √
    gridGrad (aclTensor*) Output Represents the grid gradient, corresponding to `dgrid` in the formula description.
    • Supports empty tensor.
    • The data type is consistent with the data type of `input`.
    • The shape is consistent with `grid`.
    BFLOAT16, FLOAT16, FLOAT32, DOUBLE ND 4 √
    workspaceSize (uint64_t*) Output Returns the workspace size to be allocated on the Device side. - - - - -
    executor (aclOpExecutor**) Output Returns the op executor, which contains the operator computation flow. - - - - -
    • Atlas training products:

      The data type of the parameters gradOutput, input, grid, inputGrad, and gridGrad does not support BFLOAT16 or DOUBLE.

  • Return Value

    aclnnStatus: return code. For details, see aclnn Return Codes.

    The first API performs input parameter validation and reports an error in the following scenarios:

    Return Code Error Code Description
    ACLNN_ERR_PARAM_NULLPTR 161001 The passed gradOutput, input, grid, inputGrad, or gridGrad is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data types of gradOutput, input, grid, inputGrad, and gridGrad are not within the supported range.
    The shape dimension values of gradOutput, input, and grid are not four-dimensional.
    The value of interpolationMode or paddingMode is not within the supported range.
    The first dimension values of input, grid, and gradOutput are not the same.
    The second dimension value of input and the second dimension value of gradOutput are not the same.
    The second dimension value of grid and the third dimension value of gradOutput are not the same.
    The third dimension value of grid and the fourth dimension value of gradOutput are not the same.
    The dimension value of the last two dimensions of input is 0.
    The value of the last dimension of grid is not equal to 2.
    The shape of input is inconsistent with that of inputGrad, or the shape of grid is inconsistent with that of gridGrad.

aclnnGridSampler2DBackward

  • Parameters

    Parameter Name Input/Output Description
    workspace Input The workspace memory address applied for on the Device side.
    workspaceSize Input Size of the workspace requested on the Device side, obtained through the first-phase API aclnnGridSampler2DBackwardGetWorkspaceSize.
    executor Input Operator executor, which contains the operator computation flow.
    stream Input Specifies the Stream on which the task is executed.
  • Return Value

    aclnnStatus: return code. For details, see aclnn Return Codes.

Constraints

  • Deterministic computation:
    • aclnnGridSampler2DBackward uses a non-deterministic implementation by default. Deterministic computation can be enabled via aclrtCtxSetSysParamOpt. The deterministic implementation requires the following conditions to be met simultaneously:
      • The data type of the input and output is not DOUBLE.
      • The C-axis value of the input and output is less than or equal to 2048.

Example of Calling

The sample code is as follows, for reference only. For specific compilation and execution processes, see Compile and Run Samples.

#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_grid_sampler2d_backward.h"

namespace {
constexpr uint64_t kNum2 = 2U;
}

#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, aclrtStream* stream)
{
    // Boilerplate, resource initialization.
    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 = 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);
    // Call aclrtMalloc to apply for device-side memory.
    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);
    // Call aclrtMemcpy to copy host-side data to device-side memory.
    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);

    // Compute the strides of the contiguous tensor.
    std::vector<int64_t> strides(shape.size(), 1);
    for (int64_t i = shape.size() - 2; i >= 0; i--) {
        strides[i] = shape[i + 1] * strides[i + 1];
    }

    // Call the aclCreateTensor API to create an aclTensor.
    *tensor = aclCreateTensor(
        shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(),
        *deviceAddr);
    return 0;
}

int main()
{
    // 1. (Boilerplate) Initialize the device/stream. See the ACL API manual.
    // Fill in the deviceId based on your actual device.
    int32_t deviceId = 0;
    aclrtStream stream;
    auto ret = Init(deviceId, &stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);

    // 2. Construct the input and output. Customize the construction based on the API.
    int64_t interpolationMode = 0;
    int64_t paddingMode = 0;
    bool alignCorners = false;
    aclBoolArray* outputMask = nullptr;
    std::vector<int64_t> gradOutputShape = {1, 1, 3, 3};
    std::vector<int64_t> inputShape = {1, 1, 5, 8};
    std::vector<int64_t> gridShape = {1, 3, 3, 2};
    std::vector<int64_t> inputGradShape = {1, 1, 5, 8};
    std::vector<int64_t> gridGradShape = {1, 3, 3, 2};
    void* gradOutputDeviceAddr = nullptr;
    void* inputDeviceAddr = nullptr;
    void* gridDeviceAddr = nullptr;
    void* inputGradDeviceAddr = nullptr;
    void* gridGradDeviceAddr = nullptr;
    aclTensor* gradOutput = nullptr;
    aclTensor* input = nullptr;
    aclTensor* grid = nullptr;
    aclTensor* inputGrad = nullptr;
    aclTensor* gridGrad = nullptr;

    std::vector<float> gradOutputHostData = {1, 1, 1, 1, 1, 1, 1, 1, 1};
    std::vector<float> inputHostData = {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};
    std::vector<float> gridHostData = {-1, -1, 0, -1, 1, -1, -1, 0, 0, 0, 1, 0, -1, 1, 0, 1, 1, 1};
    std::vector<float> inputGradHostData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                                            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    std::vector<float> gridGradHostData = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

    bool maskValue[2] = {true, true};
    outputMask = aclCreateBoolArray(&(maskValue[0]), kNum2);

    // Create the gradOutput aclTensor.
    ret = CreateAclTensor(
        gradOutputHostData, gradOutputShape, &gradOutputDeviceAddr, aclDataType::ACL_FLOAT, &gradOutput);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the input aclTensor.
    ret = CreateAclTensor(inputHostData, inputShape, &inputDeviceAddr, aclDataType::ACL_FLOAT, &input);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the grid aclTensor.
    ret = CreateAclTensor(gridHostData, gridShape, &gridDeviceAddr, aclDataType::ACL_FLOAT, &grid);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the inputGrad aclTensor.
    ret = CreateAclTensor(inputGradHostData, inputGradShape, &inputGradDeviceAddr, aclDataType::ACL_FLOAT, &inputGrad);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the gridGrad aclTensor.
    ret = CreateAclTensor(gridGradHostData, gridGradShape, &gridGradDeviceAddr, aclDataType::ACL_FLOAT, &gridGrad);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    // 3. Call the CANN operator library API. Replace with the actual API name.
    uint64_t workspaceSize = 0;
    aclOpExecutor* executor;
    // Call the first-phase API of aclnnGridSampler2DBackward.
    ret = aclnnGridSampler2DBackwardGetWorkspaceSize(
        gradOutput, input, grid, interpolationMode, paddingMode, alignCorners, outputMask, inputGrad, gridGrad,
        &workspaceSize, &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGridSampler2DBackwardGetWorkspaceSize failed. ERROR: %d\n", ret);
              return ret);
    // Apply for device memory based on the workspaceSize calculated by the first-phase API.
    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);
    }
    // Call the second-phase API of aclnnGridSampler2DBackward.
    ret = aclnnGridSampler2DBackward(workspaceAddr, workspaceSize, executor, stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnGridSampler2DBackward failed. ERROR: %d\n", ret); return ret);

    // 4. (Boilerplate) Synchronize and wait for task execution to complete.
    ret = aclrtSynchronizeStream(stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclrtSynchronizeStream failed. ERROR: %d\n", ret); return ret);

    // 5. Obtain the output value and copy the result from the device memory to the host memory. Modify based on the specific API definition.
    auto inputGradSize = GetShapeSize(inputGradShape);
    std::vector<float> inputGradResultData(inputGradSize, 0);
    ret = aclrtMemcpy(
        inputGradResultData.data(), inputGradResultData.size() * sizeof(inputGradResultData[0]), inputGradDeviceAddr,
        inputGradSize * sizeof(inputGradResultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy inputGradResultData from device to host failed. ERROR: %d\n", ret);
              return ret);
    for (int64_t i = 0; i < inputGradSize; i++) {
        LOG_PRINT("inputGradResultData[%ld] is: %f\n", i, inputGradResultData[i]);
    }

    auto gridGradSize = GetShapeSize(gridGradShape);
    std::vector<float> gridGradResultData(gridGradSize, 0);
    ret = aclrtMemcpy(
        gridGradResultData.data(), gridGradResultData.size() * sizeof(gridGradResultData[0]), gridGradDeviceAddr,
        gridGradSize * sizeof(gridGradResultData[0]), ACL_MEMCPY_DEVICE_TO_HOST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy gridGradResultData from device to host failed. ERROR: %d\n", ret);
              return ret);
    for (int64_t i = 0; i < gridGradSize; i++) {
        LOG_PRINT("gridGradResultData[%ld] is: %f\n", i, gridGradResultData[i]);
    }

    // 6. Release aclTensor and aclBoolArray. Modify based on the specific API definition.
    aclDestroyTensor(gradOutput);
    aclDestroyTensor(input);
    aclDestroyTensor(grid);
    aclDestroyTensor(inputGrad);
    aclDestroyTensor(gridGrad);
    aclDestroyBoolArray(outputMask);

    // 7. Release device resources. Modify based on the specific API definition.
    aclrtFree(gradOutputDeviceAddr);
    aclrtFree(inputDeviceAddr);
    aclrtFree(gridDeviceAddr);
    aclrtFree(inputGradDeviceAddr);
    aclrtFree(gridGradDeviceAddr);
    if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
    }
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();
    return 0;
}