原文

This repo contains the instruction-tuned 0.5B Qwen2.5 model, which has the following features:

  • Type: Causal Language Models
  • Training Stage: Pretraining & Post-training
  • Architecture: transformers with RoPE, SwiGLU, RMSNorm, Attention QKV bias and tied word embeddings
  • Number of Parameters: 0.49B
  • Number of Paramaters (Non-Embedding): 0.36B
  • Number of Layers: 24
  • Number of Attention Heads (GQA): 14 for Q and 2 for KV
  • Context Length: Full 32,768 tokens and generation 8192 tokens

For more details, please refer to our blog, GitHub, and Documentation.

5 个组件


与所熟知的“原始” Transformer(即 2017 年《Attention Is All You Need》中的标准架构)相比,Qwen2.5 使用的这 5 个组件都有显著不同或增强。

但这并不意味着“标准 Transformer”不能用这些,而是说这些组件代表了现代高性能 LLM 的工业标准配置,它们解决了标准 Transformer 在深度训练稳定性、计算效率和大模型扩展性上的痛点。

下面逐一解析这五个模块与原始 Transformer 的区别:

一、 RoPE (Rotary Positional Embedding)

  • 原始 Transformer: 使用绝对位置编码(Absolute Positional Encoding)。直接给每个位置的向量加上一个固定的正弦/余弦向量。这种编码方式难以处理训练长度和推理长度不一致的情况,也不具备良好的外推性。
  • Qwen2.5 (RoPE): 使用旋转位置编码。它将位置信息编码为旋转矩阵,使得向量之间的相对位置关系能够被模型更好地捕捉。
    • 优势: 支持更好的长上下文外推(Extrapolation),即训练时看 32k,推理时看 128k 效果衰减较小;符合物理直觉(相对距离不变)。

1. 什么是 RoPE?

Rotary Position Embedding (RoPE) 是一种用于 Transformer 模型的位置编码方法。与传统的绝对位置编码(如正弦位置编码)不同,RoPE 将位置信息注入到查询(Query)和键(Key)向量中,使得模型能够捕捉序列中元素之间的相对位置关系。

RoPE 的核心思想是:通过旋转操作,将位置信息编码到向量的角度中。当计算 Query 和 Key 的点积时,位置信息会以相对角度的形式体现出来。

2. RoPE 的具体方案

2.1 数学原理

假设我们有一个向量 x ∈ R d x \in \mathbb{R}^d xRd,我们可以将其分解为 d / 2 d/2 d/2 个二维子向量。对于第 m m m 个位置,RoPE 通过一个旋转矩阵 R m R_m Rm 对每个二维子向量进行旋转。

对于二维向量 ( x 2 i x 2 i + 1 ) \begin{pmatrix} x_{2i} \\ x_{2i+1} \end{pmatrix} (x2ix2i+1),旋转矩阵定义为:

R m = ( cos ⁡ ( m θ ) − sin ⁡ ( m θ ) sin ⁡ ( m θ ) cos ⁡ ( m θ ) ) R_m = \begin{pmatrix} \cos(m\theta) & -\sin(m\theta) \\ \sin(m\theta) & \cos(m\theta) \end{pmatrix} Rm=(cos(mθ)sin(mθ)sin(mθ)cos(mθ))

其中 θ \theta θ 是基础频率,通常定义为:

θ i = 10000 − 2 i / d , i = 0 , 1 , … , d / 2 − 1 \theta_i = 10000^{-2i/d}, \quad i = 0, 1, \dots, d/2-1 θi=100002i/d,i=0,1,,d/21

2.2 高维推广

对于高维向量 x ∈ R d x \in \mathbb{R}^d xRd,RoPE 将向量分为 d / 2 d/2 d/2 对,每对应用上述二维旋转。最终的位置编码向量 x m x_m xm 为:

x m = R m x x_m = R_m x xm=Rmx

2.3 在注意力机制中的应用

在 Transformer 的注意力机制中,Query q m q_m qm 和 Key k n k_n kn 经过 RoPE 编码后变为 q m R m q_m R_m qmRm k n R n k_n R_n knRn。它们的点积为:

( q m R m ) ⋅ ( k n R n ) = q m ⋅ ( R m T R n k n ) (q_m R_m) \cdot (k_n R_n) = q_m \cdot (R_m^T R_n k_n) (qmRm)(knRn)=qm(RmTRnkn)

由于旋转矩阵的性质, R m T R n = R n − m R_m^T R_n = R_{n-m} RmTRn=Rnm,即点积结果只依赖于相对位置 n − m n-m nm。这使得模型能够捕捉相对位置信息。

3. RoPE 的实现过程

  1. 计算频率: 根据维度 d d d 计算基础频率 θ i \theta_i θi
  2. 应用旋转: 对于每个位置 m m m,将 Query 和 Key 向量分解为二维子向量,并应用旋转矩阵。
  3. 计算注意力: 使用旋转后的 Query 和 Key 计算注意力分数。

4. 代码实现

以下是 RoPE 的 Python 实现:

import torch
import math

def apply_rope(q, k, cos, sin):
    """
    应用 RoPE 到 Query 和 Key 向量
    
    Args:
        q: Query 向量, shape [batch, heads, seq_len, dim]
        k: Key 向量, shape [batch, heads, seq_len, dim]
        cos: 余弦值, shape [seq_len, dim]
        sin: 正弦值, shape [seq_len, dim]
    
    Returns:
        q_rope: 应用 RoPE 后的 Query
        k_rope: 应用 RoPE 后的 Key
    """
    # 将向量分为偶数和奇数部分
    q_embed = (q * cos) + (rotate_half(q) * sin)
    k_embed = (k * cos) + (rotate_half(k) * sin)
    return q_embed, k_embed

def rotate_half(x):
    """
    将向量分为两半并交换符号
    """
    x1, x2 = x.chunk(2, dim=-1)
    return torch.cat((-x2, x1), dim=-1)

def precompute_rope_params(head_dim, seq_len, theta=10000.0):
    """
    预计算 RoPE 参数
    
    Args:
        head_dim: 每个头的维度
        seq_len: 序列长度
        theta: 基础频率参数
    
    Returns:
        cos: 余弦值
        sin: 正弦值
    """
    assert head_dim % 2 == 0
    
    # 计算频率
    theta = 1.0 / (theta ** (torch.arange(0, head_dim, 2).float() / head_dim))
    
    # 生成位置索引
    position = torch.arange(seq_len).float()
    
    # 计算角度
    freqs = torch.einsum('i,j->ij', position, theta)
    
    # 计算正弦和余弦
    emb = torch.cat((freqs, freqs), dim=-1)
    cos = emb.cos()
    sin = emb.sin()
    
    return cos, sin

# 测试代码
if __name__ == "__main__":
    # 参数设置
    batch_size = 2
    num_heads = 4
    seq_len = 10
    head_dim = 64
    
    # 预计算 RoPE 参数
    cos, sin = precompute_rope_params(head_dim, seq_len)
    
    # 创建随机的 Query 和 Key
    q = torch.randn(batch_size, num_heads, seq_len, head_dim)
    k = torch.randn(batch_size, num_heads, seq_len, head_dim)
    
    # 应用 RoPE
    q_rope, k_rope = apply_rope(q, k, cos, sin)
    
    print("Query shape:", q.shape)
    print("Key shape:", k.shape)
    print("RoPE Query shape:", q_rope.shape)
    print("RoPE Key shape:", k_rope.shape)
    print("RoPE 应用成功!")

5. 总结

RoPE 是一种有效的位置编码方法,它通过旋转操作将位置信息注入到向量中,使得模型能够捕捉相对位置关系。与传统的绝对位置编码相比,RoPE 具有更好的外推能力和相对位置感知能力。

import torch
import math

def apply_rope(q, k, cos, sin):
    """
    应用 RoPE 到 Query 和 Key 向量
    
    Args:
        q: Query 向量, shape [batch, heads, seq_len, dim]
        k: Key 向量, shape [batch, heads, seq_len, dim]
        cos: 余弦值, shape [seq_len, dim]
        sin: 正弦值, shape [seq_len, dim]
    
    Returns:
        q_rope: 应用 RoPE 后的 Query
        k_rope: 应用 RoPE 后的 Key
    """
    # 将向量分为偶数和奇数部分
    q_embed = (q * cos) + (rotate_half(q) * sin)
    k_embed = (k * cos) + (rotate_half(k) * sin)
    return q_embed, k_embed

def rotate_half(x):
    """
    将向量分为两半并交换符号
    """
    x1, x2 = x.chunk(2, dim=-1)
    return torch.cat((-x2, x1), dim=-1)

def precompute_rope_params(head_dim, seq_len, theta=10000.0):
    """
    预计算 RoPE 参数
    
    Args:
        head_dim: 每个头的维度
        seq_len: 序列长度
        theta: 基础频率参数
    
    Returns:
        cos: 余弦值
        sin: 正弦值
    """
    assert head_dim % 2 == 0
    
    # 计算频率
    theta = 1.0 / (theta ** (torch.arange(0, head_dim, 2).float() / head_dim))
    
    # 生成位置索引
    position = torch.arange(seq_len).float()
    
    # 计算角度
    freqs = torch.einsum('i,j->ij', position, theta)
    
    # 计算正弦和余弦
    emb = torch.cat((freqs, freqs), dim=-1)
    cos = emb.cos()
    sin = emb.sin()
    
    return cos, sin

# 测试代码
if __name__ == "__main__":
    # 参数设置
    batch_size = 2
    num_heads = 4
    seq_len = 10
    head_dim = 64
    
    # 预计算 RoPE 参数
    cos, sin = precompute_rope_params(head_dim, seq_len)
    
    # 创建随机的 Query 和 Key
    q = torch.randn(batch_size, num_heads, seq_len, head_dim)
    k = torch.randn(batch_size, num_heads, seq_len, head_dim)
    
    # 应用 RoPE
    q_rope, k_rope = apply_rope(q, k, cos, sin)
    
    print("Query shape:", q.shape)
    print("Key shape:", k.shape)
    print("RoPE Query shape:", q_rope.shape)
    print("RoPE Key shape:", k_rope.shape)
    print("RoPE 应用成功!")

2. SwiGLU (Swish-Gated Linear Unit)

  • 原始 Transformer: 使用标准的前馈神经网络(FFN),结构通常是 Linear -> ReLU/GELU -> Linear。也就是两层全连接层中间夹一个激活函数。

在这里插入图片描述

  • Qwen2.5 (SwiGLU): 使用了门控线性单元结构。
    • 标准 FFN: W 2 ⋅ GELU ( W 1 ⋅ x ) W_2 \cdot \text{GELU}(W_1 \cdot x) W2GELU(W1x)

将 Swish 激活函数 与 门控线性单元 (GLU) 结合在一起的产物。在学术测试中,它被证明比传统的 ReLU 或 GELU 能让模型收敛得更快、效果更好。

SwiGLU 采用的是一种“左右开弓、互相控制”的机制,它的结构就像一个门控开关 (Gate):

  1. 左路(数据通道): 输入数据经过一层线性变换,直接准备输出。
  2. 右路(控制通道/门控): 输入数据经过另一层独立的线性变换,然后通过 Swish 激活函数。Swish 的输出结果在 0 0 0 1 1 1 之间,就像一个“阀门”。
  3. 合并: 将左路的数据和右路的“阀门”逐元素相乘 (Element-wise multiplication)。如果右路的阀门接近 0 0 0,对应位置的数据就被过滤掉;如果接近 1 1 1,数据就顺利通过。

如果用数学公式来表达,SwiGLU 的形态非常简洁:

SwiGLU ( x ) = Swish β ( x W + b ) ⊗ ( x V + c ) \text{SwiGLU}(x) = \text{Swish}_\beta(xW + b) \otimes (xV + c) SwiGLU(x)=Swishβ(xW+b)(xV+c)

为了更清晰地看清它的网络结构,我们通常可以忽略偏置项(Bias,在现代大模型中为了稳定往往会省略),它的计算流程如下:

  • 右路(门控): x W xW xW 是对输入 x x x 进行第一次线性变换,接着作用 Swish 函数: Swish ( x W ) = x W ⋅ σ ( β ⋅ x W ) \text{Swish}(xW) = xW \cdot \sigma(\beta \cdot xW) Swish(xW)=xWσ(βxW),其中 σ \sigma σ 是 Sigmoid 函数。
  • 左路(数据): x V xV xV 是对输入 x x x 进行第二次独立的线性变换。
  • 门控相乘: ⊗ \otimes 表示将两路的结果按位置对应相乘。

最终,这个输出还会再乘以第三个权重矩阵 W 2 W_2 W2,构成了大模型中完整的前馈网络 (FFN) 层:

FFN SwiGLU ( x ) = ( Swish ( x W ) ⊗ x V ) W 2 \text{FFN}_{\text{SwiGLU}}(x) = \left( \text{Swish}(xW) \otimes xV \right) W_2 FFNSwiGLU(x)=(Swish(xW)xV)W2

相比于老一辈的 ReLU,SwiGLU 有三个核心优势:

  • 没有“神经元死亡”问题: ReLU 在输入小于 0 0 0 时导数完全为 0 0 0,容易导致部分神经元永久失效。而 SwiGLU 的门控通道使用的是 Swish,它在负数区域有一段平滑的“非单调”下凹,保留了微弱的梯度传导。
  • 平滑的几何特性: Swish 函数处处可导且非常平滑,这种平滑性有助于深度模型在训练时更容易找到全局最优解。
  • 更强的表达能力: 因为它引入了两个独立的权重矩阵( W W W V V V)来进行元素级别的乘法交互,这相当于给网络增加了一个非线性的“动态筛选器”,让模型能够学习到更复杂的特征组合。

💡 代价是什么?
世界上没有免费的午餐。传统的 FFN 只需要两个权重矩阵(一进一出),而 SwiGLU 因为分成了左路和右路,一共需要三个权重矩阵。为了保持参数量和计算量与传统网络相当,大模型在设计时通常会按比例缩小中间隐藏层的维度(一般缩小到原本的 2 3 \frac{2}{3} 32 左右),从而在不增加计算负担的前提下,纯粹靠结构优势提升模型性能。

3. RMSNorm (Root Mean Square Layer Normalization)

在这里插入图片描述

  • 原始 Transformer: 没有明确的 LayerNorm 标准化步骤(原始论文中在 Encoder/Decoder 内部使用了残差连接后的标准化,但在后续演进中 LayerNorm 成为标配)。即使后来引入了 LayerNorm,它也是减去均值并除以标准差。

LayerNorm 做的事情可以拆解为两步:

  1. 平移与缩放(Standardization): 先算出这个向量所有元素的均值 μ \mu μ,让向量减去均值(中心化);再算出方差 σ 2 \sigma^2 σ2,除以标准差。

LayerNorm ( x ) = x − μ σ 2 + ϵ ⊙ γ + β \text{LayerNorm}(x) = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} \odot \gamma + \beta LayerNorm(x)=σ2+ϵ xμγ+β

  1. 可学习参数: 这里的 γ \gamma γ 是缩放因子(Scale), β \beta β 是平移偏置(Shift)。
  • Qwen2.5 (RMSNorm): 使用 RMSNorm。它去掉了 LayerNorm 中的“减去均值”这一步,只除以均方根(Root Mean Square)。
    • 公式差异: LayerNorm 计算 μ \mu μ σ \sigma σ,RMSNorm 只计算 1 n ∑ x i 2 \sqrt{\frac{1}{n}\sum x_i^2} n1xi2
    • 优势: 计算更简单、更快(少了一次减法运算),且在现代大模型中证明效果与 LayerNorm 相当甚至更好,尤其在深层网络中更稳定。

RMSNorm 的作者发现: LayerNorm 之所以能稳定模型训练,核心功劳在于那个分母(对激活值的缩放),它限制了梯度爆炸或消失;而分子上减去均值 μ \mu μ 以及可学习的平移参数 β \beta β,对模型最终性能的贡献微乎其微。

于是,RMSNorm 做了一个大胆的减法:不减均值了,也不要偏置 β \beta β 了,直接除以均方根!

RMSNorm 的数学原理

对于一个输入的特征向量 x = ( x 1 , x 2 , . . . , x d ) x = (x_1, x_2, ..., x_d) x=(x1,x2,...,xd),RMSNorm 的计算过程分为以下几步:

第一步:计算均方根 (RMS)

不求均值,而是把向量里每个元素的平方加起来求平均,再开根号:

RMS ( x ) = 1 d ∑ i = 1 d x i 2 \text{RMS}(x) = \sqrt{\frac{1}{d} \sum_{i=1}^{d} x_i^2} RMS(x)=d1i=1dxi2

第二步:归一化

用原向量直接除以这个均方根(为了防止分母为 0,会加上一个微小的超参数 ϵ \epsilon ϵ):

x ^ i = x i RMS ( x ) + ϵ \hat{x}_i = \frac{x_i}{\text{RMS}(x) + \epsilon} x^i=RMS(x)+ϵxi

第三步:缩放(Scale)

最后,乘上一个和 LayerNorm 一样的可学习的缩放向量 γ \gamma γ(维度与 x x x 相同),但不加偏置 β \beta β

RMSNorm ( x ) i = x ^ i ⋅ γ i \text{RMSNorm}(x)_i = \hat{x}_i \cdot \gamma_i RMSNorm(x)i=x^iγi

特性 / 步骤 LayerNorm (层归一化) RMSNorm (均方根归一化)
减去均值 ( μ \mu μ) 需要(将数据中心化) 不需要
分母计算 标准差(需先算均值,再算方差) 均方根(直接平方求和,一步到位)
可学习参数 γ \gamma γ (缩放) + β \beta β (平移) 只有 γ \gamma γ (缩放)
计算开销 较高(包含两次向量均值相关的遍历) 较低(减少了计算损耗和内存读写)

换成了 RMSNorm?

  1. 计算速度显著提升: 在大模型(LLM)的实际计算中,归一化操作是一个典型的 GPU 内存带宽受限(Memory-Bound) 任务。LayerNorm 需要计算均值和方差,意味着 GPU 要反复读写两次数据。而 RMSNorm 不需要均值,只需要遍历一次数据算出平方和即可,这显著减少了 GPU 显存的读写开销,能带来 7% ~ 50% 的层级加速
  2. 效果完全不打折: 实验证明,虽然 RMSNorm 舍弃了均值中心化,但在千亿参数级的大模型训练中,其收敛速度和最终的下游任务准确率与 LayerNorm 几乎完全一致。

总结来说: RMSNorm 通过“砍掉” LayerNorm 中性价比极低的均值计算和平移偏置,在不损失模型性能的前提下,实现了更快的计算速度更低的显存带宽占用

4. Attention QKV Bias

  • 原始 Transformer: 在 Multi-Head Attention 的 Query, Key, Value 线性变换中,通常不使用 Bias。原始论文为了保持注意力机制的平移不变性和计算简洁性,去掉了 Bias。
  • Qwen2.5 (QKV Bias): 在生成 Q、K、V 的线性层中加入了 Bias
    • 优势: 对于某些模型架构和训练策略,加入 Bias 有助于模型更快地收敛,或者在微调阶段提供额外的表达能力。虽然理论上注意力机制对 Bias 不敏感,但在实际大模型训练中,这是一个常见的工程优化选择。

5. Tied Word Embeddings (权重共享)

  • 原始 Transformer: 输出层的权重矩阵与输入的词嵌入(Embedding)矩阵是共享的。即,如果你有一个 V × d V \times d V×d 的词嵌入矩阵,输出层也是用这个矩阵的转置来映射回词汇表。
  • Qwen2.5: 也使用了 Tied Embeddings
    • 优势: 可以大幅减少参数量。对于较小的模型(如 0.5B, 1.5B),嵌入层的参数量占比很高,共享权重可以节省约 10%-20% 的总参数量,同时保持性能。这也是为什么你的文档中提到“Number of Paramaters: 0.49B”,但“Non-Embedding: 0.36B”,说明嵌入层占了约 0.13B 的参数。

Tied Word Embeddings(权重绑定词嵌入),在学术界也常被称为 Weight Tying(权重绑定)。它是自然语言处理和大语言模型中一种非常经典的参数优化技术。

核心思想非常简单直接:让自然语言模型“输入端”的词嵌入矩阵(Word Embedding)和“输出端”的线性投影矩阵(Output Projection / LM Head)共享同一个权重矩阵。
在这里插入图片描述

我们可以通过大模型的结构工作流来观察这两个矩阵所处的位置:

在一个常规的语言模型中,有两处涉及全量词表(Vobabulary Size = V V V)和隐藏层维度(Hidden Dimension = d d d)的大型矩阵:

  1. 输入端(Embedding Layer): 负责把离散的 Token ID 映射成连续的稠密向量(从 V → d V \to d Vd)。权重矩阵大小为 W i n ∈ R V × d W_{in} \in \mathbb{R}^{V \times d} WinRV×d
  2. 输出端(LM Head): 负责把模型最后一层输出的语义向量,投影回词表大小的对数几率(Logits),用来预测下一个词(从 d → V d \to V dV)。权重矩阵大小为 W o u t ∈ R d × V W_{out} \in \mathbb{R}^{d \times V} WoutRd×V

如果不做任何处理,这意味着同一个词表要在模型里存两份独立的巨大矩阵,带来极大的资源开销。

Tied Embeddings 的工作原理

Tied Embeddings 直接打破了这种独立性,它的计算逻辑如下图右侧所示:
在这里插入图片描述

  • 实现方法: 设共享的矩阵为 W ∈ R V × d W \in \mathbb{R}^{V \times d} WRV×d
  • 输入端变换: 正常的查表操作,输入 Token x x x,得到嵌入向量 e = W [ x ] e = W[x] e=W[x]
  • 输出端变换: 模型最后的隐藏状态向量为 h ∈ R d h \in \mathbb{R}^{d} hRd。要得到它属于每个词的概率,直接乘以共享矩阵的转置 W T W^T WT

Logits = h ⋅ W T \text{Logits} = h \cdot W^T Logits=hWT

通过 W T W^T WT,输出层线性投影就完美复用了输入层的矩阵。

这个设计在学术论文 《Using the Output Embedding to Improve Language Models》(Press & Wolf, 2016) 被系统提出后,迅速成为标配,主要带来三大收益:

  1. 大幅削减参数量与显存占用:
    在大模型中,词表 V V V 通常在几万到十几万不等(如 LLaMA 3 的词表约为 128k)。以 V = 130 , 000 , d = 4096 V = 130,000, d = 4096 V=130,000,d=4096 为例,一个矩阵的参数量就是 5.3 亿(530M)。如果不绑定,光是输入输出的矩阵就要吃掉 10 亿多参数。使用 Weight Tying 能直接省下这 5.3 亿参数的显存,这对于轻量级模型(如 1B~3B 级别)的性价比提升极其夸张。
  2. 正向特征对齐与联合优化:
    直观上讲,如果“把一个词翻译成语义(输入)”和“把语义还原成词(输出)”用的是同一套刻度尺,两端的特征空间就能自动对齐。每一次反向传播,输入和输出的特征表达都能同时得到训练和修正
  3. 防止稀有词过拟合:
    在训练语料中,很多冷门或稀有的词可能只在输入端被模型看到过几次。如果输出层独立,输出端的对应参数几乎得不到有效更新。绑定权重后,输入端学到的特征能够直接反哺输出端,增强模型对罕见词的鲁棒性。

现代大模型的工业界现状:Tied 还是 Untied?

好玩的是,技术的发展是一个轮回。在今天的大模型时代,是否选择 Tied Embedding 取决于模型的规模和设计偏好:

  • 选择 Tied(绑定)的模型: Gemma 系列 (Google)、Gemma 2、Qwen 系列的部分小参数版本。Google 倾向于在小参数模型(如 2B、7B)中疯狂使用 Weight Tying,把省下来的参数和显存额度全部挪给 Transformer 内部的注意力层和 FFN 层,从而在同等体积下榨干性能。
  • 选择 Untied(不绑定)的模型: LLaMA 系列 (Meta)、Mistral。Meta 的 LLaMA 采用了标准的 Untied 结构。因为当模型总参数量达到 70B 甚至 405B 时,区区 500M 的词嵌入矩阵只占总体积的冰山一角。不绑定可以赋予输出层更高的表达自由度,允许输入语义空间和输出预测空间存在微小的几何偏置,从而略微提升极大规模模型的能力上限。

总结: Tied Word Embeddings 是一种高效的参数复用技术,通过转置共享同一个词表矩阵,在小参数和中型大模型中是极佳的“显存省钱利器”;而在超大规模模型中,为了极致的特征拟合,工程师才会选择将它们解耦。


总结对比表

模块 原始 Transformer (2017) Qwen2.5 / 现代 LLM 标准 主要改进目的
位置编码 绝对位置编码 (Sin/Cos) RoPE 更好的长文本外推性、相对位置感知
FFN 激活 GELU / ReLU (全连接) SwiGLU (门控) 更强的非线性表达能力、训练更稳定
层归一化 (原始无,后期加 LayerNorm) RMSNorm 计算更快、内存占用更少、深层训练更稳
Attention Bias 无 Bias 有 QKV Bias 微调灵活性、潜在的性能微调增益
Embedding 权重 共享 (Tied) 共享 (Tied) 节省参数,对小模型尤其重要

结论

看到的这些配置,实际上是当前最先进的开源大语言模型(如 Llama 3, Qwen, Mistral 等)的标准技术栈

它们不是“错误”,而是经过多年实践验证的最优解组合。如果你是在复现或对比实验,直接使用这些现代组件通常会比“原始 Transformer”获得更好的效果,尤其是对于小参数模型(如 0.5B)而言,SwiGLU 和 RMSNorm 的贡献尤为关键。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐