CANN/ops-transformer FlashAttention梯度V2
aclnnFlashAttentionUnpaddingScoreGradV2【免费下载链接】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 训练系列产品×功能说明接口功能训练场景下计算注意力的反向输出即aclnnFlashAttentionVarLenScoreV2的反向计算。该接口相较于aclnnFlashAttentionUnpaddingScoreGrad接口新增pseType参数pseType1时与aclnnFlashAttentionUnpaddingScoreGrad实现相同。pseType其他取值时需要先mul再add。计算公式已知注意力的正向计算公式pseType1时与aclnnFlashAttentionUnpaddingScoreGrad计算公式相同pseType其他取值公式如下$$ YDropout(Softmax(Mask(\frac{QK^T}{\sqrt{d}}pse),atten_mask),keep_prob)V $$为方便表达以变量$S$和$P$表示计算公式$$ SMask(\frac{QK^T}{\sqrt{d}}pse),atten_mask $$$$ PDropout(Softmax(S),keep_prob) $$$$ YPV $$则注意力的反向计算公式为$$ dVP^TdY $$$$ dQ\frac{((dS)*K)}{\sqrt{d}} $$$$ dK\frac{((dS)^T*Q)}{\sqrt{d}} $$函数原型每个算子分为两段式接口必须先调用“aclnnFlashAttentionUnpaddingScoreGradV2GetWorkspaceSize”接口获取计算所需workspace大小以及包含了算子计算流程的执行器再调用“aclnnFlashAttentionUnpaddingScoreGradV2”接口执行计算。aclnnStatus aclnnFlashAttentionUnpaddingScoreGradV2GetWorkspaceSize( const aclTensor *query, const aclTensor *keyIn, const aclTensor *value, const aclTensor *dy, const aclTensor *pseShiftOptional, const aclTensor *dropMaskOptional, const aclTensor *paddingMaskOptional, const aclTensor *attenMaskOptional, const aclTensor *softmaxMaxOptional, const aclTensor *softmaxSumOptional, const aclTensor *softmaxInOptional, const aclTensor *attentionInOptional, const aclIntArray *prefixOptional, const aclIntArray *actualSeqQLenOptional, const aclIntArray *actualSeqKvLenOptional, const aclIntArray *qStartIdxOptional, const aclIntArray *kvStartIdxOptional, double scaleValue, double keepProb, int64_t preTokens, int64_t nextTokens, int64_t headNum, char *inputLayout, int64_t innerPrecise, int64_t sparseMode, int64_t pseType, const aclTensor *dqOut, const aclTensor *dkOut, const aclTensor *dvOut, const aclTensor *dpseOut, uint64_t *workspaceSize, aclOpExecutor **executor)aclnnStatus aclnnFlashAttentionUnpaddingScoreGradV2( void *workspace, uint64_t workspaceSize, aclOpExecutor *executor, const aclrtStream stream)aclnnFlashAttentionUnpaddingScoreGradV2GetWorkspaceSize参数说明参数名输入/输出描述使用说明数据类型数据格式维度(shape)非连续Tensorquery输入公式中的Q。数据类型与keyIn/value一致。FLOAT16、BFLOAT16、FLOAT32ND[TND]√keyIn输入公式中的K。数据类型与query/value一致。FLOAT16、BFLOAT16、FLOAT32ND[TND]√value输入公式中的V。数据类型与query/keyIn一致。FLOAT16、BFLOAT16、FLOAT32ND[TND]√dy输入公式中的dY。-FLOAT16、BFLOAT16、FLOAT32ND[TND]√pseShiftOptional可选输入公式中的pse。数据类型与query的数据类型一致该参数需要与pseType配套使用。FLOAT16、BFLOAT16、FLOAT32ND[B,N,1024,Skv]、[1,N,1024,Skv]、[B,N]、[N]√dropMaskOptional可选输入Dropout掩码。-UINT8ND0、1√paddingMaskOptional可选输入预留参数暂未使用。调用时需传空。----qStartIdxOptional可选输入代表外切场景当前分块的query的sequence在全局中的起始索引。-INT64ND0、1-kvStartIdxOptional可选输入代表外切场景当前分块的key和value的sequence在全局中的起始索引。-INT64ND0、1-attenMaskOptional可选输入公式中的atten_mask。取值为1代表该位不参与计算为0代表该位参与计算。BOOL、UINT8ND[B,N,Sq,Skv]、[B,1,Sq,Skv]、[1,1,Sq,Skv]、[Sq,Skv]√softmaxMaxOptional可选输入正向softmax的中间输出。-FLOATND[N,T,8]√softmaxSumOptional可选输入正向softmax的中间输出。-FLOATND[N,T,8]√softmaxInOptional可选输入正向softmax的中间输出。预留参数暂未使用。----attentionInOptional可选输入正向注意力输出。与query数据类型、shape一致。FLOAT16、BFLOAT16、FLOAT32ND[TND]√prefixOptional可选输入prefix稀疏场景每个Batch的N。-INT64ND0、1-actualSeqQLenOptional可选输入实际Query序列长度。-INT64ND1-actualSeqKvLenOptional可选输入实际Key/Value序列长度。-INT64ND1-scaleValue可选输入scale缩放系数。-DOUBLE---keepProb可选输入dropMaskOptional中1的比例。-DOUBLE---preTokens可选输入稀疏计算时滑窗左边界。-INT64---nextTokens可选输入稀疏计算时滑窗右边界。-INT64---headNum输入单卡head数量即Query的N轴长度。-INT64---inputLayout输入输入Q/K/V数据排布。支持TND。String---innerPrecise可选输入内部计算精度控制。保留参数暂未使用。INT64---sparseMode可选输入稀疏模式。支持配置0~8。不支持5。INT64---pseType可选输入数据类型支持INT64。支持配置值为0、1、2、3。INT64---dqOut输出公式中的dQQuery梯度。-FLOAT16、BFLOAT16、FLOAT32ND[TND]√dkOut输出公式中的dKKey梯度。-FLOAT16、BFLOAT16、FLOAT32ND[TND]√dvOut输出公式中的dVValue梯度。-FLOAT16、BFLOAT16、FLOAT32ND[TND]√dpseOut输出d(pse)梯度。预留参数暂未使用。----workspaceSize输出返回需要Device侧申请的workspace大小。-----executor输出返回op执行器包含算子计算流程。-----返回值aclnnStatus返回状态码具体参见aclnn返回码。第一段接口完成入参校验出现以下场景时报错返回值错误码描述ACLNN_ERR_PARAM_NULLPTR161001传入参数是必选输入输出或者必选属性且是空指针。ACLNN_ERR_PARAM_INVALID161002query、keyIn、value、dy、pseShiftOptional、dropMaskOptional、paddingMaskOptional、attenMaskOptional、softmaxMaxOptional、softmaxSumOptional、softmaxInOptional、attentionInOptional、dqOut、dkOut、dvOut 的数据类型不在支持的范围内。query、keyIn、value、dy、pseShiftOptional、dropMaskOptional、paddingMaskOptional、attenMaskOptional、softmaxMaxOptional、softmaxSumOptional、softmaxInOptional、attentionInOptional、dqOut、dkOut、dvOut 的数据格式不在支持的范围内。aclnnFlashAttentionUnpaddingScoreGradV2参数说明参数名输入/输出描述workspace输入在Device侧申请的workspace内存地址。workspaceSize输入在Device侧申请的workspace大小由第一段接口aclnnFlashAttentionUnpaddingScoreGradV2GetWorkspaceSize获取。executor输入op执行器包含了算子计算流程。stream输入指定执行任务的Stream。返回值返回aclnnStatus状态码具体参见aclnn返回码。约束说明确定性计算aclnnFlashAttentionUnpaddingScoreGradV2默认非确定性实现支持通过aclrtCtxSetSysParamOpt开启确定性。该接口与PyTorch配合使用时需要保证CANN相关包与PyTorch相关包的版本匹配。输入query、key、value、dy的Bbatchsize必须相等inputLayout必须一致。输入query、key、value的DHead-Dim必须满足(qD kD kD vD)。输入query、key、value、pseShiftOptional的数据类型必须一致。输入key/value的shape除D外必须一致在query/key/value的D大小相同的情况下query/dy的shape必须一致。支持输入query的N和key/value的N不相等但必须成比例关系即Nq/Nkv必须是非0整数Nq取值范围1~256。关于数据shape的约束以inputLayout的TND为例其中T取值范围为1~1M。N取值范围为1~256。D取值范围为1~768。KeepProb: 取值范围为(0, 1]。query、key、value数据排布格式仅支持TNDT是B和S合轴紧密排列的数据每个batch的SeqLenQ和SeqLenKV其中BBatch表示输入样本批量大小、SSeq-Length表示输入样本序列长度、HHead-Size表示隐藏层的大小、NHead-Num表示多头数、DHead-Dim表示隐藏层最小的单元尺寸且满足DH/N。pseShiftOptional如果Sq大于1024且每个batch的Sq与Skv等长且是sparseMode为0、2、3的下三角掩码场景可使能alibi位置编码压缩此时只需要输入原始PSE最后1024行实现内存优化即alibi_compress ori_pse[:, :, -1024:, :]具体如下参数每个batch不相同时shape为BNHSkv(H1024)。每个batch相同时shape为1NHSkv(H1024)。TND场景下每个batch段内部仍按[N, Sq_i, Skv_i]生成但存储与传参时统一flatten。若第i个batch段的真实query长度为Sq_i、真实key/value长度为Skv_i则该段PSE元素个数为N * Sq_i * Skv_i整段PSE总长度pseTotalLen为sum_i(N * Sq_i * Skv_i)。如果pseType为2或3的时候数据类型需为FLOAT32, 对应shape支持范围是[B,N]或[N]。如果不使能该参数pseShiftOptional需要传入nullptrpseType需要传入1。pseType 各个取值含义pseType含义备注0外部传入pse 先mul再add-1外部传入pse 先add再mul跟FlashAttentionUnpaddingScoreGrad实现一致。2内部生成pse 先mul再add-3内部生成pse 先mul再add再sqrt-sparseMode的约束如下:当所有的attenMaskOptional的shape小于2048且相同的时候建议使用default模式来减少内存使用量配置为1、2、3时用户配置的preTokens、nextTokens不会生效配置为0、4时须保证attenMaskOptional与preTokens、nextTokens的范围一致。用户不特意指定时建议传入0。sparse不同模式的详细说明请参见sparse模式说明。配置为7时不支持可选输入pseShiftOptional。配置为8时当每个sequence的q、kv等长时支持可选输入pseShiftOptional针对全局做pse生成。支持q方向进行外切需要外切前每个sequence的q、kv等长外切后传入的actualSeqQLenOptional[0] - actualSeqKvLenOptional[0] qStartIdxOptional - kvStartIdxOptional 0本功能属实验性功能。部分场景下如果计算量过大可能会导致算子执行超时aicore error类型报错errorStr为timeout or trap error此时建议做轴切分处理注这里的计算量会受B、S、N、D等参数的影响值越大计算量越大。prefixOptional稀疏计算仅支持压缩场景sparseMode6当Sq Skv时prefix的N值取值范围[0, Skv]当Sq Skv时prefix的N值取值范围[Skv-Sq, Skv]。actualSeqQLenOptional输入支持某个Batch上的S长度为0此时不支持可选输入pseShiftOptional。actualSeqQLenOptional的长度取值范围为1~2K。当存在prefixOptional输入的时候其长度最大支持1K。关于softmaxMax与softmaxSum参数的约束输入格式固定为[B, N, S, 8]TND的输入格式除外此时为[T, N, 8]注TB*S。headNum的取值必须和传入的Query中的N值保持一致。调用示例调用示例代码如下仅供参考具体编译和执行过程请参考编译与运行样例。#include iostream #include vector #include acl/acl.h #include aclnnop/aclnn_flash_attention_score_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::vectorint64_t shape) { int64_t shapeSize 1; for (auto i : shape) { shapeSize * i; } return shapeSize; } void PrintOutResult(std::vectorint64_t shape, void** deviceAddr) { auto size GetShapeSize(shape); std::vectorfloat resultData(size, 0); auto ret aclrtMemcpy(resultData.data(), resultData.size() * sizeof(resultData[0]), *deviceAddr, 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); for (int64_t i 0; i size; i) { LOG_PRINT(mean result[%ld] is: %f\n, i, resultData[i]); } } 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_RET(ret ACL_SUCCESS, LOG_PRINT(Init acl failed. ERROR: %d\n, ret); return ret); // 2. 构造输入与输出需要根据API的接口自定义构造 std::vectorint64_t qShape {256, 1, 128}; std::vectorint64_t kShape {256, 1, 128}; std::vectorint64_t vShape {256, 1, 128}; std::vectorint64_t dxShape {256, 1, 128}; std::vectorint64_t attenmaskShape {256, 256}; std::vectorint64_t softmaxMaxShape {256, 1, 8}; std::vectorint64_t softmaxSumShape {256, 1, 8}; std::vectorint64_t attentionInShape {256, 1, 128}; std::vectorint64_t dqShape {256, 1, 128}; std::vectorint64_t dkShape {256, 1, 128}; std::vectorint64_t dvShape {256, 1, 128}; void* qDeviceAddr nullptr; void* kDeviceAddr nullptr; void* vDeviceAddr nullptr; void* dxDeviceAddr nullptr; void* attenmaskDeviceAddr nullptr; void* softmaxMaxDeviceAddr nullptr; void* softmaxSumDeviceAddr nullptr; void* attentionInDeviceAddr nullptr; void* dqDeviceAddr nullptr; void* dkDeviceAddr nullptr; void* dvDeviceAddr nullptr; aclTensor* q nullptr; aclTensor* k nullptr; aclTensor* v nullptr; aclTensor* dx nullptr; aclTensor* pse nullptr; aclTensor* dropMask nullptr; aclTensor* padding nullptr; aclTensor* attenmask nullptr; aclTensor* softmaxMax nullptr; aclTensor* softmaxSum nullptr; aclTensor* softmaxIn nullptr; aclTensor* attentionIn nullptr; aclTensor* dq nullptr; aclTensor* dk nullptr; aclTensor* dv nullptr; aclTensor* dpse nullptr; std::vectorfloat qHostData(32768, 1); std::vectorfloat kHostData(32768, 1); std::vectorfloat vHostData(32768, 1); std::vectorfloat dxHostData(32768, 1); std::vectoruint8_t attenmaskHostData(65536, 0); std::vectorfloat softmaxMaxHostData(2048, 3.0); std::vectorfloat softmaxSumHostData(2048, 3.0); std::vectorfloat attentionInHostData(32768, 1); std::vectorfloat dqHostData(32768, 0); std::vectorfloat dkHostData(32768, 0); std::vectorfloat dvHostData(32768, 0); ret CreateAclTensor(qHostData, qShape, qDeviceAddr, aclDataType::ACL_FLOAT, q); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(kHostData, kShape, kDeviceAddr, aclDataType::ACL_FLOAT, k); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(vHostData, vShape, vDeviceAddr, aclDataType::ACL_FLOAT, v); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(dxHostData, dxShape, dxDeviceAddr, aclDataType::ACL_FLOAT, dx); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(attenmaskHostData, attenmaskShape, attenmaskDeviceAddr, aclDataType::ACL_UINT8, attenmask); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(softmaxMaxHostData, softmaxMaxShape, softmaxMaxDeviceAddr, aclDataType::ACL_FLOAT, softmaxMax); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(softmaxSumHostData, softmaxSumShape, softmaxSumDeviceAddr, aclDataType::ACL_FLOAT, softmaxSum); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(attentionInHostData, attentionInShape, attentionInDeviceAddr, aclDataType::ACL_FLOAT, attentionIn); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(dqHostData, dqShape, dqDeviceAddr, aclDataType::ACL_FLOAT, dq); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(dkHostData, dkShape, dkDeviceAddr, aclDataType::ACL_FLOAT, dk); CHECK_RET(ret ACL_SUCCESS, return ret); ret CreateAclTensor(dvHostData, dvShape, dvDeviceAddr, aclDataType::ACL_FLOAT, dv); CHECK_RET(ret ACL_SUCCESS, return ret); std::vectorint64_t prefixOp {0}; aclIntArray* prefix aclCreateIntArray(prefixOp.data(), 1); std::vectorint64_t acSeqQLenOp {256}; std::vectorint64_t acSeqKvLenOp {256}; aclIntArray* acSeqQLen aclCreateIntArray(acSeqQLenOp.data(), acSeqQLenOp.size()); aclIntArray* acSeqKvLen aclCreateIntArray(acSeqKvLenOp.data(), acSeqKvLenOp.size()); std::vectorint64_t qStartIdxOp {0}; std::vectorint64_t kvStartIdxOp {0}; aclIntArray *qStartIdx aclCreateIntArray(qStartIdxOp.data(), 1); aclIntArray *kvStartIdx aclCreateIntArray(kvStartIdxOp.data(), 1); double scaleValue 0.088388; double keepProb 1; int64_t preTokens 65536; int64_t nextTokens 65536; int64_t headNum 1; int64_t innerPrecise 0; int64_t sparseMode 0; int64_t pseType 1; char layOut[5] {T, N, D, 0}; // 3. 调用CANN算子库API需要修改为具体的Api名称 uint64_t workspaceSize 0; aclOpExecutor* executor; // 调用aclnnFlashAttentionUnpaddingScoreGradV2第一段接口 ret aclnnFlashAttentionUnpaddingScoreGradV2GetWorkspaceSize(q, k, v, dx, pse, dropMask, padding, attenmask, softmaxMax, softmaxSum, softmaxIn, attentionIn, prefix, acSeqQLen, acSeqKvLen, qStartIdx, kvStartIdx, scaleValue, keepProb, preTokens, nextTokens, headNum, layOut, innerPrecise, sparseMode, pseType, dq, dk, dv, dpse, workspaceSize, executor); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnFlashAttentionUnpaddingScoreGradV2GetWorkspaceSize 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); } // 调用aclnnFlashAttentionUnpaddingScoreGradV2第二段接口 ret aclnnFlashAttentionUnpaddingScoreGradV2(workspaceAddr, workspaceSize, executor, stream); CHECK_RET(ret ACL_SUCCESS, LOG_PRINT(aclnnFlashAttentionUnpaddingScoreGradV2 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的接口定义修改 PrintOutResult(dqShape, dqDeviceAddr); PrintOutResult(dkShape, dkDeviceAddr); PrintOutResult(dvShape, dvDeviceAddr); // 6. 释放aclTensor和aclScalar需要根据具体API的接口定义修改 aclDestroyTensor(q); aclDestroyTensor(k); aclDestroyTensor(v); aclDestroyTensor(dx); aclDestroyTensor(attenmask); aclDestroyTensor(softmaxMax); aclDestroyTensor(softmaxSum); aclDestroyTensor(attentionIn); aclDestroyTensor(dq); aclDestroyTensor(dk); aclDestroyTensor(dv); // 7. 释放device资源 aclrtFree(qDeviceAddr); aclrtFree(kDeviceAddr); aclrtFree(vDeviceAddr); aclrtFree(dxDeviceAddr); aclrtFree(attenmaskDeviceAddr); aclrtFree(softmaxMaxDeviceAddr); aclrtFree(softmaxSumDeviceAddr); aclrtFree(attentionInDeviceAddr); aclrtFree(dqDeviceAddr); aclrtFree(dkDeviceAddr); aclrtFree(dvDeviceAddr); if (workspaceSize 0) { aclrtFree(workspaceAddr); } aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); return 0; }【免费下载链接】ops-transformer本项目是CANN提供的transformer类大模型算子库实现网络在NPU上加速计算。项目地址: https://gitcode.com/cann/ops-transformer创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考