deepseek后训练算法:位置编码算法RoPE详解1

在前面章节中,我们了解了文本如何转换为词源,然后再将词源转换为向量。但我们还缺少一个重要信息,那就是每个词源在整句话里的位置,这对模型识别一句话具有非常重要的意义。一个直观感受就是,当两个词源相互

距离越远,他们在逻辑上的联系就越弱。另外相同一个词出现在不同位置时,它可能表达出不一样的含义,例如下面句子:

The first fox jump over the second fox.

上面句子中有两个相同单词fox,但显然第一个fox和第二个fox指向不同的对象。因此词源所在位置的信息会大大帮助模型理解一句话的含义。在本节,我们先拆解目前主流大模型例如deepseek, chatgpt等都广泛采用的位置

编码算法RoPE。

RoPE算法原理较为复杂,我们需要把它拆解后,步步为营的处理它。首先我们需要了解的一个问题是,给定二维平面上的一个点(x,y),将它围绕原点逆时针旋转角度θ得到一个新的点(x',y'),那么原来的点(x,y),新的点(x',y'),

以及旋转角度θ会有什么联系呢,我们先把这个情况绘制出来看看:

py 复制代码
import numpy as np
import matplotlib.pyplot as plt

def R(theta):
    return np.array([[np.cos(theta), -np.sin(theta)],
                     [np.sin(theta),  np.cos(theta)]])

P = np.array([3.0, 4.0])
theta = np.pi / 3
P_prime = R(theta) @ P

fig, ax = plt.subplots(figsize=(7, 7))
ax.set_xlim(-6, 6); ax.set_ylim(-6, 6)
ax.set_aspect('equal'); ax.grid(True, alpha=0.3)
ax.axhline(0, color='gray', linewidth=0.8)
ax.axvline(0, color='gray', linewidth=0.8)

ax.annotate("", xy=P, xytext=(0,0), arrowprops=dict(arrowstyle='->', color='blue', lw=2))
ax.text(P[0]+0.2, P[1]+0.2, f'P=({P[0]:.0f},{P[1]:.0f})', color='blue', fontsize=12)

ax.annotate("", xy=P_prime, xytext=(0,0), arrowprops=dict(arrowstyle='->', color='red', lw=2))
ax.text(P_prime[0]+0.2, P_prime[1]+0.2, f"P'=({P_prime[0]:.2f},{P_prime[1]:.2f})", color='red', fontsize=12)θ
Ø
t = np.linspace(0, theta, 50)
ax.plot(2*np.cos(t), 2*np.sin(t), 'g--', alpha=0.6)
ax.text(2.3, 0.8, f'θ={np.degrees(theta):.0f}°', color='green', fontsize=12)

circle = plt.Circle((0,0), np.linalg.norm(P), fill=False, linestyle=':', color='gray')
ax.add_patch(circle)
ax.set_title('pic1:P rotate θ to P\',vector length unchanged', fontsize=13)
plt.show()

上面代码运行后结果如下:

上面图像中,蓝色箭头所在的点(3,4)对应(x,y),红色箭头所在点(-1.96, 4.60)是点(3,4)围绕原点逆向选择60度后所在的坐标。一个很重要的信息是,红色箭头到原点的长度跟蓝色箭头到原点的长度是一样的。也就是所,旋转操作改变了向量的方向,但是不会改变向量的长度。

从数学上,我们需要依靠极坐标来表示这个旋转过程。假设最开始的点P(上图蓝色箭头只写的位置)与原点的距离为r, 它与原点连线形成的向量与x对应夹角为Ø,那么坐标(x,y)可以表示如下:

逆时针旋转θ后,坐标点的位置变了,但是不改变它到原点的距离,只是旋转后,向量与x轴的夹角变成Ø+θ,因此旋转后对应坐标(x',y')的极坐标表达如下:

根据三角和公式展开有:

把前面x,y对应的极坐标表示,结合上面的三角展开公式,带入到x',y'的极坐标表示就得到:

从图像上直观的理解就是距离不变,夹角增加θ,我们可以运行下面代码来看得更明白:

py 复制代码
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_xlim(-6, 6); ax.set_ylim(-6, 6)
ax.set_aspect('equal'); ax.grid(True, alpha=0.3)
ax.axhline(0, color='gray', linewidth=0.8)
ax.axvline(0, color='gray', linewidth=0.8)

r = np.linalg.norm(P)
phi = np.arctan2(P[1], P[0])

# 画极坐标的 r 和角度 phi
ax.annotate("", xy=P, xytext=(0,0), arrowprops=dict(arrowstyle='->', color='blue', lw=2))
ax.plot([P[0], P[0]], [0, P[1]], 'b:', alpha=0.5)
ax.plot([0, P[0]], [P[1], P[1]], 'b:', alpha=0.5)
ax.text(P[0]/2, -0.4, f'x={P[0]:.0f}', color='blue', fontsize=11)
ax.text(-0.6, P[1]/2, f'y={P[1]:.0f}', color='blue', fontsize=11)

t = np.linspace(0, phi, 50)
ax.plot(1.5*np.cos(t), 1.5*np.sin(t), 'b--', alpha=0.6)
ax.text(1.7, 0.4, f'φ={np.degrees(phi):.0f}°', color='blue', fontsize=11)

# 画旋转后的 r 和角度 phi+theta
ax.annotate("", xy=P_prime, xytext=(0,0), arrowprops=dict(arrowstyle='->', color='red', lw=2))
t2 = np.linspace(0, phi+theta, 50)
ax.plot(1.5*np.cos(t2), 1.5*np.sin(t2), 'r--', alpha=0.6)
ax.text(-1.5, 1.2, f'φ+θ={np.degrees(phi+theta):.0f}°', color='red', fontsize=11)

circle = plt.Circle((0,0), r, fill=False, linestyle=':', color='gray')
ax.add_patch(circle)
ax.text(0, -r-0.5, f'半径 r={r:.0f} 不变', color='green', fontsize=11, ha='center')
ax.set_title('图2:极坐标视角:距离 r 不变,角度从 φ 变成 φ+θ', fontsize=13)
plt.show()

上面代码运行后结果如下:

一个很重要的性质是,向量(x,y)在旋转后,向量的长度不变。我们知道向量(x,y)到原点的距离是sqrt(x^2 + y^2),在逆向旋转θ后对应的新坐标就是(x',y')=(xcons(θ)-y sin(θ), xsin(θ)+y cons(θ)),根据同样的方式计算(x',y')到原点的距离:

展开后合并所得结果如下:

由此可见一个点沿着原点旋转虽然改变了坐标的位置,但是不会改变它与原点的距离。我们使用代数形式来表达上面点(x,y)到(x',y')转变的过程:

也就是平面上点的旋转相当于点对应的坐标向量左乘一个旋转矩阵,于是定义旋转矩阵如下:

于是点P旋转到P'的过程就可以描述如下:

假设我们把点P连续旋转两次,第一次旋转a,第二次旋转b,那么对应的数学描述就是:

1.先逆时针旋转a,得到P1 = R(a) * P

2.再逆时针旋转b, 得到P2 = R(b) * P1 = R(b) * R(a) * P

从直觉上不难理解,两次分别旋转a和b,不就相当于一次直接旋转a+b吗,也就是说在数学上应该有 R(a+b) = R(b) * R(a),对于这一点我们可以从数学上加以正面,首先把R(b) * R(a)展开:

根据矩阵乘法,左边矩阵的第一行乘以右边矩阵的第一列有:

左边矩阵第一行乘以右边矩阵第二列有:

同理左边矩阵第二行乘以右边矩阵第一列有:

左边矩阵第二行乘以右边矩阵第二列:

总结上面的步骤就有:

上面正面的核心主要就基于两个三角和公式:

矩阵R有两个性质:

1.它每一列的模数值为1

2.选择它的任意两列进行内积运算,所得结果为0,也就是任意两列对应的向量相互垂直

我们看看R为什么能满足上面两点。首先确定它每一列求模运算后所得结果为1,先抽出R对应的两列:

由于向量求模运算时将每个分量平方后求和,然后在对求和结果开方,我们可以省略掉看法,看看如果每个分量平方后求和结果为1,那么开方的结果也一定是1,那么第一列求模运算为:

根据三角恒等式,上面的结果显然是1,然后看第二列求模运算的平方:

上面计算结果显然是1,所有R每一列求模运算的结果都是1.接下来我们确认矩阵的列相互垂直,也就是任意两列对应的向量做内积运算结果为0:

不难看出,上面内积运算结果为0,由此可得矩阵R每列向量求模运算后结果为1,同时任意两列的内积为0,因此R是正交矩阵。正交矩阵有一个很重要的性质,那就是它的转置等于它的逆,也就是:

上面等式第一部分我们不做证明,它属于线性代数的范畴,后面等式我们需要摸清楚,也就是R的逆等于把参数theta前面加个负号,这个性质对后面算法的推导非常重要。前面我们已经证明过:R(a)R(b) = R(a+b),我们可以使用这个性质来辅助证明,令a = theta, b = -theta,那么就有:

我们把R(0)展开就能看到它是个单位矩阵:

所以我们能证明前面等式后半部分:

RoPE算法不会把前面描述的角度0作为一个固定值来用,而是先设定一个频率ω,然后θ被设置为这个频率的倍数,通常我们会给R加一个下标i,那么赋值给R的参数θ就是频率的i倍,也就是θ=iω, 于是R_i(θ) = R(iω).基于这里的设定,我们不难推出:

这个结论很重要,我们先看它的推导,首先使用R的转置等于R的逆这个性质有:

然后使用性质R(a)*R(b) = R(a+b)有:

上面是等式的左边,然后右边就是下面:

综合起来我们就有如下结论:

这个结论的意思是说,带有下标的R_i, R_j 做乘积运行后,所得的结果只跟下标的差值:j-i相关,跟下标i,j的具体取什么值没有关系。其实这个结论直观上不难理解,假设甲在位置A,乙在位置B,两者距离相差10米,甲在前,以在后。如果甲向前走了5米,乙向前走了3米,那么甲和乙之间距离相差多少?不难算出是12米,同时可以确定的是甲和乙直接距离的大小跟原来甲所在的位置A和乙所在的位置B没有任何关系,只跟他们之间的相对位移有关系。

接下来我们看二维内积在旋转操作下的变化。对于二维向量u=(u1,u2), v=(v1,v2),他们内积定义为:

转换为矩阵形式如下:

从几何上看,内积还等于:

也就是说内积的值只依赖于向量的长度和他们之间的夹角。

假设两个二维向量x, y,首先对x做逆时针转动iw,得到R(i w)x, 于是有R_i = R(iw) ,针对y做逆时针转动j w, 于是有R_j=R(jw) ,那么x,y在做了相应的逆时针转动后,他们的内积会有怎样的变化呢。

首先我们把内积写成矩阵乘积的形式:

然后利用转置的性质(AB)^T = B^T * A^T,于是有:

我们把前面等式展开:

前面我们已经证明过:

把上面带入前面的等式就有:

这里的结论是,两个向量逆时针旋转后再进行内积运算,所得结果跟两个向量转动的频率倍数i,j没有关系,只跟他们频率倍数的差值有关。这跟我们前面说的甲乙相互距离的变动跟他们初始位置无关是一个道理。

相关推荐
青少儿编程课堂1 小时前
蓝桥杯·全国青少年信息素养大赛·电子学会C/C++考级:分层图最短路解析
c++·python·算法·bfs·信息学竞赛
YSL0701242 小时前
一文通关-----排序
数据结构·算法
ZhengEnCi12 小时前
GPT-6 Astra 爆改 Qwen 权重真有那么神吗:权重空间编程 0.016% 参数的真相
算法·架构
罗西的思考12 小时前
【轻量化复刻大模型 / KIMI K3】KDA(Kimi Delta Attention):并行
人工智能·算法·机器学习
夜不会漫长15 小时前
C++:红黑树
数据结构·算法
AI备案指南-满满15 小时前
算法备案审核不通过,一般是什么原因?
java·人工智能·算法·大模型备案·算法备案·大模型登记
Ramble_Naylor15 小时前
Rust深度解读:Send 与 Sync 的本质与线程安全边界
python·算法·rust
秋天的怀念_David15 小时前
算法一千零一夜 · 第二十三夜 电力民生写成的曲线
java·程序人生·算法
不一样的故事12615 小时前
NB/T 47013.8-2015 附录D 吸枪技术
人工智能·经验分享·算法