大模型中的位置编码Rope + Llama3 源码示例 详解

大模型中的位置编码Rope + Llama3 源码示例 详解

今天由于idea的需要接触到了position embedding的细节知识,顺便做了一个梳理

首先,提供一些链接去学习rope的基础知识

位置编码基础:https://zhuanlan.zhihu.com/p/454482273
苏神原文:https://arxiv.org/abs/2104.09864
Rope知乎上的博客:https://zhuanlan.zhihu.com/p/642884818
https://zhuanlan.zhihu.com/p/647109286

了解原理后,我们直接看公式

在这里插入图片描述
我们的目标是找到functions f q ( . ) f_q(.) fq(.) f k ( . ) f_k(.) fk(.),使其满足这个等式
这个等式是什么意思呢?
x m x_m xm表示第m个token的feature, f q ( x m , m ) f_q(x_m, m) fq(xm,m)表示融合m位置编码后的 x m x_m xm
x n x_n xn表示第n个token的feature, f k ( x n , n ) f_k(x_n, n) fk(xn,n)表示融合m位置编码后的 x n x_n xn
< f q ( x m , m ) , f k ( x n , n ) > <f_q(x_m, m), f_k(x_n, n)> <fq(xm,m),fk(xn,n)>表示inner product,内积
g表示一种方法,输入是 x m x_m xm x n x_n xn,以及相对位置 m − n m - n mn

我们直接看作者给出的solution(详细推导见上面博客链接)
这是一个2D的情形,就是token embedding只有2维时:
在这里插入图片描述
下面是具体的展开
在这里插入图片描述我们可以看到,其实只需要在 q q q前面乘上一个旋转角矩阵,就可以完成这个目标
那么上升到多维的场景,可以表示为这样:
在这里插入图片描述
由于 R R R太过稀疏,为了提高计算效率,作者给出了等价计算方法(该方法被广泛用在LLM中):
在这里插入图片描述
所以这就是我们需要计算的公式,至于其中具体的性质、意义及推导,前面提供的链接中已经讲得非常明白了,这里不再阐述
在注意力机制中,我们仅对 q q q k k k套用这个公式计算位置编码

下面看一下llama3中实现这个公式的代码

总共分为两步,分别是计算RotaryEmbedding和将RotaryEmbedding apply 到 q q q k k k向量

计算RotaryEmbedding

首先,初始化上边公式里面的 θ \theta θ,在下面这个class的init方法里面。这个借鉴了最早Transformer中的绝对位置编码,我在下面也贴一下这个计算theta的公式
在这里插入图片描述
在这里插入图片描述
然后,算 m m m θ \theta θ的乘积,并计算得到 c o s ( m ∗ θ ) cos(m*\theta) cos(mθ) s i n ( m ∗ θ ) sin(m*\theta) sin(mθ)
在这里插入图片描述

将RotaryEmbedding apply 到 q q q k k k向量

方法很简单,就是通过下面这两行代码实现的上面公式

这里的rotate_half代码如下
在这里插入图片描述

那最后,我们可以得到了位置编码后的q和k,并按照常方法计算attention
在这里插入图片描述

### 2D 绳子位置编码方法解释 为了处理二维数据的空间关系,在自然语言处理和其他领域中引入了多种位置编码方案。对于特定应用如图像或网格上的序列建模,采用基于正弦波函数的位置编码是一种常见做法[^1]。 这种编码方式通过不同频率的正弦和余弦函数来表示各个维度上相对距离的信息。具体来说: - 对于给定的位置 `(i, j)` 和特征维度 `d`, - 使用两个不同的频率因子分别作用于行索引 `i` 和列索引 `j` 上; - 这样可以确保即使是在较大的输入尺寸下也能保持良好的泛化能力。 以下是 Python 实现的一个简单例子,展示了如何创建适用于二维矩阵的位置编码向量: ```python import numpy as np def get_2d_sinusoid_encoding_table(n_position_rows, n_position_cols, d_hid): &#39;&#39;&#39; Sinusoid position encoding table for 2D inputs &#39;&#39;&#39; def cal_angle(position, hid_idx): return position / np.power(10000, 2 * (hid_idx // 2) / d_hid) def get_posi_angle_vec(position_row_col_tuple): row, col = position_row_col_tuple return [cal_angle(row, hid_j) for hid_j in range(d_hid)] + \ [cal_angle(col, hid_j) for hid_j in range(d_hid)] sinusoid_table = np.array([get_posi_angle_vec((pos_i, pos_j)) for pos_i in range(n_position_rows) for pos_j in range(n_position_cols)]) sinusoid_table[:, 0::2] = np.sin(sinusoid_table[:, 0::2]) # dim 2i sinusoid_table[:, 1::2] = np.cos(sinusoid_table[:, 1::2]) # dim 2i+1 return sinusoid_table.reshape(n_position_rows, n_position_cols, -1).astype(&#39;float32&#39;) # Example usage: n_position_rows = 8 n_position_cols = 8 d_hid = 64 encoding_table = get_2d_sinusoid_encoding_table(n_position_rows, n_position_cols, d_hid) print(encoding_table.shape) # Output should be (8, 8, 128), since each spatial dimension gets its own set of encodings. ``` 此代码片段定义了一个名为 `get_2d_sinusoid_encoding_table()` 的函数,它接受三个参数:行列数 (`n_position_rows`, `n_position_cols`) 及隐藏层大小(`d_hid`) 。该函数返回一个形状为 `[rows, cols, 2*d_hid]` 的张量作为最终的结果,其中每一项都对应着相应坐标的绝对位置嵌入向量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值