【LLM-八股篇】为什么现在的LLM都是Decoder-only架构

118 阅读16分钟

LLM为什么Decoder only架构

参考: 苏剑林-kexue.fm/archives/95…

先给出苏神的答案:

LLM之所以主要都用Decoder-only架构,除了训练效率和工程实现上的优势外,在理论上是因为Encoder的双向注意力会存在低秩问题,这可能会削弱模型表达能力,就生成任务而言,引入双向注意力并无实质好处。而Encoder-Decoder架构之所以能够在某些场景下表现更好,大概只是因为它多了一倍参数。所以,在同等参数量、同等推理成本下,Decoder-only架构就是最优选择了。


这里给出三个理由,更多的可以参考为什么现在的LLM都是Decoder only的架构?_为什么decoder-only的模型这么火-CSDN博客

1.Encoder的双向注意力会存在低秩问题,可能会削弱模型的表达能力

2.decoder-only支持一直复用KV-Cache,对多轮对话更友好,因为每个token的表示只和它之前的输入有关,而encoder-decoder和PrefixLM就难以做到

3.decoder-only 模型在没有任何 tuning 数据的情况下、zero-shot 表现最好,而 encoder-decoder 则需要在一定量的标注数据上做 multitask finetuning 才能激发最佳性能。

Decoder-only训练的时候做的就是生成任务,在 Zero-shot 场景下,Prompt 往往符合自然语言的流动习惯,天生就是为了把话接下去而设计的。


然后我们进行更本质地探索,为什么说Encoder的双向注意力会存在低秩问题呢?

低秩问题探讨

Transformer一开始是用于做seq2seq任务的,即包含Encoder和Decoder两个部分。Encoder在抽取序列中某一个词的特征时,可以看到整个序列中的信息(完形填空)。而Decoder中因为有mask机制的存在,使得其在编码某一个词的特种功能时,只能看到自身和它之前的文本信息(生成任务)。

Decoder 采用Causal Mask(因果掩码)方法,第一行只能看1个,第N行看N个,是一个下三角矩阵。(满秩)

decoder-only架构通过mask机制,在物理上锁死了矩阵的秩,防止它坍塌。

注意力

下面的图为注意力的矩阵。AttnQ,K,V=softmax(QKT/(dk))VAttn(Q,K,V)=softmax(QK^T/\sqrt{(d_k)})*V Q,K都是一个低秩矩阵,即Q=n×d,K=n×dQ=n\times d, K=n\times d => Attn矩阵大小为n×nn\times n

这里的n可以理解为seq_len,d理解为hidden dimension, 在代码实现中,Q,K,VQ,K,V都是输入xx通过Linear变换得到,即self.q=Linear(d,d),Q=sele.q(x),(xsize=[B,L,d])self.q=Linear(d, d), Q=sele.q(x), (x_{size}=[B,L,d])

如何理解这张图,设 A 为 Decoder 的 Attention 权重矩阵。AiiA_{ii} 代表第 i 个 Token 对自身的注意力。decoder的mask策略就是把 j>i的部分(上三角)给mask掉

对于Decoder的attn矩阵来说,是一个下三角矩阵。而三角矩阵的行列式永远等于对角线上的元素的乘积。

又Attention矩阵的对角线元素AiiA_{ii}是通过softmax计算得到的概率值。即ex>0e^x>0(softmax的输出恒大于0),这意味着attn矩阵的对角元素非0 =》 Decoder-only 的 Attention 矩阵在数学上必然是满秩的。

行列式不为0 => 满秩矩阵 => 表征能力更强 => 不会出现低秩坍塌(low rank)

那满秩意味着什么?

满秩意味着,每个位置的token都能保留其独特的几何位置信息,信息没有损失,表征更丰富。

满秩(Rank NN ): 表达能力最强,能区分 NN 个不同的特征。

低秩(Rank 1): 极端情况下,所有行都一样,矩阵变成秩为 1。这意味着模型失去了区分不同位置 Token 的能力,所有 Token 的表示趋同

如果是bert这种双向注意力,其attn矩阵为稠密矩阵(上图第一个子图)。在深度网络中,双向注意力易出现同质化smoothing。即随着层数的加深,大家看到的上下文越来越像,矩阵A可能趋向每一行都差不多(比如每行都关注某一个特殊的token)。一旦行与行之间变得相似(线性相关)矩阵的秩就会下降(low rank)

从第一性原理来看,表达能力本质上是模型能够区分不同输入状态的能力。 满秩能够 抵抗“表征坍塌” (Preventing Rank Collapse)

  • Encoder(双向):  它是全连通图。在深度网络中,如果没有残差连接,双向注意力会倾向于让所有 Token 的 Embedding 趋同(过平滑)。从矩阵角度看,其每一行越来越像,矩阵的秩迅速萎缩。一旦秩下降,模型就会丢失局部细节,只能记住模糊的全局统计特征。
  • Decoder(单向):  强制性的下三角结构像一道“防火墙”。无论网络多深,第 1 个词永远只看自己,第 2 个词看 1 和 2……这种层次化的信息流动强制保证了每个位置的表征在数学上必须是独立的。满秩保证了模型在每一层都保留了最大化的信息熵。

下面链接一下attn coding的具体实现:

缩放点积注意力(Pytorch实现)

import torch  
import torch.nn as nn  
import torch.nn.functional as F  
import math  

def scaled_dot_product_attention(query, key, value, mask=None):  
    """  
    query: (batch, n_heads, seq_len, d_k)  
    key:   (batch, n_heads, seq_len, d_k)  
    value: (batch, n_heads, seq_len, d_v)  
    mask:  (batch, 1, seq_len, seq_len) - 用于遮盖 Padding 或 Decoder 的后续 Token  
    """  
    d_k = query.size(-1)  
    
    # 1. 计算注意力得分: Q * K^T  
    # scores 形状: (batch, n_heads, seq_len, seq_len)  
    scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(d_k)  
    
    # 2. 如果有 Mask,将 Mask 为 0 的位置填为极小值,Softmax 后变为 0  
    if mask is not None:  
        scores = scores.masked_fill(mask == 0, -1e9)  
    
    # 3. Softmax 归一化得到注意力权重  
    p_attn = F.softmax(scores, dim=-1)  
    
    # 4. 权重乘以 Value 向量  
    return torch.matmul(p_attn, value), p_attn

多头注意力 (Multi-Head Attention)

class MultiHeadAttention(nn.Module):  
    def __init__(self, d_model, n_heads):  
        super(MultiHeadAttention, self).__init__()  
        assert d_model % n_heads == 0  
        
        self.d_k = d_model // n_heads  
        self.n_heads = n_heads  
        
        # 定义四个线性层:W_q, W_k, W_v 和 最后的输出投影 W_o  
        self.linears = nn.ModuleList([nn.Linear(d_model, d_model) for _ in range(4)])  
        
    def forward(self, query, key, value, mask=None):  
        batch_size = query.size(0)  
        
        # 1. 线性变换投影并切分为 n_heads 个头  
        # (batch, seq_len, d_model) -> (batch, n_heads, seq_len, d_k)  
        query, key, value = [  
            l(x).view(batch_size, -1, self.n_heads, self.d_k).transpose(1, 2)  
            for l, x in zip(self.linears, (query, key, value))  
        ]  
        
        # 2. 应用缩放点积注意力  
        x, self.attn = scaled_dot_product_attention(query, key, value, mask=mask)  
        
        # 3. 将多头合并 (Concat)  
        # (batch, n_heads, seq_len, d_k) -> (batch, seq_len, d_model)  
        x = x.transpose(1, 2).contiguous().view(batch_size, -1, self.n_heads * self.d_k)  
        
        # 4. 最后一次线性投影  
        return self.linears[-1](x)
  1. 为什么除以 dk\sqrt{d_k}
    如果 dkd_k 很大,点积的结果会变得很大,导致 Softmax 进入梯度极小的区域(饱和区)。除以根号 dkd_k 可以让方差保持在 1 左右,训练更稳定。

  2. Mask 的实现:

    • 在 Encoder 中,Mask 用于遮盖补零的 Token(Padding Mask)。
    • 在 Decoder 中,Mask 是一个下三角矩阵(Causal Mask),防止模型在预测当前词时“偷看”未来的词。
  3. Dimension 重排 (View & Transpose):
    多头注意力的精髓在于将 dmodeld_{model} 拆开。通过 transpose(1, 2),我们将 n_heads 维度提前,这样在计算点积时,每个头之间是完全独立的并行计算。

代码调用方式:

# 假设 d_model=512, 8个头, 序列长度为10, batch大小为2  
mha = MultiHeadAttention(d_model=512, n_heads=8)  
x = torch.randn(2, 10, 512)  

# 自注意力计算 (Self-Attention)  
output = mha(x, x, x, mask=None)  
print(output.shape) # 输出应为 torch.Size([2, 10, 512])

那为什么说下三角矩阵的行列式不为0,就意味着满秩呢?这里涉及到线性代数的相关基础计算,这里总结了矩阵的秩和行列式之间的关联:

矩阵的秩和行列式的关联

秩的定义

矩阵的秩:本质上是列向量(或行向量)线性无关向量的最大个数。反映了矩阵所代表的线性变换将空间压缩的程度。

如果一个矩阵的秩等于行数/列数,那么其代表的变换是满秩的,意味着不会将空间压缩到更低的维度。(表征更丰富,没有被拍扁了)

怎么和三体里面的知识联动了

计算秩

行阶梯形矩阵中非零行的行数就是矩阵的秩。

而矩阵的秩的计算,最常用的是通过行阶梯矩阵来实现。即矩阵通过初等行变换(交换,某行乘以非零常数)化为行阶梯矩阵。行阶梯矩阵的特点是:

  • 非零行都在零行之上。
  • 每行第一个非零元素(称为主元或先导元素)所在的列,在该主元下方所有元素都是零。
  • 主元所在的列,其右侧的行的主元必须在更靠右的位置。

行列式是一个标量值,它反映了方阵所代表的线性变换对空间体积的伸缩比例。

  • 二维空间: 对于 2×2 矩阵,其行列式的绝对值代表了由其列向量(或行向量)所张成的平行四边形的面积。
  • 三维空间: 对于 3×3 矩阵,其行列式的绝对值代表了由其列向量(或行向量)所张成的平行六面体的体积。
  • 更高维度: 推广到 n 维空间,行列式的绝对值代表了由其列向量(或行向量)所张成的 n 维“平行体”的体积。

此外,行列式的正负表示了变换是否改变了空间的定向(例如,是否发生了翻转)。如果行列式为零,则意味着变换将空间压缩到了更低的维度,体积变为零。

矩阵的秩和行列式之间的关联

  • 行列式为零 <=> 矩阵不可逆

如果一个方阵的行列式为零意味着它所代表的线性变换将 n 维空间压缩到了一个更低的维度(例如,将三维空间压缩成一个平面或一条线)。当空间被压缩时,不同的原始向量可能会被映射到相同的目标向量,导致变换是不可逆的

  • 矩阵不可逆 <=> 矩阵的列向量(或行向量)线性相关

如果一个矩阵的列向量是线性相关的,这意味着其中至少有一个列向量可以通过其他列向量的线性组合来表示。在这种情况下,这些列向量无法张成整个 n 维空间,它们只能张成一个更低的子空间。当向量无法张成整个空间时,变换会“丢失信息”,导致不可逆

  • 矩阵的列向量(或行向量)线性相关 <=> 矩阵的秩小于其维数

矩阵的秩定义为线性无关列向量的最大个数。如果列向量线性相关,则线性无关的列向量数量会少于矩阵的列数,从而导致秩小于矩阵的维数。

我们可以得到以下重要的等价关系(对于 n×n 方阵A)

  1. det(A)=0 (行列式为零)
  2. 矩阵 A 不可逆 (没有逆矩阵)
  3. 矩阵 A 的列向量(或行向量)线性相关
  4. rank(A)<n (秩小于矩阵的维数,即不满秩)
  5. 线性方程组 Ax=0 有非零解 (齐次线性方程组有无穷多解)

反之,如果行列式不为零,则这些条件全部取反:

  1. det(A)≠0 (行列式不为零)
  2. 矩阵 A 可逆 (存在逆矩阵)
  3. 矩阵 A 的列向量(或行向量)线性无关
  4. rank(A)=n (秩等于矩阵的维数,即满秩)
  5. 线性方程组 Ax=0 只有零解 (齐次线性方程组只有唯一零解)

总结

  • 行列式为0 <=> 矩阵不满秩
  • 行列式不为0 => 满秩

好的,了解完行列式和矩阵的秩的关联之后,你应该彻底懂了,而什么Encoder的双向注意力会导致低秩问题了,而decoder-only的架构具备更强的表征能力。

这里在讨论一下更广义的低秩问题:

Low-Rank Bottleneck

LLM 中更广义的“低秩问题”(Low-Rank Bottleneck)通常指的是Softmax Bottleneck,这直接影响模型的能力。在LLM的输出层,需要生成一个词表大小(VV)的概率分布, p=softmax(HWT)p=softmax(H\cdot W^T)

H是隐藏层,size为B×dB\times d, d即hidden dimension,如4096

W是输出嵌入矩阵,size为V×d,V即词表大小,如100kV\times d, V即词表大小,如100k

最终logits矩阵的秩受限于中间维度,因为d<<Vd << V,所以输出的logits矩阵是一个低秩矩阵

而自然语言的真实分布是非常复杂的,理论上可能需要满秩(Rank V)才能完美拟合。用一个低秩矩阵去强行拟合一个高秩的真实数据分布,就是低秩瓶颈

当出现低秩问题时,LLM主要表现为表达多样性能力的下降:

  • 困惑度(Perplexity)无法继续下降: 模型在训练集上已经拟合不动了,因为它的几何空间不够大,无法将复杂的语言模式区分开。
  • 退化与重复(Degeneracy): 模型生成的文本容易陷入循环或重复。因为在低秩投影下,原本应该有细微差别的两个词(例如“高兴”和“愉悦”),在低维空间里重叠了,模型无法区分它们的语境差异。
  • 多义词理解变差: 比如 context 变了,但模型输出的概率分布没变,因为它缺乏足够的维度来编码这种上下文的变换。

说到复读机问题,那么为什么decoder-only中的attn矩阵明明是满秩的,按道理来说表征应该更丰富,不会陷入到低维坍塌中,可是我们初期在使用LLM的时候,会经常看到他重复输出一些无意义的内容。

这里给出Gemini的回答:

Attention矩阵满秩(Full Rank)只代表模型具备“区分不同token位置”的能力(表达能力),并不代表模型会“主动选择多样化”的输出(行为策略)。

可以从以下三个维度来分析:

1. 自回归

GPT的本质是一个自回归模型(Autoregressive Model),其生成公式为:P(xtx<t)P(x_t | x_{<t})

自回归(Auto + Regression),用“自己已经生成的结果”,继续预测后面的结果

非自回归模型, BERT。 BERT是一个基于 Transformer Encoder 的预训练语言模型。它的核心目标是理解文本,而不是生成文本。(双向注意力,训练的目的是 填空)

  • 正反馈循环(Positive Feedback Loop):

    当你生成了一个token(比如“是”),这个token立刻变成了下一个时间步的输入

    如果模型在训练数据中见过很多重复的模式(比如列表、代码缩进、强调句),或者模型对当前语境的判断出现了“不确定性坍缩”,它可能会认为“重复上一个词”是概率最安全的选择。

  • 吸引子(Attractor State):

    一旦模型生成了 A, A,输入变成了 ...A, A

    在Attention机制中,Query (当前token) 会去和 Keys (历史token) 做点积。如果历史中充斥着大量的 A,Query A 和 Key A 的相似度极高。

    这将导致 Attention Score 更加集中在这些重复的 token 上,输出概率分布 P(xnext=A)P(x_{next}=A) 进一步接近 1.0。

    这就形成了一个动力学系统中的强吸引子——一旦掉进去,就出不来了。

2. 线性代数视角:满秩与“各向异性”

“满秩”是指 Decoder 的 Masked Attention Matrix(下三角矩阵)理论上是满秩的(对角线非零即可逆)。

  • 满秩 \neq 均匀分布:

    矩阵满秩只意味着行向量线性无关,意味着模型有能力关注到每一个历史时刻。但是,Attention 的核心是 Softmax

    Attention(Q,K,V)=Softmax(QKTdk)V\text{Attention}(Q, K, V) = \text{Softmax}(\frac{QK^T}{\sqrt{d_k}})V

  • Softmax 的稀疏化倾向:

    Softmax 是一个指数函数,它会极大地放大 Logits 之间的微小差异。

    假设 Context 中有 100 个 token,如果第 99 个 token 和当前的 Query 相似度稍微高一点(比如点积是 10,其他是 5),经过 Softmax 后,第 99 个 token 的权重可能接近 0.99,而其他 99 个 token 的权重之和不到 0.01。

    虽然矩阵在数学上是满秩的,但在数值上,它退化成了一个极其稀疏的矩阵(接近 One-hot)。 这导致模型实际上“看不见”其他的上下文,只能看到那个引发它复读的 token。

3. 几何视角:表示空间的坍缩 (Representation Collapse)

这是更深层的解释,涉及到 Embedding 空间的几何形状。

  • 锥形效应 (The Cone Effect):

    研究发现,深层 Transformer 的 Token Embedding 往往存在各向异性(Anisotropy) 。即所有的 Token 向量都挤在向量空间的一个狭窄圆锥体内。

  • 余弦相似度的失效:

    当所有向量都挤在一起时,它们之间的余弦相似度都很高。为了区分它们,模型必须极度依赖局部的最近邻关系。

    当出现重复时,重复的 token 在空间中是重叠的。模型为了“省力”(最小化损失),倾向于直接复制最近的强信号,而不是去从长距离上下文中推理复杂的语义。

所以,复读机不是“由于矩阵缺秩导致无法访问历史信息”,而是“由于概率分布的峰值化(Peaking)和自回归的惯性,导致模型主动选择了重复”。

这也是为什么在推理阶段,我们必须引入工程手段来打破这个循环,例如:

  1. Temperature > 0: 强制展平概率分布,增加随机性。
  2. Repetition Penalty(重复惩罚): 人为降低已出现 token 的 Logits。
  3. Top-P / Top-K Sampling: 截断长尾,但也防止单纯的 Argmax 陷入局部最优。

总结

所以,为什么现在的LLM都是Decoder-only架构?

  1. 因果律(Causal Mask)  导致了 数学上的下三角结构
  2. 下三角结构 + Softmax 保证了 矩阵永远满秩
  3. 满秩 确保了 特征空间的维度不坍塌,从而让模型能够承载更大规模的知识

Encoder 的双向注意力虽然在“获取上下文”上更直观,但由于缺乏结构性约束,在大规模参数下容易陷入低秩陷阱(过平滑)。而 Decoder-only 架构通过“舍弃”未来的视野,换取了数学上更稳健、秩更高、表达力更持久的表征空间。 这正是为什么 GPT 系列(Decoder-only)能扩展到千亿、万亿参数而不断裂,而 BERT 系列(Encoder)在扩大规模时边际效应递减更快的原因之一。


了解完低秩坍塌的问题之后,你是否疑惑,为什么低秩矩阵会出现坍塌,但是LoRA(低秩矩阵)在大模型微调中还那么火呢?请参考【LLM】为什么LoRA中低秩矩阵是一个优势?