图 1:本文封面,三张掩码矩阵看懂三种架构的信息流方向。
先说明属性:这是一篇经典题解析,题目出自本地历史面试资料整理,与任何公司的当前招聘无关。「prefix Decoder、causal Decoder、Encoder-Decoder 有什么区别」是大模型面试里最经典的架构题,考的不是背模型名单,而是你能不能把三种架构的差异收拢到一件事上——注意力掩码。三种架构用的是同一套 Transformer 积木,差别只在「每个位置允许看见谁」。
一句话结论:三种架构的本质差异就是注意力掩码的取值模式——causal Decoder 是下三角单向掩码,prefix Decoder 是「前缀双向 + 续写单向」的混合掩码,Encoder-Decoder 是编码器全双向、解码器单向、再加一条全通的交叉注意力;掩码决定信息流方向,信息流方向决定训练目标,训练目标决定适用任务。
先统一度量衡:掩码是怎么实现的。
自注意力的核心是 softmax(QKᵀ/√d)。掩码不靠删掉连接,而是在做 softmax 之前给注意力分数矩阵加上一个掩码矩阵 M:允许看见的位置加 0,不允许看见的位置加负无穷。负无穷经过 softmax 之后权重恒等于 0,被遮住的位置对输出没有任何贡献——信息就被精确地掐断了。所以「架构之争」翻译过来就是「M 矩阵的 0 和负无穷怎么摆」。
第一种:causal Decoder,下三角掩码,一路向左看。
第 i 个位置只能看见第 1 到第 i 个位置,右边一律屏蔽。训练目标是自回归的「预测下一个词」:每个位置的输出都以它左边的全部内容为条件,损失在所有位置上都计算。这套因果约束保证了训练与推理完全一致——模型生成时本来就是一个词一个词往外吐,永远看不到未来。代表模型是 GPT 系、LLaMA 系。它天生适合开放式生成与对话:续写、聊天、代码补全,本质都是「给定前文生成后文」。
第二种:prefix Decoder,前缀双向、续写单向。
把输入切成「前缀」和「续写」两段:前缀内部的词互相可见(双向注意力,类似 BERT 的理解方式),续写部分仍遵守单向因果,只能看前缀和已生成的部分。损失只计算在续写段上。直觉很直白:读题的时候要读透(双向),答题的时候不能作弊看后面(单向)。代表是 GLM 系,2021 年的 GLM 论文用「自回归空白填充」把这套掩码统一成一个预训练框架,同一模型既能做理解任务也能做条件生成。可以把它看成 causal Decoder 和 Encoder-Decoder 的折中:比纯单向更充分地消化输入,又不必维护两个组件。
第三种:Encoder-Decoder,两个组件、三种掩码。
编码器对输入做全双向自注意力,把整段输入消化成一组上下文表示;解码器自注意力是单向的,逐词生成输出;两者之间靠交叉注意力连接——解码器的每个位置拿着自己的查询去「查阅」编码器的全部输出,这条通路全通。训练目标是序列到序列的「整句映射整句」,比如 T5 把一切任务统一成 text-to-text 格式,预训练采用去噪式的 span 还原。代表是 T5、BART。输入理解充分、输出又受因果约束,所以翻译、摘要这类「先吃透全文、再定向改写」的任务是它的主场。
用代码把三张掩码摆出来(Node.js 22.15.0,node masks.js):分别构造三种掩码矩阵,并验证「加负无穷再过 softmax」确实把屏蔽位置压成 0:
const pl = 2; // prefix 长度
const causal = Array.from({length: n}, (_, i) =>
Array.from({length: n}, (_, j) => (j <= i ? 1 : 0)));
const prefix = Array.from({length: n}, (_, i) =>
Array.from({length: n}, (_, j) => (j <= Math.max(i, pl - 1) ? 1 : 0)));
// 掩码实现:注意力分数加 -∞ 后 softmax 恒为 0
const masked = scores.map((s, i) => (mask[i] ? s : -Infinity));
实测输出(■ 可见、· 屏蔽):
causal Decoder(5 词,下三角)
■ · · · ·
■ ■ · · ·
■ ■ ■ · ·
■ ■ ■ ■ ·
■ ■ ■ ■ ■
prefix Decoder(前 2 词为前缀,双向)
■ ■ · · ·
■ ■ · · ·
■ ■ ■ · ·
■ ■ ■ ■ ·
■ ■ ■ ■ ■
未掩码 softmax: 0.2934, 0.0536, 0.6530
掩码后 softmax: 0.8455, 0.1545, 0.0000
最后两行是关键:未掩码时第三个位置分到 0.6530 的权重,掩码后精确变成 0.0000,注意力重新归一到可见位置。Encoder-Decoder 的三块矩阵(编码器全亮方阵、解码器下三角、交叉注意力全通)在概念图里画得比文字更直观。
图 2:三种架构的掩码矩阵、训练目标与适用任务对照。
为什么今天的生成式主流收敛到了 causal Decoder?
这不是因果掩码「更强」,而是它更省、更通用。第一,预训练与下游用法完全同构:无标注语料拿来就能做「预测下一个词」,训完直接零样本生成,不需要像 Encoder-Decoder 那样靠多任务微调激发性能;第二,训练效率高,每个位置都算损失,而且单一组件的工程实现、KV cache 复用都最简单;第三,纯生成的任务里双向注意力本来就没有实质收益——「输入理解」完全可以用「把输入塞进前缀」实现。Encoder-Decoder 的双组件结构还意味着更多参数与更复杂的推理流水线。于是在「生成是主战场」的大模型时代,causal Decoder 成了默认答案,prefix Decoder 作为折中在 GLM 系里保留下来,Encoder-Decoder 则退守翻译、摘要等定向改写场景。
面试现场的答题结构。
先给结论「差别全在注意力掩码」,顺手讲掩码的实现原理(加负无穷再过 softmax)。然后按掩码模式逐个过:causal 下三角、prefix 混合、Encoder-Decoder 三块掩码(编码器双向、解码器单向、交叉全通),每个都带上训练目标与代表任务——自回归续写对开放生成、空白填充对理解与生成混合、序列到序列对翻译摘要。追问「为什么现在主流是 causal Decoder」时答三点:训推一致零样本强、单组件效率高、生成任务里双向注意力收益有限。有余力再补一句 GLM 的空白填充如何统一三类任务,展示你读过代表性论文。你在实际项目里用过 Encoder-Decoder 架构做生成任务吗?效果跟 Decoder-only 比怎么样?欢迎聊聊。