旋转位置编码(Rotary Position Embedding,RoPE),以旋转矩阵的方式在q、k中注入位置信息,使得attention计算时能感受到token的位置关系,在各大模型中,RoPE被广泛应用。RoPE以绝对位置编码的方式实现了相对位置编码,能有效保持位置信息相对关系,并且可以通过编码外推的方式支持超过训练长度的位置编码。
对于二维情况
假设空间是偶数维的,把原始空间切分一个个正交的二维子空间,在上面做独立的不同角度的旋转,可以扩展到高维空间。
1 2 3 4 5 |
struct RopeParam { int32_t rotaryCoeff = 4; int32_t cosFormat = 0; uint8_t rsv[8] = {0}; }; |
成员名称 |
类型 |
默认值 |
描述 |
---|---|---|---|
rotaryCoeff |
int32_t |
4 |
rope,旋转系数,对半旋转是2,支持配置2、4、head_size / 2、head_size。 |
cosFormat |
int32_t |
0 |
训练用参数,支持配置0或1。 |
rsv[8] |
uint8_t |
{0} |
预留参数。 |
rotaryCoeff参数选择与原始计算公式的对应关系如下:
其中m为token的位置,d为query或key的维度。
以此类推,rotaryCoeff = 4对应于1/4,表示query和key分成前后两半,每一半按rotaryCoeff = 2的情况处理。
参数 |
维度 |
数据类型 |
格式 |
描述 |
---|---|---|---|---|
query |
[ntokens, hiddenSizeQ] |
float16/bf16 |
ND |
当前step多个token的query。 |
key |
[ntokens, hiddenSizeK] |
float16/bf16 |
ND |
当前step多个token的key。 |
cos |
[ntokens, head_size] / [ntokens, head_size / 2] |
float16/float/bf16 |
ND |
|
sin |
[ntokens, head_size] / [ntokens, head_size/ 2] |
float16/float/bf16 |
ND |
|
seqlen |
[batch] |
uint32/int32 |
ND |
- |
参数 |
维度 |
数据类型 |
格式 |
描述 |
---|---|---|---|---|
ropeQ |
[ntokens, hiddenSizeQ] |
float16/bf16 |
ND |
旋转后的query。 |
ropeK |
[ntokens, hiddenSizeK] |
float16/bf16 |
ND |
旋转后的key。 |
[batch, seqlen, headNum, head_size];对应的ropeQ、ropeK也是四维,维度输入输出对应。
前置条件和编译命令请参见算子调用示例。
场景:基础场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
#include <iostream> #include <vector> #include <numeric> #include "acl/acl.h" #include "atb/operation.h" #include "atb/types.h" #include "atb/atb_infer.h" #include "demo_util.h" const uint32_t BATCH_SIZE = 1; // 批处理大小 const uint32_t NTOKENS = 4; // TOKEN大小 const uint32_t HIDDENSIZEQ = 16; // Q 隐藏层大小 const uint32_t HIDDENSIZEK = 16; // K 隐藏层大小 const uint32_t HEAD_SIZE = 8; // 头大小 /** * @brief 创建一个ROPE的Operation,并设置参数 * @return atb::Operation * 返回一个Operation指针 */ atb::Operation *PrepareOperation() { atb::infer::RopeParam opParam; atb::Operation *ropeOp = nullptr; CHECK_STATUS(atb::CreateOperation(opParam, &ropeOp)); return ropeOp; } /** * @brief 准备atb::VariantPack中的所有输入tensor * @param contextPtr context指针 * @param stream stream * @return atb::SVector<atb::Tensor> atb::VariantPack中的输入tensor * @note 需要传入所有host侧tensor */ atb::SVector<atb::Tensor> PrepareInTensor(atb::Context *contextPtr, aclrtStream stream) { std::vector<float> qData(NTOKENS * HIDDENSIZEQ, 1.0); atb::Tensor tensorQ = CreateTensorFromVector( contextPtr, stream, qData, ACL_FLOAT16, aclFormat::ACL_FORMAT_ND, {NTOKENS, HIDDENSIZEQ}); std::vector<float> kData(NTOKENS * HIDDENSIZEK, 1.0); atb::Tensor tensorK = CreateTensorFromVector( contextPtr, stream, kData, ACL_FLOAT16, aclFormat::ACL_FORMAT_ND, {NTOKENS, HIDDENSIZEK}); std::vector<float> cos(NTOKENS * HEAD_SIZE, 1.0); atb::Tensor tensorCos = CreateTensorFromVector(contextPtr, stream, cos, ACL_FLOAT16, aclFormat::ACL_FORMAT_ND, {NTOKENS, HEAD_SIZE}); std::vector<float> sin(NTOKENS * HEAD_SIZE, 1.0); atb::Tensor tensorSin = CreateTensorFromVector(contextPtr, stream, sin, ACL_FLOAT16, aclFormat::ACL_FORMAT_ND, {NTOKENS, HEAD_SIZE}); std::vector<int32_t> seqLenHost(BATCH_SIZE, 4); atb::Tensor tensorSeqLen = CreateTensor(ACL_INT32, aclFormat::ACL_FORMAT_ND, {BATCH_SIZE}); tensorSeqLen.hostData = seqLenHost.data(); atb::SVector<atb::Tensor> inTensors = {tensorQ, tensorK, tensorCos, tensorSin, tensorSeqLen}; return inTensors; } int main(int argc, char **argv) { // 设置卡号、创建context、设置stream CHECK_STATUS(aclInit(nullptr)); int32_t deviceId = 0; CHECK_STATUS(aclrtSetDevice(deviceId)); atb::Context *context = nullptr; CHECK_STATUS(atb::CreateContext(&context)); void *stream = nullptr; CHECK_STATUS(aclrtCreateStream(&stream)); context->SetExecuteStream(stream); // 算子实例 atb::Operation *ropeOp = PrepareOperation(); // 准备输入张量 atb::VariantPack variantPack; variantPack.inTensors = PrepareInTensor(context, stream); // 准备输入张量ropeQ和ropeK atb::Tensor ropeQ = CreateTensor(ACL_FLOAT16, aclFormat::ACL_FORMAT_ND, {NTOKENS, HIDDENSIZEQ}); atb::Tensor ropeK = CreateTensor(ACL_FLOAT16, aclFormat::ACL_FORMAT_ND, {NTOKENS, HIDDENSIZEK}); variantPack.outTensors = {ropeQ, ropeK}; uint64_t workspaceSize = 0; CHECK_STATUS(ropeOp->Setup(variantPack, workspaceSize, context)); uint8_t *workspacePtr = nullptr; if (workspaceSize > 0) { CHECK_STATUS(aclrtMalloc((void **)(&workspacePtr), workspaceSize, ACL_MEM_MALLOC_HUGE_FIRST)); } // 算子执行 ropeOp->Execute(variantPack, workspacePtr, workspaceSize, context); CHECK_STATUS(aclrtSynchronizeStream(stream)); for (atb::Tensor &inTensor : variantPack.inTensors) { CHECK_STATUS(aclrtFree(inTensor.deviceData)); } if (workspaceSize > 0) { CHECK_STATUS(aclrtFree(workspacePtr)); } // 资源释放 CHECK_STATUS(atb::DestroyOperation(ropeOp)); CHECK_STATUS(aclrtDestroyStream(stream)); CHECK_STATUS(atb::DestroyContext(context)); CHECK_STATUS(aclFinalize()); std::cout << "Rope demo success!" << std::endl; return 0; } |