aclnnModulateBackward

Applicable Products

ProductSupported
Atlas A3 training products/Atlas A3 inference products√
Atlas A2 training products/Atlas A2 inference products√

Function

  • Operator function: Computes the parameters in the ModulateBackward backward propagation for gradient update.

  • Formula:

    Assume the shape of input self is [B, L, D]. The formula is as follows: Formula:

    {grad_input=grad_output⊙scale↑Lgrad_scale=∑l=1L(grad_output⊙input)b,l,dgrad_shift=∑l=1Lgrad_outputb,l,d\begin{cases} \text{grad\_input} = \text{grad\_output} \odot \text{scale}^{\uparrow L} \\ \text{grad\_scale} = \sum_{l=1}^{L} (\text{grad\_output} \odot \text{input})_{b,l,d} \\ \text{grad\_shift} = \sum_{l=1}^{L} \text{grad\_output}_{b,l,d} \end{cases}

    Symbol description:

    • ⊙\odot: Indicates element-wise multiplication.

    • ∑l=1L\sum_{l=1}^{L}: Summation operation, performed along the sequence dimension LL (i.e., dim=1).

    • b,l,db,l,d: Subscripts, representing the dimension indices of the tensor (typically Batch, Length, Dimension).

    • scale↑L\text{scale}^{\uparrow L}: Indicates broadcasting (expanding) the scale tensor along the sequence dimension LL.

Function Prototype

Each operator is divided into a two-phase API. First call the "aclnnModulateBackwardGetWorkspaceSize" API to obtain the workspace size required for computation and the executor that contains the operator computation process, and then call the "aclnnModulateBackward" API to perform the computation.

aclnnStatus aclnnModulateBackwardGetWorkspaceSize(
    const aclTensor* grad_output,
    const aclTensor* input,
    const aclTensor* scale,
    const aclTensor* shift,
    const aclTensor* grad_input,
    const aclTensor* grad_scale,
    const aclTensor* grad_shift,
    uint64_t*        workspaceSize,
    aclOpExecutor**  executor)
aclnnStatus aclnnModulateBackward(
    void*          workspaceAddr,
    uint64_t       workspaceSize,
    aclOpExecutor* executor,
    aclrtStream    stream)

aclnnModulateBackwardGetWorkspaceSize

  • Parameters:

    Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor
    grad_output Input Represents the input feature tensor, i.e., grad_output in the formula. - FLOAT, FLOAT16, BFLOAT16 ND (batch_size, seq_len, hidden_dim) √
    input Input. Represents the feature tensor of forward propagation, i.e., input in the formula. - FLOAT, FLOAT16, BFLOAT16 ND (batch_size, seq_len, hidden_dim) √
    scale Input Optional parameter, representing the scaling coefficient, i.e., scale in the formula. The data type must match grad_output and only 2D input is supported. FLOAT, FLOAT16, BFLOAT16 ND (batch_size, hidden_dim) √
    shift Input Optional parameter that represents the shift coefficient and determines whether a corresponding output exists. Must match the data type of grad_output and supports only 2D input. FLOAT, FLOAT16, BFLOAT16 ND (batch_size, hidden_dim) √
    grad_input Output Represents the gradient of the input tensor, grad_input in Formula. Data type and shape must match input. FLOAT, FLOAT16, BFLOAT16 ND - √
    grad_scale Output Represents the scaled gradient, i.e., grad_scale in the formula. Data type and shape must match those of scale. FLOAT, FLOAT16, BFLOAT16 ND - √
    grad_shift Output Represents the gradient of the shift, grad_shift in Formula. Data type and shape must match shift. FLOAT, FLOAT16, BFLOAT16 ND - √
    workspaceSize Output Returns the workspace size to be allocated on the Device side. - - - - -
    executor Output Returns the operator executor, which contains the operator computation flow. - - - - -
  • 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 Value Error Code Description
    ACLNN_ERR_PARAM_NULLPTR 161001 Passed self or out is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data type or data format of self, scaleOptional, or shiftOptional is not within the supported range.
    The shapes of self, scaleOptional, and shiftOptional do not satisfy the constraints.
    self is an empty tensor, and scale or shift is not an empty tensor.

aclnnModulateBackward

  • Parameters:

    Parameter Input/Output Description
    workspace Input Device-side-allocated workspace memory address.
    workspaceSize Input Device-side-allocated workspace size, obtained from the first-phase API aclnnModulateBackwardGetWorkspaceSize.
    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 Code.

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

    Return Value Error Code Description
    ACLNN_ERR_PARAM_NULLPTR 161001 Passed grad_output or input is a null pointer.
    ACLNN_ERR_PARAM_INVALID 161002 The data type or data format of the input and output is not within the supported range.
    The shapes of input, scale, and shift do not satisfy the constraints.
    The input dimension exceeds 3.

Constraints

  • Deterministic computation:

    • aclnnModulateBackward defaults to a deterministic implementation.
  • scale and shift are two-dimensional vectors. The first dimension must match the first dimension of input, and the second dimension must match the third dimension of input.

  • The shape of the input gradoutput must be consistent with the shape of the input input.

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_modulate_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;
}

void PrintOutResult(const std::vector<int64_t>& shape, void* deviceAddr, const std::string& name){
    auto size = GetShapeSize(shape);
    std::vector<float>resultData(size,0);
    auto ret = aclrtMemcpy(resultData.data(),resultData.size() * sizeof(float),deviceAddr,
                            size * sizeof(float),ACL_MEMCPY_DEVICE_TO_HOST);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("copy %s from device to host failed. ERROR:%d\n",name.c_str(),ret); return);

    int print_count = std::min(static_cast<int64_t>(10),size);
    LOG_PRINT("%s (first %d elements):\n",name.c_str(),print_count);
    for (int64_t i = 0; i < print_count; i++){
        LOG_PRINT(" [%ld]: %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-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 strides for 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_ND, shape.data(), shape.size(),
        *deviceAddr);
    return 0;
}

int main()
{
    // 1. (Boilerplate) Initialize the device/stream. See the acl API manual.
    // Set deviceId based on the 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.
    std::vector<int64_t> grad_outputShape = {2, 1, 1};
    std::vector<int64_t> inputShape = {2, 1, 1};
    std::vector<int64_t> scaleShape = {2, 1};
    std::vector<int64_t> shiftShape = {2, 1};
    std::vector<int64_t> grad_inputShape = {2, 1, 1};
    std::vector<int64_t> grad_scaleShape = {2, 1};
    std::vector<int64_t> grad_shiftShape = {2, 1};
    void* grad_outputDeviceAddr = nullptr;
    void* inputDeviceAddr = nullptr;
    void* scaleDeviceAddr = nullptr;
    void* shiftDeviceAddr = nullptr;
    void* grad_inputDeviceAddr = nullptr;
    void* grad_scaleDeviceAddr = nullptr;
    void* grad_shiftDeviceAddr = nullptr;
    aclTensor* grad_output = nullptr;
    aclTensor* input = nullptr;
    aclTensor* scale = nullptr;
    aclTensor* shift = nullptr;
    aclTensor* grad_input = nullptr;
    aclTensor* grad_scale = nullptr;
    aclTensor* grad_shift = nullptr;
    std::vector<float> grad_outputHostData{10, 20};
    std::vector<float> inputHostData{10, 20};
    std::vector<float> scaleHostData{20, 30};
    std::vector<float> shiftHostData{30, 40};
    std::vector<float> grad_inputHostData{0, 0};
    std::vector<float> grad_scaleHostData{0, 0};
    std::vector<float> grad_shiftHostData{0, 0};
    // Create the grad_output aclTensor.
    ret = CreateAclTensor(grad_outputHostData, grad_outputShape, &grad_outputDeviceAddr, aclDataType::ACL_FLOAT, &grad_output);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create input aclTensor.
    ret = CreateAclTensor(inputHostData, inputShape, &inputDeviceAddr, aclDataType::ACL_FLOAT, &input);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create scale aclTensor.
    ret = CreateAclTensor(
        scaleHostData, scaleShape, &scaleDeviceAddr, aclDataType::ACL_FLOAT, &scale);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create shift aclTensor.
    ret = CreateAclTensor(
        shiftHostData, shiftShape, &shiftDeviceAddr, aclDataType::ACL_FLOAT, &shift);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create grad_input aclTensor.
    ret = CreateAclTensor(grad_inputHostData, grad_inputShape, &grad_inputDeviceAddr, aclDataType::ACL_FLOAT, &grad_input);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create grad_scale aclTensor.
    ret = CreateAclTensor(grad_scaleHostData, grad_scaleShape, &grad_scaleDeviceAddr, aclDataType::ACL_FLOAT, &grad_scale);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // Create the grad_shift aclTensor.
    ret = CreateAclTensor(grad_shiftHostData, grad_shiftShape, &grad_shiftDeviceAddr, aclDataType::ACL_FLOAT, &grad_shift);
    CHECK_RET(ret == ACL_SUCCESS, return ret);
    // 3. Call the CANN operator library API. Replace with the specific API name.
    uint64_t workspaceSize = 0;
    aclOpExecutor* executor = nullptr;
    // Call the first-phase API of aclnnModulate.
    ret = aclnnModulateBackwardGetWorkspaceSize(grad_output, input, scale, shift, grad_input, grad_scale, grad_shift, &workspaceSize, &executor);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnModulateBackwardGetWorkspaceSize failed. ERROR: %d\n", ret); return ret);
    // Apply for device memory based on the workspaceSize computed 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 aclnnModulate.
    ret = aclnnModulateBackward(workspaceAddr, workspaceSize, executor, stream);
    CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnModulateBackward failed. ERROR: %d\n", ret); return ret);

    // 4. (Boilerplate) Synchronously wait for task execution to finish.
    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-side memory to the host side. Modify based on the specific API definition.
   PrintOutResult(grad_inputShape, grad_inputDeviceAddr,"grad_input");
   PrintOutResult(grad_scaleShape, grad_scaleDeviceAddr,"grad_scale");
   PrintOutResult(grad_shiftShape, grad_shiftDeviceAddr,"grad_shift");

    // 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
    aclDestroyTensor(grad_output);
    aclDestroyTensor(input);
    aclDestroyTensor(scale);
    aclDestroyTensor(shift);
    aclDestroyTensor(grad_input);
    aclDestroyTensor(grad_scale);
    aclDestroyTensor(grad_shift);

    // 7. Release device resources. Modify based on the specific API definition.
    aclrtFree(grad_outputDeviceAddr);
    aclrtFree(inputDeviceAddr);
    aclrtFree(scaleDeviceAddr);
    aclrtFree(shiftDeviceAddr);
    aclrtFree(grad_inputDeviceAddr);
    aclrtFree(grad_scaleDeviceAddr);
    aclrtFree(grad_shiftDeviceAddr);
    if (workspaceSize > 0) {
        aclrtFree(workspaceAddr);
    }
    aclrtDestroyStream(stream);
    aclrtResetDevice(deviceId);
    aclFinalize();
    LOG_PRINT("Program completed successfully.\n");
    return 0;
}