aclnnBlockSparseAttentionGrad【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer产品支持情况产品是否支持Ascend 950PR/Ascend 950DT×Atlas A3 训练系列产品√Atlas A3 推理系列产品×Atlas A2 训练系列产品√Atlas A2 推理系列产品×Atlas 200I/500 A2 推理产品×Atlas 推理系列产品×Atlas 训练系列产品×功能说明​接口功能​aclnnBlockSparseAttention稀疏注意力反向计算支持灵活的块级稀疏模式通过BlockSparseMask指定每个Q块选择的KV块实现高效的稀疏注意力计算。​计算公式​ 稀疏块大小$blockShapeX×blockShapeY$BlockSparseMask指定稀疏模式。已知正向计算公式为$$ attentionOutSoftmax(Mask(scale⋅query⋅key_{sparse}^{T}, atten_mask))⋅value_{sparse} $$为方便表达以变量$S$和$P$表示计算公式$$ S Mask(scale⋅query⋅key_{sparse}^{T},atten_mask) $$$$ P SoftMax(S) $$$$ V value_{sparse} $$$$ Out PV $$则反向计算公式为$$ softmax_grad softmaxGrad(dOut, attentionOut) $$$$ dPdOut * V^T $$$$ dS P * (dP-softmax_grad) $$$$ dVP^T * dOut $$$$ dQ(dS*K)*scale $$$$ dK(dS^T*Q)*scale $$BlockSparseAttentionGrad输入dout、 query、key、value, attentionOut的数据排布格式支持从多种维度排布解读可通过qInputLayout和kvInputLayout传入。为了方便理解后续支持的具体排布格式如 BNSD、TND 等此处先对排布格式中各缩写字母所代表的维度含义进行统一说明B表示输入样本批量大小BatchTB和S合轴紧密排列的长度Total tokensS表示输入样本序列长度Seq-LengthH表示隐藏层的大小Head-SizeN表示多头数Head-NumD表示隐藏层最小的单元尺寸需满足DH/NHead-Dim当前支持的布局qInputLayout: TND BNSDkvInputLayout: TND BNSD函数原型每个算子分为两段式接口必须先调用aclnnBlockSparseAttentionGradGetWorkspaceSize接口获取计算所需workspace大小以及包含了算子计算流程的执行器再调用aclnnBlockSparseAttentionGrad接口执行计算。aclnnStatus aclnnBlockSparseAttentionGradGetWorkspaceSize( const aclTensor *dout, const aclTensor *query, const aclTensor *key, const aclTensor *value, const aclTensor *attentionOut, const aclTensor *softmaxLse, const aclTensor *blockSparseMaskOptional, const aclTensor *attenMaskOptional, const aclIntArray *blockShapeOptional, const aclIntArray *actualSeqLengthsOptional, const aclIntArray *actualSeqLengthsKvOptional, char *qInputLayout, char *kvInputLayout, int64_t numKeyValueHeads, int64_t maskType, double scaleValue, int64_t preTokens, int64_t nextTokens, aclTensor *dq, aclTensor *dk, aclTensor *dv, uint64_t *workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnBlockSparseAttentionGrad( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream)aclnnBlockSparseAttentionGradGetWorkspaceSize参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续TensordoutaclTensor*输入反向输出梯度代表最终输出对当前算子的梯度信息。不支持空Tensor。支持的shape为TND: [totalQTokens, headNum, headDim]。BNSD: [batch, headNum, maxQSeqLength, headDim]。FLOAT16、BFLOAT16ND3-4×queryaclTensor*输入注意力计算中的查询向量即公式中的query。不支持空Tensor。支持的shape为TND: [totalQTokens, headNum, headDim]。BNSD: [batch, headNum, maxQSeqLength, headDim]。FLOAT16、BFLOAT16ND3-4×keyaclTensor*输入注意力计算中的键向量即公式中的key。不支持空Tensor。支持的shape为TND: [totalKTokens, numKeyValueHeads, headDim]。BNSD: [batch, numKeyValueHeads, maxKvSeqLength, headDim]。FLOAT16、BFLOAT16ND3-4×valueaclTensor*输入注意力计算中的值向量即公式中的value。不支持空Tensor。支持的shape为TND: [totalVTokens, numKeyValueHeads, headDim]。BNSD: [batch, numKeyValueHeads, maxKvSeqLength, headDim]。FLOAT16、BFLOAT16ND3-4×attentionOutaclTensor*输入正向 BlockSparseAttention 计算的输出结果即公式中的attentionOut。不支持空Tensor。支持的shape为TND: [totalQTokens, headNum, headDim]。BNSD: [batch, headNum, maxQSeqLength, headDim]。FLOAT16、BFLOAT16ND3-4×softmaxLseaclTensor*输入Softmax计算的log-sum-exp中间结果。用于反向计算梯度的对数和指数逆推。不支持空Tensor。支持的shape为TND: [totalQTokens, headNum, 1]。BNSD: [batch, headNum, maxQSeqLength, 1]。FLOATND3-4×blockSparseMaskOptionalaclTensor*输入块状稀疏掩码表示实际的稀疏pattern。决定哪些block实际参与注意力计算。不支持空Tensor。可选输入当前版本为必选shape为[batch, headNum, ceilDiv(maxQSeqLength, blockShapeX), ceilDiv(maxKvSeqLength, blockShapeY)]。表示按block划分后哪些block需要参与计算为1哪些block不参与计算为0。如传入nullptr则视为不开启块稀疏计算即所有token之间的注意力分数都会被计算。BOOLND4×attenMaskOptionalaclTensor*输入注意力掩码即公式中的atten_mask。用于屏蔽不应参与计算的特定token。支持空Tensor。当前不支持应传入nullptr。BOOLND2×blockShapeOptionalaclIntArray*输入稀疏块形状数组。指定每个稀疏块的二维尺寸行数和列数。当配置了blockSparseMaskOptional时如配置此输入算子会从中获取稀疏块尺寸如不配置此输入算子将默认稀疏块尺寸为[128,128]。INT64-1-当未配置blockSparseMaskOptional时无论此项如何配置算子均将忽略。当配置此输入时的元素要求必须包含至少两个元素 [blockShapeX, blockShapeY]。blockShapeX: Q方向块大小值必须大于0。blockShapeY: KV方向块大小值必须大于0。actualSeqLengthsOptionalaclIntArray*输入query的实际序列长度数组。用于描述变长序列场景下即含有 Padding 填充数据的场景每个 Batch 中实际有效的 query token 数量。变长序列场景当 qInputLayout 为 TND 时该项输入必须配置。因为 TND 格式为一维连续排布算子需要依赖该数组来准确切分界定各个序列的真实边界。INT64-1-定长/变长场景当 qInputLayout 为 BNSD 时如配置该项算子会按指定的有效长度处理忽略 Padding 部分的数据提升性能如不配置传 nullptr算子将默认把 query shape 中的 S 维度作为有效长度进行全量处理。actualSeqLengthsKvOptionalaclIntArray*输入key/value的实际序列长度数组。用于描述变长序列场景下即含有 Padding 填充数据的场景每个 Batch 中实际有效的 key/value token 数量。变长序列场景当 kvInputLayout 为 TND 时该项输入必须配置。因为 TND 格式为一维连续排布算子需要依赖该数组来准确切分界定各个序列的真实边界。INT64-1-定长/变长场景当 kvInputLayout 为 BNSD 时如配置该项算子会按指定的有效长度处理忽略 Padding 部分的数据提升性能如不配置传 nullptr算子将默认把 key/value shape 中的 S 维度作为有效长度进行全量处理。qInputLayoutchar*输入query的数据排布格式。指示输入张量在内存中的具体排布如连续或合轴排列。当前仅支持TND、BNSDqInputLayout与kvInputLayout需要保持一致。----kvInputLayoutchar*输入key和value的数据排布格式。指示输入张量在内存中的具体排布。当前仅支持TND、BNSDqInputLayout与kvInputLayout需要保持一致。----numKeyValueHeadsint64_t输入key/value的注意力头数。用于支持GQA分组查询注意力机制下的头数比例映射。-----maskTypeint64_t输入注意力计算中的掩码类型。指定采用何种预设规则的掩码逻辑。当前只支持传 0代表不加mask场景。----scaleValuedouble输入缩放系数即公式中的scale。用于注意力分数的归一化处理。一般设置为D^-0.5。----preTokensint64_t输入滑窗向前包含的token数量。限制当前token只能与前方的多少个历史token计算注意力。用于滑窗attention场景当前不支持滑窗attention只支持传入2147483647。----nextTokensint64_t输入滑窗向后包含的token数量。限制当前token只能与后方的多少个未来token计算注意力。用于滑窗attention场景当前不支持滑窗attention只支持传入2147483647。----dqaclTensor*输出query的梯度输出结果即公式中的dq。不支持空Tensor。数据类型和shape与输入query保持一致。FLOAT16、BFLOAT16ND3-4√dkaclTensor*输出key的梯度输出结果即公式中的dk。不支持空Tensor。数据类型和shape与输入key保持一致。FLOAT16、BFLOAT16ND3-4√dvaclTensor*输出value的梯度输出结果即公式中的dv。不支持空Tensor。数据类型和shape与输入value保持一致。FLOAT16、BFLOAT16ND3-4√workspaceSizeuint64_t*输出返回需要在Device侧申请的workspace大小。-----executoraclOpExecutor**输出返回op执行器包含了算子计算流程。-----返回值aclnnStatus返回状态码具体参见aclnn返回码。第一段接口完成入参校验出现以下场景时报错返回码错误码描述ACLNN_ERR_PARAM_NULLPTR161001输入doutquerykeyvalueattentionOut传入的是空指针。qInputLayout为TND时actualSeqLengthsOptional传入的是空指针。kvInputLayout为TND时actualSeqLengthsKvOptional传入的是空指针。ACLNN_ERR_PARAM_INVALID161002doutquerykeyvalue 数据类型不在支持的范围之内。qInputLayout或kvInputLayout输入不合法参数有效性校验失败。aclnnBlockSparseAttentionGrad参数说明参数名输入/输出描述workspace输入在Device侧申请的workspace内存地址。workspaceSize输入在Device侧申请的workspace大小由第一段接口aclnnBlockSparseAttentionGradGetWorkspaceSize获取。executor输入op执行器包含了算子计算流程。stream输入指定执行任务的AscendCL stream流。返回值aclnnStatus返回状态码具体参见aclnn返回码。约束说明该接口与PyTorch配合使用时需要保证CANN相关包与PyTorch相关包的版本匹配。actualSeqLengthsOptional在qInputLayout为“TND”时必选actualSeqLengthsKvOptional在kvInputLayout为“TND”时必选。根据算子支持的输入 Layoutquery 张量 Shape 中对应的 head 维度大小记为 N1key 和 value 张量 Shape 中对应的 head 维度大小记为 N2。必须满足 N1 N2 且 N1 % N2 0。(例如在 BNSD 布局下N1 对应 query 的第 2 维N2 对应 key/value 的第 2 维)headdim128。当前只支持 BNSD 和 MHA(N1N2)。调用示例示例代码如下仅供参考具体编译和执行过程请参考编译与运行样例。#include iostream #include vector #include cstring #include cmath #include cstdint #include acl/acl.h #include aclnn/opdev/fp16_t.h #include ../op_host/op_api/aclnn_block_sparse_attention_grad.h using namespace std; #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 shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } int Init(int32_t deviceId, aclrtStream* stream) { // 固定写法AscendCL初始化 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) { // 检查shape是否有效 if (shape.empty()) { LOG_PRINT(CreateAclTensor: ERROR - shape is empty\n); return -1; } for (size_t i 0; i shape.size(); i) { if (shape[i] 0) { LOG_PRINT(CreateAclTensor: ERROR - shape[%zu]%ld is invalid\n, i, shape[i]); return -1; } } auto size GetShapeSize(shape) * sizeof(T); // 检查hostData大小是否匹配 if (hostData.size() ! static_castsize_t(GetShapeSize(shape))) { LOG_PRINT(CreateAclTensor: ERROR - hostData size mismatch: %zu vs %ld\n, hostData.size(), GetShapeSize(shape)); return -1; } // 调用aclrtMalloc申请device侧内存 *deviceAddr nullptr; 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); aclrtFree(*deviceAddr); *deviceAddr nullptr; return ret); // 计算连续tensor的strides std::vectorint64_t strides(shape.size(), 1); if (shape.size() 1) { for (int64_t i static_castint64_t(shape.size()) - 2; i 0; i--) { strides[i] shape[i 1] * strides[i 1]; } } // 调用aclCreateTensor接口创建aclTensor *tensor nullptr; *tensor aclCreateTensor(shape.data(), shape.size(), dataType, strides.data(), 0, aclFormat::ACL_FORMAT_ND, shape.data(), shape.size(), *deviceAddr); CHECK_RET(*tensor ! nullptr, LOG_PRINT(aclCreateTensor failed - returned nullptr\n); aclrtFree(*deviceAddr); *deviceAddr nullptr; return -1); return 0; } int main() { // 1. device/stream初始化 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. 设置核心参数 (以 BNSD Layout 为例) int32_t batch 1; int32_t numHeads 1; int32_t numKvHeads 1; int32_t qSeqlen 128; int32_t kvSeqlen 128; int32_t headDim 128; int32_t blockShapeX 64; int32_t blockShapeY 64; // 块数量计算 int32_t ceilQ (qSeqlen blockShapeX - 1) / blockShapeX; int32_t ceilKv (kvSeqlen blockShapeY - 1) / blockShapeY; // 3. 构建张量 Shape std::vectorint64_t qShape {batch, numHeads, qSeqlen, headDim}; std::vectorint64_t kvShape {batch, numKvHeads, kvSeqlen, headDim}; std::vectorint64_t lseShape {batch, numHeads, qSeqlen}; // LSE 通常没有尾部 1 维防止 GE squeeze std::vectorint64_t maskShape {batch, numHeads, ceilQ, ceilKv}; // 4. 分配并初始化 Host 数据 int64_t qSize GetShapeSize(qShape); int64_t kvSize GetShapeSize(kvShape); // 将 Q, K, V 初始化为 0.1f 等较小的数 std::vectorop::fp16_t qData(qSize, 0.1f); std::vectorop::fp16_t kData(kvSize, 0.1f); std::vectorop::fp16_t vData(kvSize, 0.1f); // 梯度初始值可以给一个小正数 std::vectorop::fp16_t doutData(qSize, 0.01f); std::vectorop::fp16_t outData(qSize, 0.1f); // LSE 给一个合理的正数比如 5.0f这样 exp(S - LSE) 就是一个非常安全的负指数绝对不会溢出 std::vectorfloat lseData(GetShapeSize(lseShape), 5.0f); std::vectoruint8_t maskData(GetShapeSize(maskShape), 1); // 创建所有的前向输入/输出 aclTensor void *qAddr nullptr, *kAddr nullptr, *vAddr nullptr; void *doutAddr nullptr, *outAddr nullptr; void *lseAddr nullptr, *maskAddr nullptr; aclTensor *qTensor nullptr, *kTensor nullptr, *vTensor nullptr; aclTensor *doutTensor nullptr, *outTensor nullptr; aclTensor *lseTensor nullptr, *maskTensor nullptr; CreateAclTensor(qData, qShape, qAddr, aclDataType::ACL_FLOAT16, qTensor); CreateAclTensor(kData, kvShape, kAddr, aclDataType::ACL_FLOAT16, kTensor); CreateAclTensor(vData, kvShape, vAddr, aclDataType::ACL_FLOAT16, vTensor); CreateAclTensor(doutData, qShape, doutAddr, aclDataType::ACL_FLOAT16, doutTensor); CreateAclTensor(outData, qShape, outAddr, aclDataType::ACL_FLOAT16, outTensor); CreateAclTensor(lseData, lseShape, lseAddr, aclDataType::ACL_FLOAT, lseTensor); // 严格使用 FP32 CreateAclTensor(maskData, maskShape, maskAddr, aclDataType::ACL_UINT8, maskTensor); // 严格使用 UINT8 // 5. 创建反向输出梯度 (dq, dk, dv) std::vectorop::fp16_t dqData(qSize, 0.0f); std::vectorop::fp16_t dkData(kvSize, 0.0f); std::vectorop::fp16_t dvData(kvSize, 0.0f); void *dqAddr nullptr, *dkAddr nullptr, *dvAddr nullptr; aclTensor *dqTensor nullptr, *dkTensor nullptr, *dvTensor nullptr; CreateAclTensor(dqData, qShape, dqAddr, aclDataType::ACL_FLOAT16, dqTensor); CreateAclTensor(dkData, kvShape, dkAddr, aclDataType::ACL_FLOAT16, dkTensor); CreateAclTensor(dvData, kvShape, dvAddr, aclDataType::ACL_FLOAT16, dvTensor); // 6. 创建 aclIntArray 属性参数 (BlockShape ActualSeqLengths) std::vectorint64_t blockShapeVec {blockShapeX, blockShapeY}; aclIntArray *blockShapeArr aclCreateIntArray(blockShapeVec.data(), blockShapeVec.size()); std::vectorint64_t qSeqLenVec(batch, static_castint64_t(qSeqlen)); std::vectorint64_t kvSeqLenVec(batch, static_castint64_t(kvSeqlen)); aclIntArray *qSeqLenArr aclCreateIntArray(qSeqLenVec.data(), batch); aclIntArray *kvSeqLenArr aclCreateIntArray(kvSeqLenVec.data(), batch); // 7. 标量与字符串参数配置 char qLayoutBuffer[16] BNSD; char kvLayoutBuffer[16] BNSD; int64_t maskType 0; double scaleValue 1.0 / std::sqrt(static_castdouble(headDim)); // 强制规定滑动窗口极大值 int64_t preTokens 2147483647; int64_t nextTokens 2147483647; // 8. 调用第一段接口: GetWorkspaceSize uint64_t workspaceSize 0; aclOpExecutor* executor nullptr; LOG_PRINT(Calling aclnnBlockSparseAttentionGradGetWorkspaceSize...\n); ret aclnnBlockSparseAttentionGradGetWorkspaceSize( doutTensor, qTensor, kTensor, vTensor, outTensor, lseTensor, maskTensor, // blockSparseMaskOptional nullptr, // attenMaskOptional 必须为空 blockShapeArr, qSeqLenArr, kvSeqLenArr, qLayoutBuffer, kvLayoutBuffer, static_castint64_t(numKvHeads), maskType, scaleValue, preTokens, nextTokens, dqTensor, dkTensor, dvTensor, workspaceSize, executor ); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(GetWorkspaceSize failed. ERROR: %d\n, ret); return ret); CHECK_RET(executor ! nullptr, LOG_PRINT(executor is null after GetWorkspaceSize\n); return -1); LOG_PRINT(Workspace size required: %lu bytes\n, workspaceSize); // 9. 分配 workspace 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); } // 10. 调用第二段接口: 执行计算 LOG_PRINT(Calling aclnnBlockSparseAttentionGrad...\n); ret aclnnBlockSparseAttentionGrad(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnBlockSparseAttentionGrad failed. ERROR: %d\n, ret); return ret); // 11. 同步 Stream等待任务执行结束 ret aclrtSynchronizeStream(stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclrtSynchronizeStream failed. ERROR: %d\n, ret); return ret); // 12. 将结果拷贝回 Host 侧打印 ret aclrtMemcpy(dqData.data(), qSize * sizeof(op::fp16_t), dqAddr, qSize * sizeof(op::fp16_t), ACL_MEMCPY_DEVICE_TO_HOST); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(copy result from device to host failed.\n); return ret); LOG_PRINT(Execution Success! Output results (first 10 elements of dQ):\n); for (uint64_t i 0; i 10 i dqData.size(); i) { LOG_PRINT( dQ index %lu: %f\n, i, static_castfloat(dqData[i])); } // 13. 释放所有资源 LOG_PRINT(Cleaning up resources...\n); if (workspaceAddr) aclrtFree(workspaceAddr); aclrtFree(qAddr); aclrtFree(kAddr); aclrtFree(vAddr); aclrtFree(doutAddr); aclrtFree(outAddr); aclrtFree(lseAddr); aclrtFree(maskAddr); aclrtFree(dqAddr); aclrtFree(dkAddr); aclrtFree(dvAddr); aclDestroyTensor(qTensor); aclDestroyTensor(kTensor); aclDestroyTensor(vTensor); aclDestroyTensor(doutTensor); aclDestroyTensor(outTensor); aclDestroyTensor(lseTensor); aclDestroyTensor(maskTensor); aclDestroyTensor(dqTensor); aclDestroyTensor(dkTensor); aclDestroyTensor(dvTensor); aclDestroyIntArray(blockShapeArr); aclDestroyIntArray(qSeqLenArr); aclDestroyIntArray(kvSeqLenArr); aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); LOG_PRINT(BlockSparseAttentionGrad Test completed successfully!\n); return 0; }【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考