aclnnBatchNormElemtBackward
Applicable Products
| Product | Supported |
|---|---|
| √ | |
| √ | |
| √ | |
| × | |
| × | |
| √ |
Function
Description: Backward computation of aclnnBatchNormElemt. Computes the element-wise gradient of the input tensor to update model parameters during backward propagation.
Formula:
Function Prototype
Each operator is divided into a two-phase API. You must first call the "aclnnBatchNormElemtBackwardGetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the process, and then call the "aclnnBatchNormElemtBackward" API to perform the computation.
aclnnStatus aclnnBatchNormElemtBackwardGetWorkspaceSize(
const aclTensor* gradOut,
const aclTensor* input,
const aclTensor* mean,
const aclTensor* invstd,
const aclTensor* weight,
const aclTensor* sumDy,
const aclTensor* sumDyXmu,
aclTensor* counter,
aclTensor* gradInput,
uint64_t* workspaceSize,
aclOpExecutor** executor)aclnnStatus aclnnBatchNormElemtBackward(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
const aclrtStream stream)aclnnBatchNormElemtBackwardGetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (Shape) Non-contiguous Tensor gradOut (aclTensor*) Input Represents the differential of the forward output, corresponding to `gradOut` in the formula. - Supports empty tensor.
- The shape is consistent with the shape of `input`.
- The second dimension is fixed as the channel axis.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ input (aclTensor*) Input Represents the input for BatchNorm computation, corresponding to `input` in the formula. - Supports empty tensor.
- The second dimension is fixed as the channel axis, and the size of the channel axis cannot be 0.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ mean (aclTensor*) Input Represents the mean of input data, corresponding to `mean` in the formula. - Supports empty tensor.
- The shape length is equal to the channel axis length in the input `input`.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ invstd (aclTensor*) Input Represents the reciprocal of the standard deviation of the input data, corresponding to `invstd` in the formula. - Supports empty tensors.
- The shape length is equal to the channel axis length of the input parameter `input`.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ weight (aclTensor*) Input Represents the weight tensor, corresponding to `weight` in the formula. - Supports empty tensor.
- Optional parameter.
- The shape length and input `input` in the channel axis length are equal.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ sumDy (aclTensor*) Input Represents the average of the sample mean of the output gradient, corresponding to `sumDy` in the formula. - Supports empty tensor.
- The shape length is equal to the channel axis length in the input `input`.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ sumDyXmu (aclTensor*) Input Represents the average of the product of the sample mean and the input gradient, corresponding to `sumDyXmu` in the formula. - Supports empty tensors.
- The shape length is equal to the channel axis length in the input parameter
input.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ counter (aclTensor*) Input Represents the count of input data, corresponding to `counter` in the formula. - Supports empty tensor.
INT32, FLOAT16, FLOAT32 ND 1-8 √ gradInput (aclTensor*) Output Represents the gradient of the input tensor, corresponding to `gradInput` in the formula. - Supports empty tensors.
- The shape must be consistent with the shape of `input`.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ workspaceSize (uint64_t*) Output Returns the workspace size that the user needs to apply on the Device side. - - - - - executor(aclOpExecutor**) Output Returns the operator executor, which contains the operator computation flow. - - - - - Atlas training products : The data type of the parametersgradOut,input,mean,invstd,weight,sumDy,sumDyXmu, andgradInputdoes 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 passed gradOut, input, mean, invstd, sumDy, sumDyXmu, counter, or gradInput is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data type of gradOut, input, mean, invstd, sumDy, sumDyXmu, counter, or gradInput is not within the supported range. When weight is a non-null pointer, the data type of weight is not within the supported range. The data format of gradOut, input, or gradInput is not within the supported range. The dimension of input is less than 2. The dimension of input, gradOut, gradInput, or counter is greater than 8. The size of the channel axis of input is 0. The shape of gradOut or gradInput is inconsistent with that of input. The shape of mean, invstd, sumDy, or sumDyXmu is inconsistent with the channel axis of input. When weight is a non-null pointer, the shape of weight is inconsistent with the channel axis of input.
aclnnBatchNormElemtBackward
Parameters
Parameter Input/Output Description workspace Input Workspace memory address applied on the Device side. workspaceSize Input Workspace size applied on the Device side, obtained from the first-phase API aclnnBatchNormElemtBackwardGetWorkspaceSize. 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.
Constraints
Deterministic computation:
- aclnnBatchNormElemtBackward 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_elemt_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 memory on the device side.
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);
// Copy data from the host side to the device side memory by calling aclrtMemcpy.
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];
}
// Create an aclTensor by calling the aclCreateTensor API.
*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. Refer to the acl API manual.
// Set deviceId based on the actual device.
int32_t deviceId = 0;
aclrtStream stream;
auto ret = Init(deviceId, &stream);
// Handle the check 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 = {1, 2, 4};
std::vector<int64_t> inputShape = {1, 2, 4};
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> counterShape = {2};
std::vector<int64_t> gradInputShape = {1, 2, 4};
void* gradOutDeviceAddr = nullptr;
void* inputDeviceAddr = nullptr;
void* meanDeviceAddr = nullptr;
void* invstdDeviceAddr = nullptr;
void* weightDeviceAddr = nullptr;
void* sumDyDeviceAddr = nullptr;
void* sumDyXmuDeviceAddr = nullptr;
void* counterDeviceAddr = nullptr;
void* gradInputDeviceAddr = nullptr;
aclTensor* gradOut = nullptr;
aclTensor* input = nullptr;
aclTensor* mean = nullptr;
aclTensor* invstd = nullptr;
aclTensor* weight = nullptr;
aclTensor* sumDy = nullptr;
aclTensor* sumDyXmu = nullptr;
aclTensor* counter = nullptr;
aclTensor* gradInput = nullptr;
std::vector<float> gradOutHostData = {0, 1, 2, 3, 4, 5, 6, 7};
std::vector<float> inputHostData = {0, 1, 2, 3, 4, 5, 6, 7};
std::vector<float> meanHostData = {0, 0};
std::vector<float> invstdHostData = {1, 1};
std::vector<float> weightHostData = {1, 1};
std::vector<float> sumDyHostData = {0, 0};
std::vector<float> sumDyXmuHostData = {1, 1};
std::vector<float> counterHostData = {5, 5};
std::vector<float> gradInputHostData(8, 0);
// Create the gradOut aclTensor.
ret = CreateAclTensor(gradOutHostData, gradOutShape, &gradOutDeviceAddr, aclDataType::ACL_FLOAT, &gradOut);
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 weight aclTensor.
ret = CreateAclTensor(weightHostData, weightShape, &weightDeviceAddr, aclDataType::ACL_FLOAT, &weight);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create mean aclTensor.
ret = CreateAclTensor(meanHostData, meanShape, &meanDeviceAddr, aclDataType::ACL_FLOAT, &mean);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create invstd aclTensor.
ret = CreateAclTensor(invstdHostData, invstdShape, &invstdDeviceAddr, aclDataType::ACL_FLOAT, &invstd);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create sumDy aclTensor.
ret = CreateAclTensor(sumDyHostData, sumDyShape, &sumDyDeviceAddr, aclDataType::ACL_FLOAT, &sumDy);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create sumDyXmu aclTensor.
ret = CreateAclTensor(sumDyXmuHostData, sumDyXmuShape, &sumDyXmuDeviceAddr, aclDataType::ACL_FLOAT, &sumDyXmu);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create counter aclTensor.
ret = CreateAclTensor(counterHostData, counterShape, &counterDeviceAddr, aclDataType::ACL_FLOAT, &counter);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the gradInput aclTensor.
ret = CreateAclTensor(gradInputHostData, gradInputShape, &gradInputDeviceAddr, aclDataType::ACL_FLOAT, &gradInput);
CHECK_RET(ret == ACL_SUCCESS, return ret);
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// aclnnBatchNormElemtBackward API call example
// 3. Call the CANN operator library API. Replace with the actual API name.
// Call the first-phase API of aclnnBatchNormElemtBackward.
ret = aclnnBatchNormElemtBackwardGetWorkspaceSize(
gradOut, input, mean, invstd, weight, sumDy, sumDyXmu, counter, gradInput, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnBatchNormElemtBackwardGetWorkspaceSize 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 aclnnBatchNormElemtBackward.
ret = aclnnBatchNormElemtBackward(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnBatchNormElemtBackward failed. ERROR: %d\n", ret); return ret);
// 4. (Boilerplate) Wait synchronously 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 size = GetShapeSize(gradInputShape);
std::vector<float> resultData(size, 0);
ret = aclrtMemcpy(
resultData.data(), resultData.size() * sizeof(resultData[0]), gradInputDeviceAddr, 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(gradOut);
aclDestroyTensor(input);
aclDestroyTensor(weight);
aclDestroyTensor(mean);
aclDestroyTensor(invstd);
aclDestroyTensor(sumDy);
aclDestroyTensor(sumDyXmu);
aclDestroyTensor(counter);
aclDestroyTensor(gradInput);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(gradOutDeviceAddr);
aclrtFree(inputDeviceAddr);
aclrtFree(weightDeviceAddr);
aclrtFree(meanDeviceAddr);
aclrtFree(invstdDeviceAddr);
aclrtFree(sumDyDeviceAddr);
aclrtFree(sumDyXmuDeviceAddr);
aclrtFree(counterDeviceAddr);
aclrtFree(gradInputDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}