Administrator
发布于 2026-02-17 / 10 阅读
0
0

Llama3的关键组件解读

微信图片_2026-02-17_143649_885.png

如果前面已经了解过transformer,那么学习llama就顺理成章了一些,llama采用Decoder-only 架构,也就是transformer架构的右侧部分,并结合前人工作做了一些改进,咱们直接来看看整体差异:

组件

原始 Transformer (2017)

LLaMA3 (2024)

改进原因

架构类型

Encoder-Decoder

Decoder-only

更适合生成任务

归一化位置

Post-Norm (子层后)

Pre-Norm (子层前)

训练更稳定

归一化类型

LayerNorm

RMSNorm

计算更快,参数更少

位置编码

正弦/余弦绝对位置编码

RoPE (旋转位置编码)

更好的相对位置表示

激活函数

ReLU

SwiGLU

更强的非线性表达能力

注意力机制

标准 MHA

GQA (分组查询注意力)

推理速度更快,显存更少

FFN 扩展比

4× d_model

8/3 × d_model

保持参数量平衡

词汇表大小

~32K

128K

更好的多语言支持

一、RoPE (旋转位置编码)

1. 为什么我们需要 RoPE?

在传统的 Transformer 中,Self-Attention 机制本身是置换不变(Permutation Invariant)的。也就是说,如果不加位置编码,模型无法区分 "我爱你" 和 "你爱我"。早期的解决方案(如原始 Transformer 论文)是给每个词加上一个绝对位置向量。但这存在两个问题:

  1. 缺乏相对位置感知:模型需要费力地从绝对位置中推导相对距离。

  2. 外推能力差:如果训练时最长序列是 512,推理时遇到 1024 的长度,模型往往表现不佳。

RoPE 的核心目标:设计一种编码方式,使得Attention计算的结果天然地、仅依赖于词与词之间的相对距离,从而同时获得更好的泛化能力和长度外推性。。

2. 图解 RoPE:将“位置”转化为“旋转”

🟢 上半部分:核心几何原理

想象我们在一个二维平面上(对应 embedding 维度d=2d=2),有一个向量x=(x1,x2)\mathbf{x} = (x_1, x_2),代表某个词的 Query 或 Key 的一部分。

  • 位置即角度:每个位置 m 被映射为一个旋转角度 mθ(图中的 θ1, θ2 即由位置决定)。

  • 编码即旋转:一个词的 QueryKey向量(图中灰色向量),会根据其所在位置 m,在二维平面上旋转 mθ 角度,得到新的 Position Encoded Query/Key​ 向量。

  • 直观理解:可以把它想象成一个钟表盘。不同位置的词,其向量就像指针一样,从初始方向(12点方向)开始旋转。位置 1 转 1 个小角度,位置 2 转 2 倍的角度,以此类推。位置信息已经完全蕴含在旋转后的角度中。

🔵 下半部分:多维度的扩展

真实的 Embedding 维度通常是 4096 或更高,不仅仅是 2 维。RoPE 如何处理?

  • 分组旋转:如图所示,RoPE将高维向量成对地划分为多个二维子空间。图中每一行代表一个词(Token),每一列代表一个维度。颜色相同的两个格子(如浅红与深红)即组成一个二维组。

  • 不同频率:关键来了!不同颜色的组(红、绿、蓝…)使用不同的旋转速度(频率)。这通过为每组设置不同的基础旋转角度 θ来实现(通常按指数衰减设置)。这类似于傅里叶变换,让模型能够用不同“转速”的旋转组合来捕捉从短程到长程的复杂依赖模式。

最终,一个高维的词向量,其每一对维度都根据词的位置进行了独立的旋转,从而将完整的位置信息编织进向量的各个角落。

3. 数学魔法:为什么旋转能表示相对位置?

这是 RoPE 最精妙的地方。旋转操作有一个美妙的数学性质:两个向量旋转后再做点积,其结果只与它们原始的夹角和相对旋转角度有关。让我们形式化地看一下:

设词在位置mm的查询向量为qmq_m,在位置nn的键向量为knk_n。经过RoPE编码后,它们分别被旋转了mθm_θnθn_θ角度。当我们计算Attention分数(即点积)<Rotate(qmmθ)Rotate(knnθ)><Rotate(q_m, m_θ), Rotate(k_n, n_θ)>时,根据复数乘法或旋转矩阵的性质,它等价于将其中一个向量(比如 knk_n)反向旋转(mn)θ(m-n)θ角度后,再与qmq_m做点积。

用公式简化表示:

<Rotate(qm)Rotate(kn)>=<qmRotate(kn(nm)θ)><Rotate(q_m), Rotate(k_n)> = <q_m, Rotate(k_n, (n-m)θ)>

这样一来,绝对位置 mn消失了,最终只剩下它们的差值 (n-m),即相对距离。​ 这意味着,在Attention计算时,模型无需任何额外学习,就能直接感知词与词之间的相对位置关系。

平移不变形(QK乘积只与QK的相对位置有关,与他们的绝对位置无关)

理解了原理,代码便一目了然。RoPE的实现本质是应用一系列二维旋转。

1. 几何操作 vs. 数值叠加:对语义空间的干扰不同

  • 绝对位置编码(Additive):就像一个“拼贴”过程。将代表词义的向量 V_word和代表位置的向量 V_pos直接相加,得到 V_final = V_word + V_pos

    • 问题:即使 V_pos设计得与大部分 V_word正交,加法操作在几何上相当于将词向量“推”到了语义空间中的一个新点。这会不可避免地改变词向量的原始方向和长度。模型必须从这个“被污染”的表示中,费力地分离出纯粹的语义信息和位置信息。这确实引入了噪声或干扰

  • 旋转位置编码(Multiplicative/Rotative):就像一个“旋转”过程。它在词向量所在的同一高维空间内,通过一个保持长度不变的旋转变换来编码位置。

    • 优势:旋转不改变向量的模长(即“强度”或“能量”),只改变其方向。这意味着,词向量本身的“语义强度”被完整保留。位置信息被编码在方向的变化规律中。这是一种更“优雅”的干扰,因为它严格遵守了几何规则。

2. 内积(点积)的性质:相对位置的内生性

这是 RoPE 最精妙的数学特性,也印证了你的“更纯粹”说。

  • 绝对位置编码:计算 Attention 分数时,<q_m, k_n> = <(q + p_m), (k + p_n)>。展开后包含 p_mp_n的交叉项,模型需要学习这些复杂的交互模式来推断相对位置。

  • 旋转位置编码:正如我们之前推导的,经过旋转后,<Rotate(q, mθ), Rotate(k, nθ)> = <q, Rotate(k, (n-m)θ)>

    • 这个等式的成立,完全依赖于旋转操作在线性代数中“保内积”和“可交换”的优良性质。相对位置 (n-m)被直接、干净地提取出来,并作用于原始的键向量 k

    • 这个过程没有引入任何新的、与语义无关的向量或复杂的交互项。相对位置感知能力是旋转变换的自然产物,而不是模型从噪声数据中学到的模式。这使它从原理上就更加“纯粹”和高效。

3. 信息注入方式:正交与融合

  • 绝对位置编码:试图将位置信息作为一个独立的、正交的维度“贴”到语义向量上。但这在实际高维空间和复杂训练中很难完美实现,容易产生“信息粘连”。

  • 旋转位置编码:它将位置信息编织进语义向量的每一个维度对中。它不是添加一个新维度,而是改变现有维度之间的关系(即相位差)。这就像调制无线电波:载波(语义信息)的振幅不变,而是通过改变其相位(位置信息)来编码信号。这是一种更深层次的“融合”,而非“叠加”。

二、RMSNorm

三、KV Cache与GQA (分组查询注意力)

(需要再具体,手绘推导)

四、SwiGLU激活函数

  1. 什么是激活函数?

如果把神经网络中的一个神经元想象成一个简单的“加工站”:它接收输入的数字,进行一些加减乘除的计算,然后输出结果。但如果每个加工站都只做这种线性计算,那么无论堆叠多少层,整个网络也只能处理最简单、最直来直去的模式。激活函数,就是这个加工站里一个关键的“魔法开关”。它的作用是对计算出的结果进行一次非线性的转换——比如把负数变成零、把数值压缩到某个范围内,或者根据结果本身动态调整输出。正是这一步“魔法操作”,让神经元能够学习到数据中各种复杂的弯曲、转折与关联,从而赋予人工智能模型理解图像、语言和世界深层规律的能力。没有它,AI的大脑就如同只能画直线的尺子,无法描绘出丰富多彩的现实世界。

  1. SwiGLU激活函数又是什么?

在了解SwiGLU之前,需要先了解Swish函数(也叫SiLU),以及GLU(Gated Linear Unit,门控线性单元)。

  • Swish函数

Swish(x)=xσ(βx)Swish(x)=x⋅σ(βx)(通常β=1β=1),其中 σ 是 sigmoid 函数(σ(x)=11+ex\sigma(x)=\frac{1}{1+e^{-x}}),故一般而言

Swish(x)=x1+exSwish(x)=\frac{x}{1+e^{-x}}

  • GLU

GLU是一种门控结构,它把“线性变换”这一步,一口气做成两份(用两套不同的权重W和V):一份变换(W*x)负责生成要传递的“内容”。另一份变换(V*x)经过一个Sigmoid函数,生成一个0到1之间的“门控信号”。然后,它将“内容”和“门控信号”逐元素相乘。公式如下:

GLU(x)=(Wx)σ(Vx)GLU(x) = (W*x) ⊗ σ(V*x)

(其中 是逐元素乘法,σ是Sigmoid函数)

公式看起来有些复杂,咱们直接看一个例子,假设我们正在处理一个单词的向量表示,并且为了计算简单,我们使用一个2维的输入向量。

第1步:准备输入

我们的输入向量 x代表一个单词的特征, x=[1.0,0.5]x=[1.0, -0.5​]

第2步:定义权重矩阵

GLU需要两个不同的权重矩阵 WV。为了简化,我们假设它们是2×2的小矩阵,在实际的神经网络中,这些权重是通过训练学习得到的。

第3步:计算内容部分 (Wx)

专家A对输入进行处理:

这就是经过专家A加工后的"原始内容"。

第4步:计算门控信号 (σ(Vx))

专家B也对同一个输入进行处理:

然后将这个结果通过Sigmoid函数(σ),将每个值压缩到0到1之间:

所以门控信号为:

第5步:逐元素相乘(门控)

现在我们将内容部分与门控信号逐元素相乘:

最终结果分析

我们的GLU输出是 [0.188, -0.397]。看看发生了什么:

  1. 第一个维度:原始内容值是0.7,门控信号是0.269,这意味着只保留了约26.9%的信息。可能是因为专家B认为这个特征在当前上下文中不太重要。

  2. 第二个维度:原始内容值是-0.65,门控信号是0.611,这意味着保留了约61.1%的信息,而且保持了负号。负值在神经网络中同样传递重要信息。

基于上述内容,咱们可以回到swiGLU来, SwiGLU(x)=(Wx)Swish(Vx)SwiGLU(x) = (W*x) ⊗ Swish(V*x),相当于就是把原来门控当中的 sigmoid 函数替换成 swish 函数,即上述第4步:计算门控信号 σ(Vx)替换成 swish(Vx),其他步骤不会有任何变化,这里计算比较简单就不展开赘述了。


评论