Encoder/Decoder 与 GPT:结构和任务差异
这一篇把几种常见的 Transformer 结构放在一起看:Encoder-Decoder、纯 Encoder(BERT 一类)和纯 Decoder(GPT 一类),以及它们各自适合做什么。
原始 Transformer:Encoder-Decoder 结构
最初的 Transformer(机器翻译场景)是一个标准的 Encoder-Decoder:
- Encoder:读入源语言句子,输出一串上下文表示;
- Decoder:在看到 Encoder 输出的同时,自回归地生成目标语言。
可以粗略画成这样:
1源语言 x1 x2 x3 ... xN ──> Encoder ──> 上下文表示(序列)
2 │
3 ▼
4目标 y1 y2 ... y_{t-1} ──> Decoder ──> 预测 y_t
Encoder 和 Decoder 内部都堆了很多层 Transformer Block,不过两边的注意力结构略有不同:
- Encoder 内部是 Self-Attention + FFN;
- Decoder 内部除了 Self-Attention + FFN 之外,还多了一层 Encoder-Decoder Attention:
- Self-Attention:在“目标语言的前缀”之间做注意力;
- Encoder-Decoder Attention:拿当前 Decoder 表示去看 Encoder 输出的整串上下文。
这种结构特别适合:
- 源→目标的序列到序列任务:机器翻译、摘要生成、代码翻译等;
- 输入和输出是两种不同“语言”(或模态)的时候。
Encoder-only:BERT 一类偏“理解”的模型
如果只保留 Encoder,把 Decoder 整块拿掉,就得到了一类典型的 Encoder-only 模型,代表是 BERT。
特点是:
- 输入一串 token,输出一串“上下文感知”的向量;
- 每个位置的向量同时看到左边和右边(双向 Self-Attention);
- 常用的训练目标是 Masked Language Modeling:随机挖掉一部分 token,让模型去预测。
这类模型特别适合做:
- 文本分类(情感分析、意图识别等);
- 文本匹配(相似度、问答匹配);
- 序列标注(命名实体识别、分词等);
- 检索场景里的 embedding 编码器(把文本编码成向量)。
在工程上,你通常会这样用 Encoder-only 模型:
- 把整句(或一对句子)丢进去;
- 拿某个位置的向量(比如
[CLS])或者对整串做池化,当作句向量; - 再接一两层全连接网络做下游任务。
Decoder-only:GPT 一类偏“生成”的模型
如果只保留 Decoder,并且把 Encoder-Decoder Attention 去掉,只保留 Self-Attention + FFN,就得到了 Decoder-only 结构,代表就是 GPT 系列。
关键区别在于:
- 自注意力里使用了因果 Mask(Causal Mask):
- 位置 (i) 只能看到 (0 \dots i) 的 token,不能看到右边未来的位置;
- 这样才能自回归地预测“下一个 token”。
训练目标很简单:
给模型一段文本 ((x_1, x_2, \dots, x_T));
让它在每个位置预测下一个 token:
$$ \mathcal{L} = -\sum_{t=1}^{T} \log p(x_{t} \mid x_1, \dots, x_{t-1}) $$
推理时就是经典的“续写”过程:
- 提供一个 prompt,当作前缀;
- 模型预测下一个 token,把它拼回去;
- 重复上一步,直到结束。
这种结构非常擅长:
- 文本生成(续写、摘要、对话);
- 代码补全;
- 任何可以转成“预测下一个 token”的任务。
三类结构在任务上的差异(工程视角)
从工程使用角度,可以简单归纳成:
Encoder-only:适合做“理解类”任务
- 输入一段文本,输出一个向量表示或标签;
- 典型:BERT、RoBERTa、DeBERTa 等。
Encoder-Decoder:适合做“强结构的输入→输出转换”
- 输入是一种结构化/序列化信息,输出是另一种;
- 典型:原版 Transformer 做翻译,T5 做各种文本到文本任务。
Decoder-only:适合做“一切都当成生成”的任务
- 通过精心设计的 prompt,把各种任务都包装成“预测下一个 token”;
- 典型:GPT-2/3/4、一众 code LLM 等。
在很多实际系统里,会把不同结构的模型混用:
- 用 Encoder-only 模型做语义索引(生成 embedding),再用 Decoder-only 模型做生成回答;
- 用 Encoder-Decoder 模型做需要强约束的生成(比如格式化输出),用 Decoder-only 模型做开放式对话。
为什么现在 Decoder-only 这么流行?
虽然三个结构各有适用场景,但在“大模型 + 通用任务”这个方向上,Decoder-only 几乎成了主流选择,原因大致有:
训练目标简单统一:
- 只要是 token 序列,都可以用“预测下一个 token”来统一建模;
- 不需要为每种任务单独设计损失。
推理接口统一:
- 无论是对话、写代码、翻译还是摘要,都是“喂 prompt → 读输出”;
- 对于工程系统来说极其好集成。
上下文学习(In-Context Learning)表现好:
- 大模型可以直接从 prompt 里的示例“学会”一个任务,不需要显式微调;
- 这在 Encoder-only / Encoder-Decoder 里不是不可能,但体验和生态都没这么成熟。
不过在一些强调结构化输出、对齐严格标签的任务上,Encoder-only 和 Encoder-Decoder 依然非常有用,尤其是在资源有限、只需要做少量特定任务的场景里。