【NLP高频面题 - LLM架构篇】什么是旋转位置编码(RoPE)?
重要性:★★★ 💯
NLP Github 项目:
NLP 项目实践:fasterai/nlp-project-practice
介绍:该仓库围绕着 NLP 任务模型的设计、训练、优化、部署和应用,分享大模型算法工程师的日常工作和实战经验
AI 藏经阁:https://gitee.com/fasterai/ai-e-book
介绍:该仓库主要分享了数百本 AI 领域电子书
AI 算法面经:fasterai/nlp-interview-handbook#面经
介绍:该仓库一网打尽互联网大厂NLP算法面经,算法求职必备神器
NLP 剑指Offer:https://gitee.com/fasterai/nlp-interview-handbook
介绍:该仓库汇总了 NLP 算法工程师高频面题
旋转位置编码(Rotary Position Embedding, RoPE). RoPE 巧妙地使用了基于绝对位置信息的旋转矩阵来表示注意力中的相对位置信息。
RoPE 根据位置信息为序列中每个词元所对应的设置了独有的旋转矩阵,并和对应的查询和键进行相乘进行融合。
位置索引为 𝑡 对应的旋转矩阵定义如下所示:
利用旋转矩阵中三角函数的特性,位置索引为 𝑖 的旋转矩阵和位置索引为 𝑗 的旋转矩阵的转置的乘积等同于位置索引为它们相对距离𝑖−𝑗 的旋转矩阵,通过这种方式,键和查询之间的注意力分数能够有效融入相对位置信息,因为Q、K的注意力计算中只与𝑖、𝑗相对位置有关。
注意力计算公式如下:
大模型在位置编码上,使用旋转位置嵌入(Rotary Positional Embeddings,RoPE)代替原有的绝对位置编码。
旋转位置编码RoPE是一种固定式的绝对位置编码策略,但是它的绝对位置编码配合Transformer的Attention内积注意力机制能达到相对位置编码的效果。
RoPE的本质是对两个token形成的Query和Key向量做一个变换,使得变换后的Query和Key带有位置信息,进一步使得Attention的内积操作不需要做任何更改就能自动感知到相对位置信息。
换句话说,RoPR的出发点和策略用的相对位置编码思想,但是实现方式的确用的是绝对位置编码。
论文中有个很直观的图片展示了旋转变换的过程:
我们可以从旋转矩阵的角度理解RoPE:
RoPE代码实现:
class LlamaRotaryEmbedding(torch.nn.Module):
def __init__(self, dim, max_position_embeddings=2048, base=10000, device=None):
super().__init__()
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float().to(device) / dim))
self.register_buffer("inv_freq", inv_freq)
# Build here to make `torch.jit.trace` work.
self.max_seq_len_cached = max_position_embeddings
t = torch.arange(self.max_seq_len_cached, device=self.inv_freq.device,
dtype=self.inv_freq.dtype)
freqs = torch.einsum("i,j->ij", 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)
dtype = torch.get_default_dtype()
self.register_buffer("cos_cached", emb.cos()[None, None, :, :].to(dtype), persistent=False)
self.register_buffer("sin_cached", emb.sin()[None, None, :, :].to(dtype), persistent=False)
def forward(self, x, seq_len=None):
# x: [bs, num_attention_heads, seq_len, head_size]
# This `if` block is unlikely to be run after we build sin/cos in `__init__`.
# Keep the logic here just in case.
if seq_len > self.max_seq_len_cached:
self.max_seq_len_cached = seq_len
t = torch.arange(self.max_seq_len_cached, device=x.device, dtype=self.inv_freq.dtype)
freqs = torch.einsum("i,j->ij", 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).to(x.device)
self.register_buffer("cos_cached", emb.cos()[None, None, :, :].to(x.dtype),
persistent=False)
self.register_buffer("sin_cached", emb.sin()[None, None, :, :].to(x.dtype),
persistent=False)
return (
self.cos_cached[:, :, :seq_len, ...].to(dtype=x.dtype),
# 生成旋转矩阵
def rotate_half(x):
"""Rotates half the hidden dims of the input."""
x1 = x[..., : x.shape[-1] // 2]
x2 = x[..., x.shape[-1] // 2:]
return torch.cat((-x2, x1), dim=-1)
# 旋转位置编码计算
def apply_rotary_pos_emb(q, k, cos, sin, position_ids):
# The first two dimensions of cos and sin are always 1, so we can `squeeze` them.
cos = cos.squeeze(1).squeeze(0) # [seq_len, dim]
sin = sin.squeeze(1).squeeze(0) # [seq_len, dim]
cos = cos[position_ids].unsqueeze(1) # [bs, 1, seq_len, dim]
sin = sin[position_ids].unsqueeze(1) # [bs, 1, seq_len, dim]
q_embed = (q * cos) + (rotate_half(q) * sin)
k_embed = (k * cos) + (rotate_half(k) * sin)
return q_embed, k_embed
class Attention(nn.Module):
def __init__(self, args: ModelArgs):
super().__init__()
self.wq = Linear(...)
self.wk = Linear(...)
self.wv = Linear(...)
self.freqs_cis = precompute_freqs_cis(dim, max_seq_len * 2)
def forward(self, x: torch.Tensor):
bsz, seqlen, _ = x.shape
xq, xk, xv = self.wq(x), self.wk(x), self.wv(x)
xq = xq.view(batch_size, seq_len, dim)
xk = xk.view(batch_size, seq_len, dim)
xv = xv.view(batch_size, seq_len, dim)
# attention 操作之前,Query、Key计算之后,应用旋转位置编码(不会引入噪声)
xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis)
# scores.shape = (bs, seqlen, seqlen)
scores = torch.matmul(xq, xk.transpose(1, 2)) / math.sqrt(dim)
scores = F.softmax(scores.float(), dim=-1)
output = torch.matmul(scores, xv) # (batch_size, seq_len, dim)
# ......
NLP 大模型高频面题汇总
NLP基础篇
【NLP 面试宝典 之 模型分类】 必须要会的高频面题
【NLP 面试宝典 之 神经网络】 必须要会的高频面题
【NLP 面试宝典 之 主动学习】 必须要会的高频面题
【NLP 面试宝典 之 超参数优化】 必须要会的高频面题
【NLP 面试宝典 之 正则化】 必须要会的高频面题
【NLP 面试宝典 之 过拟合】 必须要会的高频面题
【NLP 面试宝典 之 Dropout】 必须要会的高频面题
【NLP 面试宝典 之 EarlyStopping】 必须要会的高频面题
【NLP 面试宝典 之 标签平滑】 必须要会的高频面题
【NLP 面试宝典 之 Warm up 】 必须要会的高频面题
【NLP 面试宝典 之 置信学习】 必须要会的高频面题
【NLP 面试宝典 之 伪标签】 必须要会的高频面题
【NLP 面试宝典 之 类别不均衡问题】 必须要会的高频面题
【NLP 面试宝典 之 交叉验证】 必须要会的高频面题
【NLP 面试宝典 之 词嵌入】 必须要会的高频面题
【NLP 面试宝典 之 One-Hot】 必须要会的高频面题
......
BERT 模型面
LLMs 微调面
【NLP 面试宝典 之 LoRA微调】 必须要会的高频面题
【NLP 面试宝典 之 Prompt】 必须要会的高频面题
【NLP 面试宝典 之 提示学习微调】 必须要会的高频面题
【NLP 面试宝典 之 PEFT微调】 必须要会的高频面题
【NLP 面试宝典 之 Chain-of-Thought微调】 必须要会的高频面题
......
本文由mdnice多平台发布
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。