aclnnDeepNormGrad
Applicable Products
| Product | Supported |
|---|---|
| No | |
| Yes | |
| Yes | |
| No | |
| Yes | |
| No |
Function
Description: backward propagation of aclnnDeepNorm, which computes the gradients of tensor x, tensor gx, and tensor gamma, as well as the sum of tensor dy.
Formula:
Where:
Function Prototype
Each operator is divided into a two-phase API. The "aclnnDeepNormGradGetWorkspaceSize" API must be called first to obtain the workspace size required for computation and the executor that encapsulates the operator computation flow. Then, the "aclnnDeepNormGrad" API is called to perform the computation.
aclnnStatus aclnnDeepNormGradGetWorkspaceSize(
const aclTensor *dy,
const aclTensor *x,
const aclTensor *gx,
const aclTensor *gamma,
const aclTensor *mean,
const aclTensor *rstd,
double alpha,
const aclTensor *dxOut,
const aclTensor *dgxOut,
const aclTensor *dbetaOut,
const aclTensor *dgammaOut,
uint64_t *workspaceSize,
aclOpExecutor **executor)aclnnStatus aclnnDeepNormGrad(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
aclrtStream stream)aclnnDeepNormGradGetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (shape) Non-contiguous Tensor dy (aclTensor*) Input Represents the primary gradient input, corresponding to `dy` in the formula. - Supports empty tensors.
- The data type and shape are consistent with `x`.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ x (aclTensor*) Input Represents the input tensor of the forward fusion operator, corresponding to `x` in the formula. Supports empty tensors. FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ gx (aclTensor*) Input Indicates the input tensor of the forward fusion operator, corresponding to `gx` in the formula. - Supports empty tensors.
- The data type and shape are consistent with `x`.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ gamma (aclTensor*) Input Represents the scaling parameter for forward propagation, corresponding to `gamma` in the formula. - Supports empty tensors.
- The data type is the same as that of `x`.
- The shape dimensions are the same as the last several dimensions of the input `x`, where the last several dimensions represent the dimensions to be normalized.
FLOAT32, FLOAT16, BFLOAT16 ND 1-7 √ mean (aclTensor*) Input Represents the mean of the sum of forward inputs x and gx, corresponding to `mean` in the formula. - Supports empty tensors.
- The shape dimensions are the same as the first few dimensions of the input `x`, where the first few dimensions represent the dimensions that do not require normalization.
FLOAT32 ND 2-8 √ rstd (aclTensor*) Input rstd of the sum of forward inputs x and gx, corresponding to `rstd` in the formula. - Supports empty tensors.
- The shape is consistent with the shape of `mean`.
FLOAT32 ND 2-8 √ alpha (double) Input Represents a weight parameter used to adjust the weight of the input data, corresponding to `alpha` in the formula. - - - - - dxOut (aclTensor*) Output Represents the computed gradient output, used to update the gradient of the input data x. Corresponds to `dx` in the formula. - Supports empty tensors.
- The data type and shape are consistent with `x`.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ dgxOut (aclTensor*) Output Represents the gradient of the computation output, used to update the gradient of the input data gx. Corresponds to `dgx` in the formula. - Supports empty tensors.
- Data type and shape are the same as those of `x`.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ dbetaOut (aclTensor*) Output Represents the computed output gradient, used to update the gradient of the bias parameter. Corresponds to `dbeta` in the formula. - Empty tensor is supported.
- The shape is consistent with that of `gamma`.
FLOAT32 ND 1-7 √ dgammaOut (aclTensor*) Output Represents the gradient of the computed output, used to update the gradient of the scaling parameter. Corresponds to `dgamma` in the formula. - Supports empty tensors.
- The shape is consistent with `gamma`.
FLOAT32 ND 1-7 √ workspaceSize(uint64_t*) Output Returns the workspace size that the user needs to apply for on the Device side. - - - - - executor (aclOpExecutor**) Output Returns the op executor, which contains the operator computation process. - - - - - Atlas inference products : The data type of parametersdy,x,gx,gamma,dxOut, anddgxOutdoes 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 Returns 161001 if the input parameter is a mandatory input, output, or mandatory attribute and is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data type of the input or output is not within the supported range. The shapes of the input and output do not match or are not within the supported dimension range. The data types of the input and output do not meet the constraints in the parameter description.
aclnnDeepNormGrad
Parameters
Parameter Input/Output Description workspace Input Workspace memory address applied for on the Device side. workspaceSize Input Workspace size applied for on the Device side, obtained through the first-phase API aclnnDeepNormGradGetWorkspaceSize. executor Input Operator executor, which contains the operator computation process. stream Input Specifies the stream for task execution. Return Value
aclnnStatus: return code. For details, see aclnn Return Code.
Constraints
Unsupported type description:
DOUBLE: The instruction does not support DOUBLE.
Boundary value scenario description:
When the input is Inf, the output is Inf.
When the input is NaN, the output is NaN.
Deterministic computation:
- aclnnDeepNormGrad defaults to a non-deterministic implementation. Deterministic computation cannot be enabled through aclrtCtxSetSysParamOpt.
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_deep_norm_grad.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 for 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);
// 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];
}
// 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.
// Set deviceId based on the actual device.
int32_t deviceId = 0;
aclrtStream stream;
auto ret = Init(deviceId, &stream);
// Handle based on your own needs.
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("Init acl failed. ERROR: %d\n", ret); return ret);
// 2. Construct inputs and outputs. Customize the construction based on the API.
float alpha = 0.3;
std::vector<int64_t> dyShape = {3, 1, 4};
std::vector<int64_t> xShape = {3, 1, 4};
std::vector<int64_t> gxShape = {3, 1, 4};
std::vector<int64_t> gammaShape = {4};
std::vector<int64_t> meanShape = {3, 1, 1};
std::vector<int64_t> rstdShape = {3, 1, 1};
std::vector<int64_t> outputpdxShape = {3, 1, 4};
std::vector<int64_t> outputpdgxShape = {3, 1, 4};
std::vector<int64_t> outputpdbetaShape = {4};
std::vector<int64_t> outputpdgammaShape = {4};
void* dyDeviceAddr = nullptr;
void* xDeviceAddr = nullptr;
void* gxDeviceAddr = nullptr;
void* gammaDeviceAddr = nullptr;
void* meanDeviceAddr = nullptr;
void* rstdDeviceAddr = nullptr;
void* outputpdxDeviceAddr = nullptr;
void* outputpdgxDeviceAddr = nullptr;
void* outputpdbetaDeviceAddr = nullptr;
void* outputpdgammaDeviceAddr = nullptr;
aclTensor* dy = nullptr;
aclTensor* x = nullptr;
aclTensor* gx = nullptr;
aclTensor* gamma = nullptr;
aclTensor* mean = nullptr;
aclTensor* rstd = nullptr;
aclTensor* outputpdx = nullptr;
aclTensor* outputpdgx = nullptr;
aclTensor* outputpdbeta = nullptr;
aclTensor* outputpdgamma = nullptr;
std::vector<float> dyHostData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
std::vector<float> xHostData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
std::vector<float> gxHostData = {2, 2, 2, 4, 4, 4, 6, 6, 6, 8, 8, 8};
std::vector<float> gammaHostData = {0, 1, 2, 3};
std::vector<float> meanHostData = {0, 1, 2};
std::vector<float> rstdHostData = {0, 1, 2};
std::vector<float> outputpdxHostData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
std::vector<float> outputpdgxHostData = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
std::vector<float> outputpdbetaHostData = {0, 1, 2, 3};
std::vector<float> outputpdgammaHostData = {0, 1, 2, 3};
// Create the self aclTensor.
ret = CreateAclTensor(dyHostData, dyShape, &dyDeviceAddr, aclDataType::ACL_FLOAT, &dy);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(xHostData, xShape, &xDeviceAddr, aclDataType::ACL_FLOAT, &x);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(gxHostData, gxShape, &gxDeviceAddr, aclDataType::ACL_FLOAT, &gx);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(gammaHostData, gammaShape, &gammaDeviceAddr, aclDataType::ACL_FLOAT, &gamma);
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(rstdHostData, rstdShape, &rstdDeviceAddr, aclDataType::ACL_FLOAT, &rstd);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(outputpdxHostData, outputpdxShape, &outputpdxDeviceAddr, aclDataType::ACL_FLOAT, &outputpdx);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(
outputpdgxHostData, outputpdgxShape, &outputpdgxDeviceAddr, aclDataType::ACL_FLOAT, &outputpdgx);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(
outputpdbetaHostData, outputpdbetaShape, &outputpdbetaDeviceAddr, aclDataType::ACL_FLOAT, &outputpdbeta);
CHECK_RET(ret == ACL_SUCCESS, return ret);
ret = CreateAclTensor(
outputpdgammaHostData, outputpdgammaShape, &outputpdgammaDeviceAddr, aclDataType::ACL_FLOAT, &outputpdgamma);
CHECK_RET(ret == ACL_SUCCESS, return ret);
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Example of aclnnDeepNormGrad API call.
// 3. Call the CANN operator library API. Replace with the specific API name.
// Call the first-phase API of aclnnDeepNormGrad.
LOG_PRINT("\nUse aclnnDeepNormGrad Port.");
ret = aclnnDeepNormGradGetWorkspaceSize(
dy, x, gx, gamma, mean, rstd, alpha, outputpdx, outputpdgx, outputpdbeta, outputpdgamma, &workspaceSize,
&executor);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnDeepNormGradGetWorkspaceSize 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 aclnnDeepNormGrad.
ret = aclnnDeepNormGrad(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnDeepNormGrad 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 this based on the specific API definition.
auto outputpdxsize = GetShapeSize(outputpdxShape);
std::vector<float> resultDataPdx(outputpdxsize, 0);
ret = aclrtMemcpy(
resultDataPdx.data(), resultDataPdx.size() * sizeof(resultDataPdx[0]), outputpdxDeviceAddr,
outputpdxsize * sizeof(resultDataPdx[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);
LOG_PRINT("== pdx output");
for (int64_t i = 0; i < outputpdxsize; i++) {
LOG_PRINT("result[%ld] is: %f\n", i, resultDataPdx[i]);
}
auto outputpdgxsize = GetShapeSize(outputpdgxShape);
std::vector<float> resultDataPdgx(outputpdgxsize, 0);
ret = aclrtMemcpy(
resultDataPdgx.data(), resultDataPdgx.size() * sizeof(resultDataPdgx[0]), outputpdgxDeviceAddr,
outputpdgxsize * sizeof(resultDataPdgx[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);
LOG_PRINT("== pdgx output");
for (int64_t i = 0; i < outputpdgxsize; i++) {
LOG_PRINT("result[%ld] is: %f\n", i, resultDataPdgx[i]);
}
auto outputpdbetasize = GetShapeSize(outputpdbetaShape);
std::vector<float> resultDataPdBeta(outputpdbetasize, 0);
ret = aclrtMemcpy(
resultDataPdBeta.data(), resultDataPdBeta.size() * sizeof(resultDataPdBeta[0]), outputpdbetaDeviceAddr,
outputpdbetasize * sizeof(resultDataPdBeta[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);
LOG_PRINT("== pdbeta output");
for (int64_t i = 0; i < outputpdbetasize; i++) {
LOG_PRINT("result[%ld] is: %f\n", i, resultDataPdBeta[i]);
}
auto outputpdgammasize = GetShapeSize(outputpdgammaShape);
std::vector<float> resultDataPdGamma(outputpdgammasize, 0);
ret = aclrtMemcpy(
resultDataPdGamma.data(), resultDataPdGamma.size() * sizeof(resultDataPdGamma[0]), outputpdgammaDeviceAddr,
outputpdgammasize * sizeof(resultDataPdGamma[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);
LOG_PRINT("== pdgamma output");
for (int64_t i = 0; i < outputpdgammasize; i++) {
LOG_PRINT("result[%ld] is: %f\n", i, resultDataPdGamma[i]);
}
// 6. Release aclTensor and aclScalar. Modify based on the specific API definition.
aclDestroyTensor(dy);
aclDestroyTensor(x);
aclDestroyTensor(gx);
aclDestroyTensor(gamma);
aclDestroyTensor(mean);
aclDestroyTensor(rstd);
aclDestroyTensor(outputpdx);
aclDestroyTensor(outputpdgx);
aclDestroyTensor(outputpdbeta);
aclDestroyTensor(outputpdgamma);
// 7. Release device resources. Modify based on the specific API definition.
aclrtFree(dyDeviceAddr);
aclrtFree(xDeviceAddr);
aclrtFree(gxDeviceAddr);
aclrtFree(gammaDeviceAddr);
aclrtFree(meanDeviceAddr);
aclrtFree(rstdDeviceAddr);
aclrtFree(outputpdxDeviceAddr);
aclrtFree(outputpdgxDeviceAddr);
aclrtFree(outputpdbetaDeviceAddr);
aclrtFree(outputpdgammaDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}