CANN/ops-nn量化RMSNorm算子

张开发
2026/5/11 13:35:03 15 分钟阅读

分享文章

CANN/ops-nn量化RMSNorm算子
aclnnAddRmsNormQuantV2【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn 查看源码产品支持情况产品是否支持Ascend 950PR/Ascend 950DT×Atlas A3 训练系列产品/Atlas A3 推理系列产品√Atlas A2 训练系列产品/Atlas A2 推理系列产品√Atlas 200I/500 A2 推理产品×Atlas 推理系列产品√Atlas 训练系列产品×功能描述接口功能RmsNorm是大模型常用的标准化操作相比LayerNorm其去掉了减去均值的部分。AddRmsNormQuant算子将RmsNorm前的Add算子以及RmsNorm归一化的输出给到1个或2个Quantize算子融合起来减少搬入搬出操作。AddRmsNormQuantV2算子相较于AddRmsNormQuant在RmsNorm计算过程中增加了偏置项betaOptional参数即计算公式中的beta。计算公式$$ x_i{x1}_i{x2}_i $$$$ y_i\frac{1}{\operatorname{Rms}(\mathbf{x})} * x_i * gamma_i beta, \quad \text { where } \operatorname{Rms}(\mathbf{x})\sqrt{\frac{1}{n} \sum_{i1}^n x_i^2epsilon} $$$$ rmsNormOut_i\frac{1}{\operatorname{Rms}(x_i)} * x_i * gamma_i $$divMode为True时$$ y1Outround((y/scales1)zeroPoints1Optional) $$$$ y2Outround((y/scales2)zeroPoints2Optional) $$divMode为False时$$ y1Outround((y*scales1)zeroPoints1Optional) $$$$ y2Outround((y*scales2)zeroPoints2Optional) $$函数原型每个算子分为两段式接口必须先调用aclnnAddRmsNormQuantV2GetWorkspaceSize接口获取入参并根据计算流程所需workspace大小再调用aclnnAddRmsNormQuantV2接口执行计算。aclnnStatus aclnnAddRmsNormQuantV2GetWorkspaceSize( const aclTensor *x1, const aclTensor *x2, const aclTensor *gamma, const aclTensor *scales1, const aclTensor *scales2Optional, const aclTensor *zeroPoints1Optional, const aclTensor *zeroPoints2Optional, const aclTensor *betaOptional, int64_t axis, double epsilon, bool divMode, aclTensor *y1Out, aclTensor *y2Out, aclTensor *xOut, aclTensor *rmsNormOut, uint64_t *workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnAddRmsNormQuantV2( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, aclrtStream stream)aclnnAddRmsNormQuantV2GetWorkspaceSize参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensorx1aclTensor*输入表示标准化过程中的源数据张量。对应公式中的x1。支持空Tensor。FLOAT16、BFLOAT16ND1-8√x2aclTensor*输入表示标准化过程中的源数据张量。对应公式中的x2。支持空Tensor。shape和数据类型需要与x1保持一致。FLOAT16、BFLOAT16ND1-8√gammaaclTensor*输入表示标准化过程中的权重张量。对应公式中的gamma。支持空Tensor。数据类型需要与x1保持一致。shape需要与x1需要norm的维度保持一致。FLOAT16、BFLOAT16ND1-8√scales1aclTensor*输入表示量化过程中得到y1Out进行的scales张量对应公式中的scales1。支持空Tensor。shape需要与gamma保持一致。当参数divMode的值为True时该参数的值不能为0。FLOAT32、BFLOAT16ND1-8√scales2OptionalaclTensor*输入表示量化过程中得到y2Out进行的scales张量。对应公式中的scales2。支持空Tensor。可选参数支持传入空指针。shape和数据类型需要与scales1保持一致。当参数divMode的值为True时该参数的值不能为0。FLOAT32、BFLOAT16ND1-8√zeroPoints1OptionalaclTensor*输入表示量化过程中得到y1Out进行的offset张量。对应公式中的zeroPoints1Optional。支持空Tensor。可选参数支持传入空指针。shape需要与gamma保持一致。INT32、BFLOAT16ND1-8√zeroPoints2OptionalaclTensor*输入表示量化过程中得到y2Out进行的offset张量。对应公式中的zeroPoints2Optional。支持空Tensor。可选参数支持传入空指针。数据类型需要与zeroPoints1Optional保持一致。shape需要与gamma保持一致。INT32、BFLOAT16ND1-8√betaOptionalaclTensor*输入表示标准化过程中的偏置项。对应公式中的beta。支持空Tensor。可选参数支持传入空指针。shape和数据类型需要与gamma保持一致。FLOAT16、BFLOAT16---axisint64_t输入表示需要进行量化的elewise轴其他的轴做broadcast指定的轴不能超过输入x1的维度数。当前仅支持-1传其他值均不生效。----epsilondouble输入表示用于防止除0错误对应公式中的epsilon。建议传较小的正数。----divModebool输入表示决定量化公式是否使用除法的参数对应公式中的divMode。-----y1OutaclTensor*输出表示量化输出Tensor对应公式中的y1Out。支持空Tensor。shape需要与输入x1/x2一致。INT8ND1-8√y2OutaclTensor*输出表示量化输出Tensor对应公式中的y2Out。支持空Tensor。可选输出。shape需要与输入x1/x2一致。当scales2Optional为空时该输出的值无效。INT8ND1-8√xOutaclTensor*输出表示x1和x2的和对应公式中的x。支持空Tensor。shape和数据类型需要与输入x1/x2一致。FLOAT16、BFLOAT16ND1-8√rmsNormOutaclTensor*输出表示进行RmsNorm之后的结果对应公式中的rmsNormOut。支持空Tensor。可选输出。shape和数据类型需要与输入x1/x2一致。FLOAT16、BFLOAT16ND1-8√workspaceSizeuint64_t*输出返回需要在Device侧申请的workspace大小。-----executoraclOpExecutor**输出返回op执行器包含了算子计算流程。-----Atlas 推理系列产品 参数x1、x2、gamma、scales1、scales2Optional、zeroPoints1Optional、zeroPoints2Optional、betaOptional、xOut、rmsNormOut的数据类型不支持BFLOAT16。返回值aclnnStatus返回状态码具体参见aclnn返回码。第一段接口完成入参校验出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001如果传入参数是必选输入输出或者必选属性且是空指针则返回161001。ACLNN_ERR_PARAM_INVALID161002输入或输出的数据类型不在支持的范围之内。aclnnAddRmsNormQuantV2参数说明参数名输入/输出描述workspace输入在Device侧申请的workspace内存地址。workspaceSize输入在Device侧申请的workspace大小由第一段接口aclnnAddRmsNormQuantV2GetWorkspaceSize获取。executor输入op执行器包含了算子计算流程。stream输入指定执行任务的Stream。返回值aclnnStatus返回状态码。具体参见aclnn返回码约束说明Atlas 推理系列产品 x1、x2需要norm的维度数据个数不能小于32。gamma、betaOptional、scales1、scales2Optional、zeroPoints1Optional、zeroPoints2Optional的数据个数不能小于32。输入gamma、scales1、scales2Optional、zeroPoints1Optional、zeroPoints2Optional、betaOptional、divMode、y1Out、y2Out、xOut、rmsNormOut支持的场景和组合如下所示Atlas A2 训练系列产品/Atlas A2 推理系列产品 、 Atlas A3 训练系列产品/Atlas A3 推理系列产品 gammascales1scales2OptionalzeroPoints1OptionalzeroPoints2OptionalbetaOptionaldivModey1Outy2OutxOutrmsNormOutshape为[x1的最后一维]或[1, x1的最后一维]shape为[1]空指针必传shape为[1]空指针必传且shape与gamma保持一致True必传输出无效必传空指针shape为[x1的最后一维]或[1, x1的最后一维]shape为[1]空指针必传shape为[1]空指针空指针True必传输出无效空指针必传shape与x1需要norm的维度一致shape与gamma保持一致可选shape与gamma保持一致可选shape与gamma保持一致可选shape与gamma保持一致可选shape与gamma保持一致True/False必传当scales2Optional为空时该输出无效当scales2Optional非空时该输出有效必传空指针Atlas 推理系列产品 gammascales1scales2OptionalzeroPoints1OptionalzeroPoints2OptionalbetaOptionaldivModey1Outy2OutxOutrmsNormOutshape与x1需要norm的维度一致shape与gamma保持一致可选shape与gamma保持一致可选shape与gamma保持一致可选shape与gamma保持一致可选shape与gamma保持一致True/False必传当scales2Optional为空时该输出无效当scales2Optional非空时该输出有效必传空指针边界值场景说明Atlas 推理系列产品 输入不支持包含inf和NaN。Atlas A2 训练系列产品/Atlas A2 推理系列产品 、 Atlas A3 训练系列产品/Atlas A3 推理系列产品 当输入是inf时输出为inf。当输入是NaN时输出为NaN。维度的边界说明参数x1、x2、gamma、scales1、scales2Optional、zeroPoints1Optional、zeroPoints2Optional、betaOptional、y1Out、y2Out、xOut、rmsNormOut的shape中每一维大小都不大于INT32的最大值2147483647。数据格式说明所有输入输出Tensor的数据格式推荐使用ND格式其他数据格式会由框架默认转换成ND格式进行处理。各产品型号支持数据类型说明Atlas A2 训练系列产品/Atlas A2 推理系列产品 、 Atlas A3 训练系列产品/Atlas A3 推理系列产品 | x1数据类型 | x2数据类型 | gamma数据类型 | scales1数据类型 | scales2Optional数据类型 | zeroPoints1Optional数据类型 | zeroPoints2Optional数据类型 | betaOptional数据类型 | y1Out数据类型 | y2Out数据类型 | xOut数据类型 | rmsNormOut数据类型 | | - | - | - | - | - | - | - | - | - | - | - | - | | FLOAT16 | FLOAT16 | FLOAT16 | FLOAT32 | FLOAT32 | INT32 | INT32 | FLOAT16 | INT8 | INT8 | FLOAT16 | FLOAT16 | | BFLOAT16 | BFLOAT16 | BFLOAT16 | BFLOAT16 | BFLOAT16 | BFLOAT16 | BFLOAT16 | BFLOAT16 | INT8 | INT8 | BFLOAT16 | BFLOAT16 |Atlas 推理系列产品 | x1数据类型 | x2数据类型 | gamma数据类型 | scales1数据类型 | scales2Optional数据类型 | zeroPoints1Optional数据类型 | zeroPoints2Optional数据类型 | betaOptional数据类型 | y1Out数据类型 | y2Out数据类型 | xOut数据类型 | rmsNormOut数据类型 | | - | - | - | - | - | - | - | - | - | - | - | - | | FLOAT16 | FLOAT16 | FLOAT16 | FLOAT32 | FLOAT32 | INT32 | INT32 | FLOAT16 | INT8 | INT8 | FLOAT16 | FLOAT16 |确定性计算aclnnAddRmsNormQuantV2默认确定性实现。调用示例示例代码如下仅供参考具体编译和执行过程请参考编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_add_rms_norm_quant_v2.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::vectorint64_t shape) { int64_t shape_size 1; for (auto i : shape) { shape_size * i; } return shape_size; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法资源初始化 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::vectorT hostData, const std::vectorint64_t shape, void** deviceAddr, aclDataType dataType, aclTensor** tensor) { auto size GetShapeSize(shape) * sizeof(T); // 调用aclrtMalloc申请device侧内存 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); // 调用aclrtMemcpy将host侧数据拷贝到device侧内存上 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); // 计算连续tensor的strides std::vectorint64_t strides(shape.size(), 1); for (int64_t i shape.size() - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } // 调用aclCreateTensor接口创建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. 固定写法device/stream初始化参考acl API手册 // 根据自己的实际device填写deviceId int32_t deviceId 0; aclrtStream stream; auto ret Init(deviceId, stream); // check根据自己的需要处理 CHECK_RET(ret 0, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t xShape {64, 32}; std::vectorint64_t gammaShape {32}; std::vectorint64_t yShape {64, 32}; long long xShapeSize GetShapeSize(xShape); long long gammaShapeSize GetShapeSize(gammaShape); void* x1DeviceAddr nullptr; void* x2DeviceAddr nullptr; void* gammaDeviceAddr nullptr; void* betaDeviceAddr nullptr; void* scales1DeviceAddr nullptr; void* zeroPoints1DeviceAddr nullptr; void* y1DeviceAddr nullptr; void* y2DeviceAddr nullptr; void* xDeviceAddr nullptr; aclTensor* x1 nullptr; aclTensor* x2 nullptr; aclTensor* gamma nullptr; aclTensor* beta nullptr; aclTensor* scales1 nullptr; aclTensor* zeroPoints1 nullptr; aclTensor* y1 nullptr; aclTensor* y2 nullptr; aclTensor* x nullptr; std::vectorint16_t x1HostData(xShapeSize, 0); std::vectorint16_t x2HostData(xShapeSize, 0); std::vectorint16_t gammaHostData(gammaShapeSize, 0); std::vectorint16_t betaHostData(gammaShapeSize, 0); std::vectorfloat scales1HostData(gammaShapeSize, 1); std::vectorint32_t zeroPoints1HostData(gammaShapeSize, 100); std::vectorint8_t y1HostData(xShapeSize, 0); std::vectorint8_t y2HostData(xShapeSize, 0); std::vectorint16_t xHostData(xShapeSize, 0); float epsilon 1e-6; int64_t axis -1; bool divMode true; // 创建x1 aclTensor ret CreateAclTensor(x1HostData, xShape, x1DeviceAddr, aclDataType::ACL_FLOAT16, x1); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建x2 aclTensor ret CreateAclTensor(x2HostData, xShape, x2DeviceAddr, aclDataType::ACL_FLOAT16, x2); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建gamma aclTensor ret CreateAclTensor(gammaHostData, gammaShape, gammaDeviceAddr, aclDataType::ACL_FLOAT16, gamma); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建beta aclTensor ret CreateAclTensor(betaHostData, gammaShape, betaDeviceAddr, aclDataType::ACL_FLOAT16, beta); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建scales1 aclTensor ret CreateAclTensor(scales1HostData, gammaShape, scales1DeviceAddr, aclDataType::ACL_FLOAT, scales1); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建zeroPoints1 aclTensor ret CreateAclTensor(zeroPoints1HostData, gammaShape, zeroPoints1DeviceAddr, aclDataType::ACL_INT32, zeroPoints1); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建y1 aclTensor ret CreateAclTensor(y1HostData, yShape, y1DeviceAddr, aclDataType::ACL_INT8, y1); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建y2 aclTensor ret CreateAclTensor(y2HostData, yShape, y2DeviceAddr, aclDataType::ACL_INT8, y2); CHECK_RET(ret ACL_SUCCESS, return ret); // 创建x aclTensor ret CreateAclTensor(xHostData, xShape, xDeviceAddr, aclDataType::ACL_FLOAT16, x); CHECK_RET(ret ACL_SUCCESS, return ret); // 3. 调用CANN算子库API需要修改为具体的API uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnAddRmsNormQuantV2第一段接口 ret aclnnAddRmsNormQuantV2GetWorkspaceSize( x1, x2, gamma, scales1, nullptr, zeroPoints1, nullptr, beta, axis, epsilon, divMode, y1, y2, x, nullptr, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnAddRmsNormQuantV2GetWorkspaceSize failed. ERROR: %d\n, ret); return ret); // 根据第一段接口计算出的workspaceSize申请device内存 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;); } // 调用aclnnAddRmsNormQuantV2第二段接口 ret aclnnAddRmsNormQuantV2(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnAddRmsNormQuantV2 failed. ERROR: %d\n, ret); return ret); // 4. 固定写法同步等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 5. 获取输出的值将device侧内存上的结果拷贝至host侧需要根据具体API的接口定义修改 auto size GetShapeSize(yShape); std::vectorint8_t resultData(size, 0); ret aclrtMemcpy( resultData.data(), resultData.size() * sizeof(resultData[0]), y1DeviceAddr, size * sizeof(int8_t), 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: %d\n, i, resultData[i]); } // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(x1); aclDestroyTensor(x2); aclDestroyTensor(gamma); aclDestroyTensor(beta); aclDestroyTensor(scales1); aclDestroyTensor(zeroPoints1); aclDestroyTensor(y1); aclDestroyTensor(y2); aclDestroyTensor(x); // 7. 释放device资源需要根据具体API的接口定义修改 aclrtFree(x1DeviceAddr); aclrtFree(x2DeviceAddr); aclrtFree(gammaDeviceAddr); aclrtFree(betaDeviceAddr); aclrtFree(scales1DeviceAddr); aclrtFree(zeroPoints1DeviceAddr); aclrtFree(y1DeviceAddr); aclrtFree(y2DeviceAddr); aclrtFree(xDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }【免费下载链接】ops-nn本项目是CANN提供的神经网络类计算算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-nn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

更多文章