aclnnUpsampleBilinear2dBackward

📄 View Source Code

Applicable Products

ProductSupported
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√

Function

  • Description: Backward propagation of aclnnUpsampleBilinear2d.
  • Formula:
    • Core algorithm logic of the forward pass:
      1. Map each point of the target image back to the source image, obtaining a coordinate with decimal values.
      2. Based on this floating-point coordinate, calculate the adjacent points in the original image.
      3. Calculate the weights from each adjacent point to the corresponding target point, then multiply and accumulate by weight to obtain the target point value.
    • Specific calculation logic: The scaling method is divided into corner alignment and edge alignment. Corner alignment means aligning by the pixel center of the top-left corner of the original image, while edge alignment means aligning by the top-left vertex and two edges of the original image. Differences exist in the calculation of scaling factors and coordinate positions. For a two-dimensional interpolation point (N,C,H,W)(N, C, H, W), the following formulas apply:scaleH={(inputSize[2]−1)/(outputSize[0]−1)alignCorners=true1/scalesHalignCorners=false&scalesH>0inputSize[2]/outputSize[0]alignCorners=falsescaleH =\begin{cases} (inputSize[2]-1) / (outputSize[0]-1) & alignCorners=true \\ 1 / scalesH & alignCorners=false\&scalesH>0\\ inputSize[2] / outputSize[0] & alignCorners=false \end{cases} scaleW={(inputSize[3]−1)/(outputSize[1]−1)alignCorners=true1/scalesWalignCorners=false&scalesW>0inputSize[3]/outputSize[1]alignCorners=falsescaleW =\begin{cases} (inputSize[3]-1) / (outputSize[1]-1) & alignCorners=true \\ 1 / scalesW & alignCorners=false\&scalesW>0\\ inputSize[3] / outputSize[1] & alignCorners=false \end{cases} Therefore, for a point p(x,y) in a certain direction of the output, the point mapped back to the original image is denoted as q(x',y'), and the relationship is as follows:x′={x∗scaleHalignCorners=trueMAX(0,(x+0.5)∗scaleH−0.5)alignCorners=falsex' =\begin{cases} x * scaleH & alignCorners=true \\ MAX(0,{(x+0.5)*scaleH-0.5}) & alignCorners=false \end{cases} y′={y∗scaleWalignCorners=trueMAX(0,(y+0.5)∗scaleW−0.5)alignCorners=falsey' =\begin{cases} y * scaleW & alignCorners=true \\ MAX(0,{(y+0.5)*scaleW-0.5}) & alignCorners=false \end{cases}
      • Where:

        x0=int(x′),x1=int(x′)+1,lambda0=x1−x′,lambda1=1−lambda0x_{0} =int(x'),x_{1} =int(x')+1, lambda_{0} = x_{1}-x', lambda_{1} = 1-lambda_{0} y0=int(y′),y1=int(y′)+1,lambdb0=y1−y′,lambdb1=1−lambdb0y_{0} =int(y'),y_{1} =int(y')+1, lambdb_{0} = y_{1}-y', lambdb_{1} = 1-lambdb_{0}
      • Then the following formula applies:

        V(px,y)=V(px0,y0)∗lambda0∗lambdb0+V(px0,y1)∗lambda0∗lambdb1+V(px1,y0)∗lambda1∗lambdb0+V(px1,y1)∗lambda1∗lambdb1{V(p_{x, y})} = {V(p_{x0, y0})} * {lambda_{0}} * {lambdb_{0}} + {V(p_{x0, y1})} * {lambda_{0}} * {lambdb_{1}} + {V(p_{x1, y0})} * {lambda_{1}} * {lambdb_{0}} + {V(p_{x1, y1})} * {lambda_{1}} * {lambdb_{1}}
      • Assume that the output image out (x,y)(x, y) of the forward interpolation is affected by the original image input (xi,yj)(x_i, y_j), then:

        gradInput(xi,yj)+=gradOutput(x,y)∗lambda(xi,yj)∗lambdb(xi,yj)gradInput(x_i,y_j) += gradOutput(x,y) * lambda(x_i,y_j) * lambdb(x_i,y_j)

Function Prototype

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

aclnnStatus aclnnUpsampleBilinear2dBackwardGetWorkspaceSize(
  const   aclTensor *gradOut,
  const aclIntArray *outputSize,
  const aclIntArray *inputSize,
  bool               alignCorners,
  double             scalesH,
  double             scalesW,
  aclTensor         *out,
  uint64_t          *workspaceSize,
  aclOpExecutor    **executor)
aclnnStatus aclnnUpsampleBilinear2dBackward(
  void*          workspace,
  uint64_t       workspace_size,
  aclOpExecutor* executor,
  aclrtStream    stream)

aclnnUpsampleBilinear2dBackwardGetWorkspaceSize

  • Parameters

    Parameter Name Input/Output Description Usage Notes Data Type Data Format Dimension (shape) Non-Contiguous Tensor
    gradOut (aclTensor*) Input Represents the gradient tensor for backward computation, corresponding to `gradOutput` in the formula. Empty tensors are not supported. FLOAT32, FLOAT16, BFLOAT16 NCHW, NHWC 4 √
    outputSize (aclIntArray*) Input Represents the spatial size of the input `gradOutput` in the H and W dimensions. Corresponds to `outputSize` in the corresponding formula. size is 2, and each element is greater than zero. INT64 - - -
    inputSize (aclIntArray*) Input Represents the spatial size of the output `out` in the N, C, H, and W dimensions respectively. Corresponding formula: `inputSize`. The size is 4, and each element is greater than zero. INT64 - - -
    alignCorners (bool) Input Determines whether to align corner pixels, corresponding to `alignCorners` in the formula. If set to True, the input and output tensors are aligned by the center points of their corner pixels, preserving the values at corner pixels. If set to False, the input and output tensors are aligned by the corner points of their corner pixels, and the interpolation uses edge value padding for out-of-boundary values, making this operation independent of the input size scalesH and scalesW when kept unchanged. - - - -
    scalesH (double) Input Represents the height dimensional multiplier of the output `out`, corresponding to `scalesH` in the formula. - - - - -
    scalesW (double) Input Represents the width dimensional multiplier of the output `out`, corresponding to `scalesW` in the corresponding formula. - - - - -
    out (aclTensor*) Output Represents the output tensor of the backward computation, corresponding to `gradInput` in the formula.
    • Empty tensors are not supported.
    • The data format must be consistent with the input `gradOut`.
    FLOAT32, FLOAT16, BFLOAT16 NCHW, NHWC 4 √
    workspaceSize (uint64_t*) Output Returns the workspace size to be applied on the Device side. - - - - -
    executor (aclOpExecutor**) Output Returns the operator executor, which contains the operator computation flow. - - - - -
    • Atlas inference products, Atlas training products, Atlas A2 training products/Atlas A2 inference products, Atlas A3 training products/Atlas A3 inference products:
      • The data type of the parameters gradOut and out does not support BFLOAT16.
      • The data type of the parameter out must be consistent with the data type of gradOut.
    • Ascend 950PR/Ascend 950DT:
      • When the data type of gradOut is not FLOAT, the data type of out is consistent with that of gradOut.
      • When the data type of gradOut is FLOAT, the data type of out can be FLOAT32, FLOAT16, or BFLOAT16.
  • Return Value

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

The first-phase 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 gradOut, outputSize, inputSize, or out is a null pointer.
ACLNN_ERR_PARAM_INVALID 161002 The data type of gradOut is not within the supported range.
The data types of gradOut and out are inconsistent.
The dimension of gradOut is not 4-dimensional.
The size of outputSize is not equal to 2.
An element value of outputSize is less than 1.
The size of inputSize is not equal to 4.
An element value of inputSize is less than 1.
The sizes of gradOut and inputSize differ in the N and C dimensions.
The sizes of gradOut in the H and W dimensions are inconsistent with outputSize[0] and outputSize[1].
The dimensional sizes of the N/C axes of gradOut and out are not equal.
The data formats of gradOut and out are not within the supported range.

aclnnUpsampleBilinear2dBackward

  • Parameters

    Parameter Name Input/Output Description
    workspace Input Side-Applied workspace memory address on the Device.
    workspaceSize Input Size of the workspace applied on the Device side, obtained through the first-phase API aclnnUpsampleBilinear2dBackwardGetWorkspaceSize.
    executor Input Operator executor, which contains the operator computation flow.
    stream Input Specifies the stream for task execution.
  • Return Value

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

Constraints

  • The parameters inputSize, outputSize, scalesH, and scalesW must satisfy the following constraints:

    outputSize_H=floor(inputSize_H∗scalesH)outputSize\_H = floor(inputSize\_H * scalesH) outputSize_W=floor(inputSize_W∗scalesW)outputSize\_W = floor(inputSize\_W * scalesW)
  • Deterministic computation:

    • aclnnUpsampleBilinear2dBackward uses deterministic implementation by default.

Example

The following sample code is for reference only. For details about compilation and execution, see Compile and Run Samples.

#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_upsample_bilinear_2d_backward.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, 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 the 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);

    // Calculate strides of a 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 aclCreateTensor to create an aclTensor.
    *tensor = aclCreateTensor(shape.data(),
        shape.size(),
        dataType,
        strides.data(),
        0,
        aclFormat::ACL_FORMAT_NCHW,
        shape.data(),
        shape.size(),
        *deviceAddr);
    return 0;
}

int main()
{
    // 1. (Boilerplate) Initialize 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 input and output. Customize the construction based on the API.
    std::vector<int64_t> selfShape = {1, 1, 6, 6};
    std::vector<int64_t> outShape = {1, 1, 3, 3};
    void *selfDeviceAddr = nullptr;
    void *outDeviceAddr = nullptr;
    aclTensor *self = nullptr;
    aclTensor *out = nullptr;
    std::vector<float> selfHostData(36, 1);
    std::vector<float> outHostData = {0, 0, 0, 0, 0, 0, 0, 0, 0};

    // Create the self aclTensor.
    ret = CreateAclTensor(selfHostData, selfShape, &selfDeviceAddr, aclDataType::ACL_FLOAT, &self);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    // Create the out aclTensor.
    ret = CreateAclTensor(outHostData, outShape, &outDeviceAddr, aclDataType::ACL_FLOAT, &out);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    std::vector<int64_t> outArraySize = {6, 6};
    const aclIntArray *outputSize = aclCreateIntArray(outArraySize.data(), outArraySize.size());
    CHECK_RET(outputSize != nullptr, return ACL_ERROR_INTERNAL_ERROR);

    std::vector<int64_t> inputArraySize = {1, 1, 3, 3};
    const aclIntArray *inputSize = aclCreateIntArray(inputArraySize.data(), inputArraySize.size());
    CHECK_RET(inputSize != nullptr, return ACL_ERROR_INTERNAL_ERROR);

    // 3. Call the CANN operator library API. Replace with the specific API name.
    uint64_t workspaceSize = 0;
    aclOpExecutor *executor;
    // Call the first-phase API of aclnnUpsampleBilinear2dBackward.
    ret = aclnnUpsampleBilinear2dBackwardGetWorkspaceSize(
        self, outputSize, inputSize, 1, 2, 2, out, &workspaceSize, &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnUpsampleBilinear2dBackwardGetWorkspaceSize 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 aclnnUpsampleBilinear2dBackward.
    ret = aclnnUpsampleBilinear2dBackward(workspaceAddr, workspaceSize, executor, stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnUpsampleBilinear2dBackward 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. Modify based on the specific API definition.
    auto size = GetShapeSize(outShape);
    std::vector<float> resultData(size, 0);
    ret = aclrtMemcpy(resultData.data(),
        resultData.size() * sizeof(resultData[0]),
        outDeviceAddr,
        size * sizeof(resultData[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("result[%ld] is: %f\n", i, resultData[i]);
    }

    // 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
    aclDestroyTensor(self);
    aclDestroyTensor(out);
    aclDestroyIntArray(outputSize);
    aclDestroyIntArray(inputSize);

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