aclnnBatchNormGatherStatsWithCounts
Applicable Products
| Product | Supported |
|---|---|
| × | |
| √ | |
| √ | |
| × | |
| × | |
| √ |
Function
Description:
Collects the mean and variance from all devices, and updates the global mean and the inverse of the standard deviation. The performance of BatchNorm is related to BatchSize. A larger BatchSize yields more accurate BatchNorm statistics. However, for tasks such as detection, which consume a large amount of video memory, a single GPU often uses only a small number of images, for example two, for training. This degrades the performance of BatchNorm. One solution is SyncBatchNorm, where all devices share the same BatchNorm to obtain global statistics.
The computation of aclnnBatchNormGatherStatsWithCounts depends on aclnnBatchNormStats to calculate the mean and the inverse of the standard deviation for single-device data.
Formula:
where the update formulas for runningMean and runningVar are as follows:
Function Prototype
Each operator is divided into a two-phase API. You must first call the "aclnnBatchNormGatherStatsWithCountsGetWorkspaceSize" API to obtain the input parameters and calculate the required workspace size based on the process, and then call the "aclnnBatchNormGatherStatsWithCounts" API to perform the computation.
aclnnStatus aclnnBatchNormGatherStatsWithCountsGetWorkspaceSize(
const aclTensor* input,
const aclTensor* mean,
const aclTensor* invstd,
aclTensor* runningMean,
aclTensor* runningVar,
double momentum,
double eps,
const aclTensor* counts,
aclTensor* meanAll,
aclTensor* invstdAll,
uint64_t* workspaceSize,
aclOpExecutor** executor)aclnnStatus aclnnBatchNormGatherStatsWithCounts(
void *workspace,
uint64_t workspaceSize,
aclOpExecutor *executor,
const aclrtStream stream)aclnnBatchNormGatherStatsWithCountsGetWorkspaceSize
Parameters
Parameter Input/Output Description Instruction Data Type Data Format Dimension (shape) Non-contiguous Tensor input (aclTensor*) Input Represents the sample values for statistics computation, corresponding to `x` in the formula. - Empty tensors are not supported.
- The second dimension is fixed as the channel axis.
FLOAT32, FLOAT16, BFLOAT16 ND 2-8 √ mean (aclTensor*) Input Represents the mean of the input data, corresponding to `E(x)` in the formula. - Empty tensors are not supported.
- The size of the first dimension must be consistent with
invstdandcounts, and the size of the second dimension must be consistent with the size of the channel axis ofinput.
FLOAT32, FLOAT16, BFLOAT16 ND 2 √ invstd (aclTensor*) Input Represents the inverse of the input data standard deviation, corresponding to the reciprocal of the square root of `Var(x) + eps` in the formula. - Empty tensors are not supported.
- All element values must be greater than 0. Precision is not guaranteed when values are less than or equal to 0.
- The size of the first dimension must be consistent with that of
meanandcounts, and the size of the second dimension must be consistent with the size of the channel axis ofinput.
FLOAT32, FLOAT16, BFLOAT16 ND 2 √ runningMean(aclTensor*) Input Optional parameter, representing the mean used to track the entire training process, corresponding to `runningMean` in the formula. - Does not support empty tensors.
- All element values must be greater than 0. Precision is not guaranteed when values are less than or equal to 0.
- When `runningMean` is a non-null pointer, the shape supports 1D, and the size must be consistent with the size of the channel axis of `input`.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ runningVar (aclTensor*) Input Optional parameter, representing the variance tracked throughout the entire training process, corresponding to `runningVar` in the formula. - Does not support empty tensors.
- All element values must be greater than 0. Precision is not guaranteed when values are less than or equal to 0.
- When `runningVar` is a non-null pointer, the shape supports 1D, and the size must be consistent with the size of the channel axis of `input`.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ momentum (double) Input Exponential smoothing parameter for runningMean and runningVar, corresponding to `momentum` in the formula. - - - - - eps (double) Input Specifies the value added to the variance to avoid division by zero. Corresponds to `eps` in the formula. - - - - - counts (aclTensor*) Input Indicates the number of elements in the input data. - Non-empty tensor.
- All element values must be positive integers. Other scenarios are not guaranteed.
- The size of the first dimension must be consistent with that of mean and invstd.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ meanAll (aclTensor*) Output Represents the mean of data across all devices, corresponding to `E(x)` in the formula. - Empty tensors are not supported.
- The shape must be consistent with the channel axis of the input parameter `input`.
- The data format must be consistent with the data format of the input parameter `input`.
FLOAT32, FLOAT16, BFLOAT16 ND 1 √ invstdAll (aclTensor*) Output Represents the reciprocal of the standard deviation of data across all devices, corresponding to the reciprocal of the square root of `Var(x) + eps` in the formula. - Does not support empty tensors.
- The shape must be consistent with the channel axis of the input parameter `input`.
- The data format must be consistent with the data format of the input parameter `input`.
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 op executor, which contains the operator computation flow. - - - - - Atlas training products andAtlas inference products : The data type of the parametersinput ,mean ,invstd ,runningMean ,runningVar ,counts ,meanAll , andinvstdAll 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 input, mean, invstd, counts, meanAll, or invstdAll is a null pointer. ACLNN_ERR_PARAM_INVALID 161002 The data type of input, meanAll, invstdAll, mean, invstd, runningMean, runningVar, or counts is not within the supported range. The data format of input, mean, invstd, or counts is not within the supported range. input has more than eight dimensions. input has fewer than two dimensions. mean or invstd does not have two dimensions. counts, mean, and invstd have inconsistent sizes in the first dimension. The size of mean or invstd in the second dimension is inconsistent with the channel axis of input. When runningMean is not empty, the size of runningMean is inconsistent with the channel axis of input. When runningVar is not empty, the size of runningVar is inconsistent with the channel axis of input. The dimension of counts is not 1D. The shape of meanAll or invstdAll is inconsistent with the channel axis of input.
aclnnBatchNormGatherStatsWithCounts
Parameters
Parameter Input/Output Description workspace Input Workspace memory address applied for on the device. workspaceSize Input Workspace size applied for on the device, obtained from the first-phase API aclnnBatchNormGatherStatsWithCountsGetWorkspaceSize. 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:
- aclnnBatchNormGatherStatsWithCounts 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_gather_stats_with_counts.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 to the device 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 aclCreateTensor.
*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.
// Enter the actual device ID.
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.
std::vector<int64_t> inputShape = {2, 4, 2};
std::vector<int64_t> meanShape = {4, 4};
std::vector<int64_t> rMeanShape = {4};
std::vector<int64_t> rVarShape = {4};
std::vector<int64_t> countsShape = {4};
std::vector<int64_t> invstdShape = {4, 4};
std::vector<int64_t> meanAllShape = {4};
std::vector<int64_t> invstdAllShape = {4};
void* inputDeviceAddr = nullptr;
void* meanDeviceAddr = nullptr;
void* rMeanDeviceAddr = nullptr;
void* rVarDeviceAddr = nullptr;
void* countsDeviceAddr = nullptr;
void* invstdDeviceAddr = nullptr;
void* meanAllDeviceAddr = nullptr;
void* invstdAllDeviceAddr = nullptr;
aclTensor* input = nullptr;
aclTensor* mean = nullptr;
aclTensor* rMean = nullptr;
aclTensor* rVar = nullptr;
aclTensor* counts = nullptr;
aclTensor* invstd = nullptr;
aclTensor* meanAll = nullptr;
aclTensor* invstdAll = nullptr;
std::vector<float> inputHostData = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
std::vector<float> meanHostData = {0, 1, 2, 3, 4, 5, 6, 7};
std::vector<float> rMeanHostData = {1, 2, 3, 4};
std::vector<float> rVarHostData = {5, 6, 7, 8};
std::vector<float> countsHostData = {1, 2, 3, 4};
std::vector<float> invstdHostData = {1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4};
std::vector<float> meanAllHostData = {4, 0};
std::vector<float> invstdAllHostData = {4, 0};
double momentum = 1e-2;
double eps = 1e-4;
// Create the input aclTensor.
ret = CreateAclTensor(inputHostData, inputShape, &inputDeviceAddr, aclDataType::ACL_FLOAT, &input);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the mean aclTensor.
ret = CreateAclTensor(meanHostData, meanShape, &meanDeviceAddr, aclDataType::ACL_FLOAT, &mean);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create the rMean aclTensor.
ret = CreateAclTensor(rMeanHostData, rMeanShape, &rMeanDeviceAddr, aclDataType::ACL_FLOAT, &rMean);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create rVar aclTensor.
ret = CreateAclTensor(rVarHostData, rVarShape, &rVarDeviceAddr, aclDataType::ACL_FLOAT, &rVar);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create counts aclTensor.
ret = CreateAclTensor(countsHostData, countsShape, &countsDeviceAddr, aclDataType::ACL_FLOAT, &counts);
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 meanAll aclTensor.
ret = CreateAclTensor(meanAllHostData, meanAllShape, &meanAllDeviceAddr, aclDataType::ACL_FLOAT, &meanAll);
CHECK_RET(ret == ACL_SUCCESS, return ret);
// Create invstdAll aclTensor.
ret = CreateAclTensor(invstdAllHostData, invstdAllShape, &invstdAllDeviceAddr, aclDataType::ACL_FLOAT, &invstdAll);
CHECK_RET(ret == ACL_SUCCESS, return ret);
uint64_t workspaceSize = 0;
aclOpExecutor* executor;
// Example of calling the aclnnBatchNormGatherStatsWithCounts API.
// 3. Call the CANN operator library API. Replace it with the specific API name.
// Call the first-phase API of aclnnBatchNormGatherStatsWithCounts.
ret = aclnnBatchNormGatherStatsWithCountsGetWorkspaceSize(
input, mean, invstd, rMean, rVar, momentum, eps, counts, meanAll, invstdAll, &workspaceSize, &executor);
CHECK_RET(ret == ACL_SUCCESS,
LOG_PRINT("aclnnBatchNormGatherStatsWithCountsGetWorkspaceSize 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 aclnnBatchNormGatherStatsWithCounts.
ret = aclnnBatchNormGatherStatsWithCounts(workspaceAddr, workspaceSize, executor, stream);
CHECK_RET(ret == ACL_SUCCESS, LOG_PRINT("aclnnBatchNormGatherStatsWithCounts 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.
auto size = GetShapeSize(meanAllShape);
std::vector<float> resultData(size, 0);
ret = aclrtMemcpy(
resultData.data(), resultData.size() * sizeof(resultData[0]), meanAllDeviceAddr, 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(input);
aclDestroyTensor(mean);
aclDestroyTensor(rMean);
aclDestroyTensor(rVar);
aclDestroyTensor(counts);
aclDestroyTensor(invstd);
aclDestroyTensor(meanAll);
aclDestroyTensor(invstdAll);
// 7. Release Device resources. Modify based on the specific API definition.
aclrtFree(inputDeviceAddr);
aclrtFree(meanDeviceAddr);
aclrtFree(rMeanDeviceAddr);
aclrtFree(rVarDeviceAddr);
aclrtFree(countsDeviceAddr);
aclrtFree(invstdDeviceAddr);
aclrtFree(meanAllDeviceAddr);
aclrtFree(invstdAllDeviceAddr);
if (workspaceSize > 0) {
aclrtFree(workspaceAddr);
}
aclrtDestroyStream(stream);
aclrtResetDevice(deviceId);
aclFinalize();
return 0;
}