aclnnBatchNormReduceBackward

📄 View Source Code

Applicable Products

Applicable ProductsSupported
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:

    It is primarily used to compute the gradient of the BatchNorm operation during backward propagation and perform reduction operations on some intermediate results to optimize computational efficiency. The computation results are as follows:

    • Computes the gradient of the loss function l with respect to the scale weight γ (∂l∂γ\frac{\partial l}{\partial γ}).

    • Computes the gradient of the loss function l with respect to the offset β (∂l∂β\frac{\partial l}{\partial β}).

    • Derives the intermediate quantities sumDy and sumDyXmu required for computing ∂l∂xi\frac{\partial l}{\partial x_i} from the deviation dyi of the loss function l with respect to the output (yi). Here (∂l∂xi\frac{\partial l}{\partial x_i}) is the gradient of the loss function l with respect to each input (xi) of the corresponding layer.

  • Formula:

    gradWeight=∂l∂γ=∑i=0m∂l∂yi⋅(xi)^=1σB2+eps⋅∑i=0m∂l∂yi⋅(xi−μB)gradWeight = \frac{\partial l}{\partial γ} = \sum^m_{i=0} \frac{\partial l}{\partial y_i} \cdot \hat{(x_i)} = \frac{1}{{\sqrt{σ^2_B + eps}}} \cdot \sum^m_{i=0} \frac{\partial l} {\partial y_i} \cdot (x_i-μ_B) gradBias=∂l∂β=∑i=0m∂l∂yigradBias = \frac{\partial l}{\partial β} = \sum^m_{i=0} \frac{\partial l}{\partial y_i} sumDy=sum(l,yi)=∑i=0m∂l∂yisumDy = sum(l, y_i) = \displaystyle \sum^m_{i=0} \frac{\partial l}{\partial y_i} sumDyXmu=sum(l,yi,xi,μB)=∑i=0m∂l∂yi⋅(xi−μB)sumDyXmu = sum(l, y_i, x_i, μ_B) = \displaystyle \sum^m_{i=0} \frac{\partial l}{\partial y_i} \cdot (x_i-μ_B)

Function Prototype

Each operator is divided into a two-phase API. You must first call the "aclnnBatchNormReduceBackwardGetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the process, and then call the "aclnnBatchNormReduceBackward" API to perform the computation.

aclnnStatus aclnnBatchNormReduceBackwardGetWorkspaceSize(
  const aclTensor* gradOut,
  const aclTensor* input,
  const aclTensor* mean,
  const aclTensor* invstd,
  const aclTensor* weight,
  const bool       inputG,
  const bool       weightG,
  const bool       biasG,
  aclTensor*       sumDy,
  aclTensor*       sumDyXmu,
  aclTensor*       gradWeight,
  aclTensor*       gradBias,
  uint64_t*        workspaceSize,
  aclOpExecutor**  executor)
aclnnStatus aclnnBatchNormReduceBackward(
  void                *workspace,
  uint64_t             workspaceSize,
  aclOpExecutor       *executor,
  const aclrtStream    stream)

aclnnBatchNormReduceBackwardGetWorkspaceSize

  • Parameters

    Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor
    gradOut (aclTensor*) Input Represents the gradient tensor, corresponding to `∂l/∂y` in the formula.
    • Supports empty tensors.
    • The data type and shape must be consistent with `input`.
    FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √
    input (aclTensor*) Input. Represents the input tensor, corresponding to `x` in the formula.
    • Supports null Tensor.
    • Defaults to the second dimension as the channel axis, and the channel axis value cannot be 0.
    FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √
    mean (aclTensor*) Input. Represents the mean, corresponding to `μB` in the formula.
    • The shape length is equal to the length of the channel axis in the input parameter `input`.
    FLOAT32, FLOAT16, BFLOAT16 ND 1 √
    invstd (aclTensor*) Input Represents the reciprocal of the standard deviation, corresponding to the reciprocal of the square root of `(σB)2+eps` in the formula.
    • The shape length equals the length of the channel axis in the input parameter `input`.
    FLOAT32, FLOAT16, BFLOAT16 ND 1 √
    weight (aclTensor*) Input Represents the weight tensor, corresponding to `γ` in the formula.
    • The shape length equals the channel axis length in the input parameter `input`.
    FLOAT32, FLOAT16, BFLOAT16 ND 1 √
    inputG (bool) Input Output mask that indicates whether to output `sumDy` and `sumDyXmu`. - - - - -
    weightG (bool) Input Indicates the output mask, marking whether to output `gradWeight`. - - - - -
    biasG (bool) Input Indicates the output mask, marking whether to output `gradBias`. - - - - -
    sumDy (aclTensor*) Output Cumulative sum of the forward output gradient `gradOut`, corresponding to `sumDy` in the formula.
    • Optional output. Output if `inputG` is True. The size of the shape must be equal to the channel axis length of `input`.
    • The data format must be consistent with `gradOut`.
    FLOAT32, FLOAT16, BFLOAT16 ND 1 √
    sumDyXmu (aclTensor*) Output Represents the sum of the product of the forward output gradient `gradOut` and the centered input data `(x-μB)`, corresponding to `sumDyXmu` in the formula.
    • Optional output. Output if `inputG` is True. The shape size must be equal to the channel axis length of `input`.
    • The data format must be consistent with `gradOut`.
    FLOAT32, FLOAT16, BFLOAT16 ND 1 ✓
    gradWeight (aclTensor*) Output Represents the gradient of the scale parameter, corresponding to `gradWeight` in the formula.
    • Optional output. Output if `weightG` is True. The size of the shape must be equal to the channel axis length of `input`.
    • The data format is consistent with `gradOut`.
    FLOAT32, FLOAT16, BFLOAT16 ND 1 √
    gradBias (aclTensor*) Output Represents the gradient of the bias parameter, corresponding to `gradBias` in the formula.
    • Optional output. Output if biasG is True. The size of the shape must be equal to the length of the channel axis of input.
    • The data format must be consistent with that of gradOut.
    FLOAT32, FLOAT16, BFLOAT16 ND 1 √
    workspaceSize (uint64_t*) Output Returns the workspace size that the user needs to apply for on the Device side. - - - - -
    executor (aclOpExecutor**) Output Returns the operator executor, which contains the operator computation process. - - - - -
    • Atlas training products, Atlas inference products: The data type of gradOut, input, mean, invstd,weight,sumDy, sumDyXmu, gradWeight, and gradBias does not support BFLOAT16.
  • Return Value

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

    The first-phase API performs input parameter validation and returns an error in the following scenarios:

    Return Code Error Code Description
    ACLNN_ERR_PARAM_NULLPTR 161001 The data type of input, meanAll, invstdAll, mean, invstd, runningMean, runningVar, or counts is not within the supported range.
    When inputG is true, sumDy or sumDyXmu is a null pointer.
    When weightG is true, gradWeight is a null pointer.
    When biasG is true, gradBias is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data type or data format of gradOut, input, mean, invstd, weight, sumDy, sumDyXmu, gradWeight, or gradBias is not within the supported range.
    The data type of gradOut must be consistent with that of input.
    When inputG is true, the data type or data format of sumDy or sumDyXmu is not within the supported range.
    When weightG is true, the data type or data format of gradWeight is not within the supported range.
    When biasG is true, the data type or data format of gradBias is not within the supported range.
    The data formats of gradOut and input are inconsistent.
    The dimension of gradOut or input exceeds 8.
    The dimension of gradOut or input is less than 2.
    The size of the channel axis of input is 0.
    When inputG is true, the size of sumDy or sumDyXmu is inconsistent with the channel axis of input.
    When weightG is true, the size of gradWeight is inconsistent with the channel axis of input.
    When biasG is true, the size of gradBias is inconsistent with the channel axis of input.

aclnnBatchNormReduceBackward

  • Parameters

    Parameter Input/Output Description
    workspace Input Memory address of the workspace applied for on the Device side.
    workspaceSize Input Size of the workspace applied for on the Device side, obtained through the first-phase API aclnnBatchNormReduceBackwardGetWorkspaceSize.
    executor Input Operator executor, which contains the operator computation process.
    stream Input Specifies the stream that executes the task.
  • Return Value

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

Constraints

  • When any input is an empty tensor, the output is an empty tensor.

  • Deterministic computation:

    • aclnnBatchNormReduceBackward defaults to a deterministic implementation.

Example

The following provides example code for reference only. For details about compilation and running, see Compile and Run Samples.

#include <iostream>
#include <vector>
#include "acl/acl.h"
#include "aclnnop/aclnn_batch_norm_backward_reduce.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;
}

void PrintOutResult(std::vector<int64_t>& shape, void** deviceAddr)
{
    auto size = GetShapeSize(shape);
    std::vector<float> resultData(size, 0);
    auto ret = aclrtMemcpy(
        resultData.data(), resultData.size() * sizeof(resultData[0]), *deviceAddr, 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);
    for (int64_t i = 0; i < size; i++) {
        LOG_PRINT("result[%ld] is: %f\n", i, resultData[i]);
    }
}

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 allocate device 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 data from the host to the device 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 the 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 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 the actual device.
    int32_t deviceId = 0;
    aclrtStream stream;
    auto ret = Init(deviceId, &stream);
    // Handle the check result as needed.
    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 interface.
    std::vector<int64_t> gradOutShape = {4, 2};
    std::vector<int64_t> inputShape = {4, 2};
    std::vector<int64_t> meanShape = {2};
    std::vector<int64_t> invstdShape = {2};
    std::vector<int64_t> weightShape = {2};
    std::vector<int64_t> sumDyShape = {2};
    std::vector<int64_t> sumDyXmuShape = {2};
    std::vector<int64_t> gradWeightShape = {2};
    std::vector<int64_t> gradBiasShape = {2};

    void* gradOutDeviceAddr = nullptr;
    void* inputDeviceAddr = nullptr;
    void* meanDeviceAddr = nullptr;
    void* invstdDeviceAddr = nullptr;
    void* weightDeviceAddr = nullptr;
    void* sumDyDeviceAddr = nullptr;
    void* sumDyXmuDeviceAddr = nullptr;
    void* gradWeightDeviceAddr = nullptr;
    void* gradBiasDeviceAddr = nullptr;

    aclTensor* gradOut = nullptr;
    aclTensor* input = nullptr;
    aclTensor* mean = nullptr;
    aclTensor* invstd = nullptr;
    aclTensor* weight = nullptr;
    aclTensor* sumDy = nullptr;
    aclTensor* sumDyXmu = nullptr;
    aclTensor* gradWeight = nullptr;
    aclTensor* gradBias = nullptr;

    std::vector<float> gradOutHostData = {1, 1, 1, 2, 2, 2, 3, 3};
    std::vector<float> inputHostData = {0, 1, 2, 3, 4, 5, 6, 7};
    std::vector<float> meanHostData = {1, 1};
    std::vector<float> invstdHostData = {1, 1};
    std::vector<float> weightHostData = {1, 1};
    std::vector<float> sumDyHostData = {1, 1};
    std::vector<float> sumDyXmuHostData = {1, 1};
    std::vector<float> gradWeightHostData = {1, 1};
    std::vector<float> gradBiasHostData = {1, 1};

    ret = CreateAclTensor(gradOutHostData, gradOutShape, &gradOutDeviceAddr, aclDataType::ACL_FLOAT, &gradOut);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(inputHostData, inputShape, &inputDeviceAddr, aclDataType::ACL_FLOAT, &input);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(meanHostData, meanShape, &meanDeviceAddr, aclDataType::ACL_FLOAT, &mean);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(invstdHostData, invstdShape, &invstdDeviceAddr, aclDataType::ACL_FLOAT, &invstd);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_FLOAT, &weight);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    bool inputG = true;
    bool weightG = true;
    bool biasG = true;

    ret = CreateAclTensor(sumDyHostData, sumDyShape, &sumDyDeviceAddr, aclDataType::ACL_FLOAT, &sumDy);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(sumDyXmuHostData, sumDyXmuShape, &sumDyXmuDeviceAddr, aclDataType::ACL_FLOAT, &sumDyXmu);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(
        gradWeightHostData, gradWeightShape, &gradWeightDeviceAddr, aclDataType::ACL_FLOAT, &gradWeight);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    ret = CreateAclTensor(gradBiasHostData, gradBiasShape, &gradBiasDeviceAddr, aclDataType::ACL_FLOAT, &gradBias);
    CHECK_RET(ret == ACL_SUCCESS, return ret);

    uint64_t workspaceSize = 0;
    aclOpExecutor* executor;

    // Example of calling the aclnnBatchNormReduceBackward API.
    // 3. Call the CANN operator library API. Replace it with the specific API name.
    // Call the first-phase API of aclnnBatchNormReduceBackward.
    ret = aclnnBatchNormReduceBackwardGetWorkspaceSize(
        gradOut, input, mean, invstd, weight, inputG, weightG, biasG, sumDy, sumDyXmu, gradWeight, gradBias,
        &workspaceSize, &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnBatchNormReduceBackwardGetWorkspaceSize 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 aclnnBatchNormReduceBackward.
    ret = aclnnBatchNormReduceBackward(workspaceAddr, workspaceSize, executor, stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnBatchNormReduceBackward failed. ERROR: %d\n", ret); return ret);

    // 4. (Boilerplate) Synchronously 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 values and copy the results from the device memory to the host memory. Modify based on the specific API definition.
    PrintOutResult(sumDyShape, &sumDyDeviceAddr);
    PrintOutResult(sumDyXmuShape, &sumDyXmuDeviceAddr);
    PrintOutResult(gradWeightShape, &gradWeightDeviceAddr);
    PrintOutResult(gradBiasShape, &gradBiasDeviceAddr);

    // 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
    aclDestroyTensor(gradOut);
    aclDestroyTensor(input);
    aclDestroyTensor(mean);
    aclDestroyTensor(invstd);
    aclDestroyTensor(weight);
    aclDestroyTensor(sumDy);
    aclDestroyTensor(sumDyXmu);
    aclDestroyTensor(gradWeight);
    aclDestroyTensor(gradBias);

    // 7. Release device resources. Modify based on the specific API definition.
    aclrtFree(gradOutDeviceAddr);
    aclrtFree(inputDeviceAddr);
    aclrtFree(meanDeviceAddr);
    aclrtFree(invstdDeviceAddr);
    aclrtFree(weightDeviceAddr);
    aclrtFree(sumDyDeviceAddr);
    aclrtFree(sumDyXmuDeviceAddr);
    aclrtFree(gradWeightDeviceAddr);
    aclrtFree(gradBiasDeviceAddr);
    if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
    }
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();
    return 0;
}