大模型系列——旋转位置编码和长度外推

绝对位置编码

 旋转位置编码

 

 

论文中有个很直观的图片展示了旋转变换的过程: 

对于“我”对应的d维向量, 拆分成d/2组以后,每组对应一个角度\theta,若\theta1对应的向量为(x1,x2),应用旋转位置编码,相当于这个分量旋转了m\theta1角度。

结合transformer4.42.4版本,qwen2源码分析如下:

1、定义和缓存cos、sin

class Qwen2RotaryEmbedding(nn.Module):
    def __init__(self, dim, max_position_embeddings=2048, base=10000, device=None):
        super().__init__()

        self.dim = dim
        self.max_position_embeddings = max_position_embeddings
        self.base = base
        inv_freq = 1.0 / (self.base ** (torch.arange(0, self.dim, 2, dtype=torch.int64).float().to(device) / self.dim))
        self.register_buffer("inv_freq", inv_freq, persistent=False)

        # Build here to make `torch.jit.trace` work.
        self._set_cos_sin_cache(
            seq_len=max_position_embeddings, device=self.inv_freq.device, dtype=torch.get_default_dtype()
        )

    def _set_cos_sin_cache(self, seq_len, device, dtype):
        self.max_seq_len_cached = seq_len
        t = torch.arange(self.max_seq_len_cached, device=device, dtype=torch.int64).type_as(self.inv_freq)

        freqs = torch.outer(t, self.inv_freq)
        # Different from paper, but it uses a different permutation in order to obtain the same calculation
        emb = torch.cat((freqs, freqs), dim=-1)
        self.register_buffer("cos_cached", emb.cos().to(dtype), persistent=False)
        self.register_buffer("sin_cached", emb.sin().to(dtype), persistent=False)

    def forward(self, x, seq_len=None):
        # x: [bs, num_attention_heads, seq_len, head_size]
        if seq_len > self.max_seq_len_cached:
            self._set_cos_sin_cache(seq_len=seq_len, device=x.device, dtype=x.dtype)

        return (
            self.cos_cached[:seq_len].to(dtype=x.dtype),
            self.sin_cached[:seq_len].to(dtype=x.dtype),
        )

在上面这段代码中,inv_freq对应的是各分量的旋转角度,长度为d/2

        t = torch.arange(self.max_seq_len_cached, device=device, dtype=torch.int64).type_as(self.inv_freq)

        freqs = torch.outer(t, self.inv_freq)

这里的t为提前把所有可能的位置id 都先取好,并与对应的角度相乘,对应公式中的m\theta,计算出来的矩阵freqs维度为(self.max_seq_len,d/2)。这里outer函数计算如下:

torch.outer

import torch
t = torch.tensor([1,2,3])
inv_freq = torch.tensor([0.1,0.2,0.3])
f = torch.outer(t, inv_freq)
tensor([[0.1000, 0.2000, 0.3000],
        [0.2000, 0.4000, 0.6000],
        [0.3000, 0.6000, 0.9000]])

emb = torch.cat((f,f), dim=-1)
emb
tensor([[0.1000, 0.2000, 0.3000, 0.1000, 0.2000, 0.3000],
        [0.2000, 0.4000, 0.6000, 0.200
### Transformer 架构中的位置编码 #### 绝对位置编码 在Transformer架构中,为了使模型能够理解序列数据中各个token之间的顺序关系,引入了位置编码机制。由于自注意力机制本身不具备捕捉序列顺序的能力,因此需要额外加入位置信息来弥补这一缺陷[^3]。 对于绝对位置编码而言,在原始的Transformer论文里采用了一种基于正弦余弦函数的方法构建固定的相对位置表示。具体来说,该方法利用不同频率的周期信号组合而成的位置向量赋予每个词其所在的位置特征: ```python import math import torch def get_position_angle_vec(position, dim): return [position / np.power(10000, 2 * (hid_j // 2) / dim) for hid_j in range(dim)] def generate_absolute_pos_encoding(max_seq_length, d_model): positional_encoding = np.array([ get_position_angle_vec(pos, d_model) for pos in range(max_seq_length) ]) # Apply sine to even indices and cosine to odd indices of the position encoding vector. positional_encoding[:, 0::2] = np.sin(positional_encoding[:, 0::2]) # dimension 2i positional_encoding[:, 1::2] = np.cos(positional_encoding[:, 1::2]) # dimension 2i+1 pe_tensor = torch.FloatTensor([positional_encoding]) return pe_tensor ``` 这种设计使得即使当句子长度超出训练期间遇到的最大长度时,仍能合理地推测未知位置上的编码值[^1]。 #### 相对位置编码及其优势 不同于上述静态分配方式,相对位置编码则关注于两个单词之间距离而非确切坐标。这种方式允许模型更好地处理变长输入并具备更强泛化能力——即所谓的“外推性”。例如DeBERTa提出的解耦注意机制就采用了动态计算查询键间相对位移来进行增强;RoFormer进一步改进提出了旋转式位置嵌入方案,这些都属于相对位置编码范畴内的重要进展[^2]。 综上所述,无论是哪种形式的位置编码都是为了让神经网络学会识别文本内在结构规律而精心设计的关键组件之一。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值