剖析 Transformer 核心:解码器层(Decoder Layer)内部结构与生成逻辑

举报
yd_299290000 发表于 2026/10/23 10:13:27 2026/10/23
【摘要】 剖析 Transformer 核心:解码器层(Decoder Layer)内部结构与生成逻辑如果说编码器(Encoder)的目标是提炼并理解源语言序列的全局语义,那么解码器(Decoder)的核心职责就是基于已生成的历史信息与编码器输出的源序列特征,以自回归(Autoregressive)的方式逐词预测未来的内容。标准 Transformer 解码器由 NNN 个结构相同的解码器层(Dec...

剖析 Transformer 核心:解码器层(Decoder Layer)内部结构与生成逻辑

如果说编码器(Encoder)的目标是提炼并理解源语言序列的全局语义,那么解码器(Decoder)的核心职责就是基于已生成的历史信息与编码器输出的源序列特征,以自回归(Autoregressive)的方式逐词预测未来的内容。

标准 Transformer 解码器由 NN 个结构相同的解码器层(Decoder Layer,论文默认 N=6N=6)垂直堆叠而成。为了兼顾历史信息的因果约束与跨序列的语义融合,解码器层在编码器的基础上进行了关键升级:扩展为三层级联子结构,并引入了因果掩码与交叉注意力机制。


一、 解码器层的宏观结构与数据流

单个解码器层接收两个截然不同的输入流:

  1. 来自目标序列(Target Sequence)的输入: 包含历史已生成词向量的主干张量 Xdec∈RB×Ltgt×dmodelX_{\text{dec}} \in \mathbb{R}^{B \times L_{\text{tgt}} \times d_{\text{model}}}。
  2. 来自编码器(Encoder)的输出: 包含源语言完整全局特征的记忆张量 Xenc∈RB×Lsrc×dmodelX_{\text{enc}} \in \mathbb{R}^{B \times L_{\text{src}} \times d_{\text{model}}}。

每个解码器层按顺序串联了三个子层:

                    解码器输入 X_dec (B, L_tgt, d_model)
                               │
               ┌───────────────┴───────────────┐
               │                               ▼
               │                [ 1. 掩码多头自注意力 (Masked MHA) ] <── 因果掩码 (Causal Mask)
               │                               │
               ▼                               ▼
        ┌─────────────┐                 ┌─────────────┐
        │  残差连接   │                 │  子层输出   │
        └──────┬──────┘                 └──────┬──────┘
               │                               │
               └───────────────► ◄─────────────┘ (Add & Norm)
                               │
               ┌───────────────┴───────────────┐
               │                               ▼
               │                [ 2. 交叉注意力 (Cross-Attention) ] <── K, V 来自 Encoder 输出
               │                               │
               ▼                               ▼
        ┌─────────────┐                 ┌─────────────┐
        │  残差连接   │                 │  子层输出   │
        └──────┬──────┘                 └──────┬──────┘
               │                               │
               └───────────────► ◄─────────────┘ (Add & Norm)
                               │
               ┌───────────────┴───────────────┐
               │                               ▼
               │                [ 3. 前馈全连接网络 (FFN) ]
               │                               │
               ▼                               ▼
        ┌─────────────┐                 ┌─────────────┐
        │  残差连接   │                 │  子层输出   │
        └──────┬──────┘                 └──────┬──────┘
               │                               │
               └───────────────► ◄─────────────┘ (Add & Norm)
                               │
                    解码器输出 Y_dec (B, L_tgt, d_model)

每个子层同样被残差连接(Residual Connection)与层归一化(Layer Normalization)严密包裹。


二、 子层一:掩码多头自注意力(Masked Multi-Head Self-Attention)

自注意力机制默认具备全局可见性,但语言生成是一个单向因果过程——在预测当前词 tt 时,模型绝不能提前“偷看”到时刻 t+1t+1 及之后的未来词,否则会导致训练时的标签泄露(Data Leakage)。

1. 因果掩码(Causal Mask)的数学构造

为了消除时间机器效应,解码器的自注意力层引入了一个下三角掩码矩阵 Mcausal∈RLtgt×LtgtM_{\text{causal}} \in \mathbb{R}^{L_{\text{tgt}} \times L_{\text{tgt}}}:

Mij={0,i≥j(允许关注历史与当前位置)−∞,i<j(禁止关注未来位置)M_{ij} = \begin{cases} 0, & i \ge j \quad (\text{允许关注历史与当前位置}) \\ -\infty, & i < j \quad (\text{禁止关注未来位置}) \end{cases}

掩码直接加在未归一化的注意力得分矩阵(Logits)上:

Attentionmasked(Q,K,V)=softmax(QKTdk+Mcausal)V\text{Attention}_{\text{masked}}(Q, K, V) = \text{softmax}\left(\frac{QK^T}{\sqrt{d_k}} + M_{\text{causal}}\right)V

通过将未来位置的值设为负无穷大(在工程实现中通常为 −109-10^9),经过 Softmax 指数运算后,e−∞=0e^{-\infty} = 0,使得未来词的注意力权重精确为 0,彻底切断反向信息流。

2. 训练与推理的差异:Teacher Forcing vs 自回归

  • 训练阶段(并行加速): 采用 Teacher Forcing 策略。虽然因果掩码阻断了未来信息,但由于真实的目标句子是已知的,整个序列可以一次性批量输入,注意力矩阵在掩码约束下实现全并行计算,无需按时间步循环。
  • 推理阶段(自回归串行): 模型没有“未来的答案”。只能将已生成的 Token 逐个作为输入,每次前向传播仅生成一个新 Token,追加到序列末尾后,再进行下一轮迭代。

三、 子层二:交叉注意力(Cross-Attention)

交叉注意力(又称 Encoder-Decoder Attention)是整个架构中源语言与目标语言进行语义对齐的枢纽。

1. 数据的异源交互(Q 来自 Decoder,K/V 来自 Encoder)

交叉注意力层的计算依然遵循缩放点积公式,但其输入张量的来源发生了根本分化:

  • QQ(Query)由解码器上一层的输出投影而来: Q=Xdec_midWQQ = X_{\text{dec\_mid}} W_Q
    Query 携带的是当前已经生成的目标语言上下文(例如:“我想要知道……”)。
  • KK(Key)由编码器最终输出投影而来: K=XencWKK = X_{\text{enc}} W_K
    Key 充当源语言所有词的特征索引标签。
  • VV(Value)由编码器最终输出投影而来: V=XencWVV = X_{\text{enc}} W_V
    Value 包含源语言序列中各词最深层的语义实体。

Cross-Attention=softmax(Xdec_midWQ(XencWK)Tdk)(XencWV)\text{Cross-Attention} = \text{softmax}\left(\frac{X_{\text{dec\_mid}} W_Q (X_{\text{enc}} W_K)^T}{\sqrt{d_k}}\right)(X_{\text{enc}} W_V)

2. 机制本质与尺寸匹配

在交叉注意力中,相关性矩阵的尺寸为 (B,Ltgt,Lsrc)(B, L_{\text{tgt}}, L_{\text{src}})。矩阵的每一行对应一个正在生成的目标词,每一列对应源句子中的输入词。这意味着:目标端的每一个 Token 都在自适应地打量源端的每一个 Token,以此决定当前应该翻译、总结或引申原文的哪一部分。

注意:交叉注意力不需要因果掩码,因为源语言序列在生成之前就已经全部已知,解码器理应且必须拥有对整篇输入文本的全局检索权限。


四、 子层三:基于位置的前馈全连接网络(FFN)

交叉注意力完成源文本与目标文本的动态融合后,输出张量进入前馈全连接网络。该结构与编码器中的 FFN 完全一致:

FFN(x)=max⁡(0,xW1+b1)W2+b2\text{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2

每个位置独立进行升维映射(从 dmodeld_{\text{model}} 升至 4×dmodel4 \times d_{\text{model}})与非线性激活,再降维投影回 dmodeld_{\text{model}}。该层负责对融合后的特征进行通道级重组与知识激活,为最后输出词表概率分布准备充分的判别性表征。


五、 推理核心工程:KV Cache 机制

在自回归解码过程中,如果每生成一个新 Token 都将整个历史序列重新输入模型,历史 Token 的 Q,K,VQ, K, V 投影以及注意力点积会被重复计算 LL 次,使得生成长度为 LL 的序列总时间复杂度飙升至 O(L3)O(L^3)(单步从 O(1)O(1) 恶化至 O(L2)O(L^2))。

KV Cache(键值缓存) 是现代解码器推理的标配优化技术:

  1. 核心观察: 预测时刻 tt 的新词时,只有新词自身需要作为 Query 去匹配过往所有词;而历史已生成的词,其 Key 和 Value 向量在后续时间步完全保持不变。
  2. 缓存机制: 在每次前向传播中,仅将当前最新的单个 Token 输入网络,计算出其对应的 qt,kt,vtq_t, k_t, v_t。将 kt,vtk_t, v_t 拼接到显存中常驻的历史缓存矩阵 [Kpast;kt][K_{\text{past}}; k_t] 和 [Vpast;vt][V_{\text{past}}; v_t] 中。
  3. 计算复杂度下降: 每次前向计算的复杂度直接降为 O(L)O(L),使得整句生成的计算量回落至 O(L2)O(L^2),极大提升了推理吞吐。

六、 PyTorch 模块化代码实现

以下代码实现了工业界通用的 Pre-LN 结构解码器层 TransformerDecoderLayer,并完整封装了因果掩码生成与多层堆叠的 TransformerDecoder:

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

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model: int, num_heads: int, dropout: float = 0.1):
        super().__init__()
        assert d_model % num_heads == 0
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_k = d_model // num_heads

        self.w_q = nn.Linear(d_model, d_model)
        self.w_k = nn.Linear(d_model, d_model)
        self.w_v = nn.Linear(d_model, d_model)
        self.w_o = nn.Linear(d_model, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, mask: torch.Tensor = None) -> torch.Tensor:
        B, L_q, _ = q.shape
        L_k = k.size(1)

        # 线性映射并切分多头
        Q = self.w_q(q).view(B, L_q, self.num_heads, self.d_k).transpose(1, 2)
        K = self.w_k(k).view(B, L_k, self.num_heads, self.d_k).transpose(1, 2)
        V = self.w_v(v).view(B, L_k, self.num_heads, self.d_k).transpose(1, 2)

        # 缩放点积
        scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k)
        
        # 掩码填充
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)

        attn_weights = self.dropout(torch.softmax(scores, dim=-1))
        context = torch.matmul(attn_weights, V)
        context = context.transpose(1, 2).contiguous().view(B, L_q, self.d_model)
        return self.w_o(context)


class TransformerDecoderLayer(nn.Module):
    """采用 Pre-LN 结构的单层 Decoder"""
    def __init__(self, d_model: int, num_heads: int, d_ff: int, dropout: float = 0.1):
        super().__init__()
        # 1. 掩码自注意力
        self.self_attn = MultiHeadAttention(d_model, num_heads, dropout)
        self.norm1 = nn.LayerNorm(d_model)

        # 2. 交叉注意力
        self.cross_attn = MultiHeadAttention(d_model, num_heads, dropout)
        self.norm2 = nn.LayerNorm(d_model)

        # 3. 前馈网络
        self.ffn = nn.Sequential(
            nn.Linear(d_model, d_ff),
            nn.GELU(),
            nn.Dropout(dropout),
            nn.Linear(d_ff, d_model)
        )
        self.norm3 = nn.LayerNorm(d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x: torch.Tensor, enc_output: torch.Tensor, 
                tgt_mask: torch.Tensor = None, src_mask: torch.Tensor = None) -> torch.Tensor:
        """
        x: 解码器自身特征 (B, L_tgt, d_model)
        enc_output: 编码器最终输出特征 (B, L_src, d_model)
        tgt_mask: 因果掩码 + 目标 padding 掩码 (B, 1, L_tgt, L_tgt)
        src_mask: 源端 padding 掩码 (B, 1, 1, L_src)
        """
        # 子层 1:掩码自注意力 (Pre-LN)
        norm_x = self.norm1(x)
        self_attn_out = self.self_attn(norm_x, norm_x, norm_x, mask=tgt_mask)
        x = x + self.dropout(self_attn_out)

        # 子层 2:交叉注意力 (Q 来自解码器,K/V 来自编码器)
        norm_x = self.norm2(x)
        cross_attn_out = self.cross_attn(norm_x, enc_output, enc_output, mask=src_mask)
        x = x + self.dropout(cross_attn_out)

        # 子层 3:前馈全连接
        norm_x = self.norm3(x)
        ffn_out = self.ffn(norm_x)
        x = x + self.dropout(ffn_out)

        return x


class TransformerDecoder(nn.Module):
    def __init__(self, num_layers: int, d_model: int, num_heads: int, d_ff: int, dropout: float = 0.1):
        super().__init__()
        self.layers = nn.ModuleList([
            TransformerDecoderLayer(d_model, num_heads, d_ff, dropout)
            for _ in range(num_layers)
        ])
        self.final_norm = nn.LayerNorm(d_model)

    def forward(self, x: torch.Tensor, enc_output: torch.Tensor, 
                tgt_mask: torch.Tensor = None, src_mask: torch.Tensor = None) -> torch.Tensor:
        for layer in self.layers:
            x = layer(x, enc_output, tgt_mask, src_mask)
        return self.final_norm(x)


# ---------------- 验证前向传播与数据流 ----------------
if __name__ == "__main__":
    B = 2
    L_src = 10
    L_tgt = 6
    d_model = 64
    num_heads = 4
    d_ff = 256
    num_layers = 4

    # 1. 模拟输入数据
    dummy_tgt = torch.randn(B, L_tgt, d_model)       # 解码器目标输入
    dummy_enc_out = torch.randn(B, L_src, d_model)   # 编码器产出特征

    # 2. 构造因果掩码 (下三角矩阵: 1 保留,0 屏蔽)
    # shape: (1, 1, L_tgt, L_tgt)
    causal_mask = torch.tril(torch.ones(L_tgt, L_tgt)).unsqueeze(0).unsqueeze(0)

    # 3. 实例化解码器并执行前向
    decoder = TransformerDecoder(num_layers, d_model, num_heads, d_ff)
    dec_out = decoder(dummy_tgt, dummy_enc_out, tgt_mask=causal_mask)

    print("Decoder 输入尺寸:", dummy_tgt.shape)    # torch.Size([2, 6, 64])
    print("Encoder 特征尺寸:", dummy_enc_out.shape) # torch.Size([2, 10, 64])
    print("Decoder 输出尺寸:", dec_out.shape)       # torch.Size([2, 6, 64])


七、 演化思考:为什么当代大模型全面倒向 Decoder-Only?

在 2017 年原生 Transformer 时代,经典的翻译模型采用 Encoder-Decoder;然而在当今大语言模型(GPT-4、LLaMA、DeepSeek 等)的版图中,Decoder-Only(仅解码器)架构占据了绝对主导地位。

架构维度 传统 Encoder-Decoder(如 T5, BART) 现代 Decoder-Only(如 GPT, LLaMA)
子层组成 掩码自注意力 + 交叉注意力 + FFN 仅保留掩码自注意力 + FFN(完全去除交叉注意力)
参数利用率 编码器参数在解码阶段只计算一次,随后进入闲置状态。 每一层参数、每一个 Token 在自回归过程中都被充分复用。
任务泛化性 需要定义源输入与目标输出的边界(如翻译、摘要)。 将理解、问答、推理统统统一为自回归下一个 Token 预测。
KV Cache 复杂度 需同时维护解码器内部与交叉注意力两套 Cache。 单一线性自回归,KV Cache 结构统一,极易通过 PagedAttention 等算子加速。

现代大模型并非抛弃了解码器,而是将理解能力完全融入到了自回归解码器的因果注意力流中。解码器层凭借其精密的因果约束设计与强大的时序推演能力,不仅构成了原生 Transformer 完成跨语言映射的终点,更成为了当代大语言模型生成整个数字世界的基本计算单元。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。