本节摘要:编码器由输入预处理(词嵌入加位置编码)与六个结构相同的编码器层堆叠而成,每层含多头自注意力与逐位置前馈两个子层,每个子层被"残差相加加层归一化"包裹。数据从嵌入出发逐层精炼,顶层输出是与输入等长的上下文表示序列,供解码器交叉注意力取用。本节追踪单层内的完整数据路径,解释"同一个词在不同句中得到不同表示"的机理、层数与信息提炼的关系,以及编码器输出的去向。
阅读完本节,你应当能够:
编码器塔本身很简单,复杂度都在塔外与层内。塔外两道工序:词嵌入与位置编码。每个词元编号查嵌入表得到模型维度的向量——这张表是可学习参数,语义相近的词在训练中被推到相近的位置;随后按第 2.3 节的方案生成位置编码并逐元素相加。走完这两步,每个词的表示同时携带"我是什么词"与"我在第几位",序列变成一个 n 行 d_model 列的矩阵,正式入塔。
一个常被追问的问题:嵌入层的数值要不要缩放?原论文把嵌入乘以模型维度的平方根,使其数值尺度与位置编码匹配,防止相加时一方淹没另一方。这类细节属于"不致命但影响训练平稳度"的工程项,复现时值得照做。
六层编码器层结构完全相同、参数各自独立。单层内的流水线分两段。
第一段,多头自注意力。输入矩阵经三组投影生成查询、键、值,八头各自在子空间做缩放点积注意力,拼接后输出投影(细节即 2.1 与 2.2 节)。这一段让每个位置吸收全句信息。输出与输入相加(残差)、层归一化,得到第一段的规范输出。
第二段,逐位置前馈网络。每个位置的向量独立通过"升维、激活、降维"的两层网络(即 2.4 节),对吸收来的信息做非线性消化。输出再次与第一段输出相加、归一化,成为本层最终输出。
形状全程不变:n 行 d_model 列进,n 行 d_model 列出。这种"形状守恒"是残差连接与整个堆叠设计的前提——任何一层都可以无痛插拔或增删。

编码器最重要的性质,是产出"上下文相关"的表示。同一个"苹果",在"苹果发布了新手机"与"我吃了一个苹果"里,经过第一层自注意力后就开始分道扬镳——前者的表示吸收了"发布""手机"的信息,偏向公司义;后者吸收了"吃",偏向水果义。层数越深,分化越细:浅层多处理词法与局部搭配,中层浮现句法结构,深层编码语义与篇章关系。到顶层,每个位置的向量已经是"这个词在这句话里到底什么意思"的浓缩答案。
这个性质直接回答了词向量的老问题。Word2Vec 时代每个词只有一个静态向量,多义词只能取各义的平均,"苹果"的向量悬在公司与水果之间。编码器的动态表示让词义随语境流动,是深度上下文表示相对静态词向量的本质跃迁——也是 BERT 一类模型做理解任务表示能力强的底层原因。
层与层之间不是简单的重复劳动,而是分工精炼。可以粗略理解为:第一层把每个词"沾上"邻居的气息,中间层把句法骨架搭起来,深层把语义关系、指代、语气这些抽象结构压进表示。六层不是魔法数字——原论文的机器翻译任务上六层是效果与成本的平衡点;后续模型从几层到近百层都有,层数与任务复杂度、数据规模同步放大。
编码器顶层输出有明确去处:作为解码器每一层交叉注意力的键与值。注意是"每一层"——解码器六层,每层的交叉注意力都查阅同一份编码器顶层输出,而不是各层对接编码器的对应层。这种"顶层对接、层层可查"的设计让信息接口简单统一。
一个有用的工程观察:由于编码器输出在推理时只需计算一次,之后整个逐词生成过程反复复用,翻译长句时编码器一次前向的开销被几十步解码摊薄。反过来,纯理解任务(分类、抽取)只需要编码器,没有解码循环——这也是 BERT 类模型推理成本可控的原因。
⚠️ 排查提示:复现时两个高频错误。一是掩码遗漏:批处理中同一批序列长短不齐,短序列补的填充位若不掩掉,编码器的自注意力会把无意义的填充向量当真实内容加权,长句短句混批时性能悄悄掉一截——需要在打分阶段把填充位的分数同样置为极大负数。二是层数参数共享搞反:六层参数应当各自独立,若误设为跨层共享,模型退化为深度循环的伪多层,容量骤降还不易察觉。
| 部件 | 参数共享范围 | 说明 |
|---|---|---|
| 词嵌入表 | 全模型共享(与输出投影可绑定) | 同词同向量 |
| 各编码器层 | 层间独立 | 六套参数各自学习 |
| 层内前馈网络 | 跨位置共享 | 同层所有位置同权重 |
| 注意力投影 | 层内统一 | 各头独立投影到子空间 |
💡 理解检验:解释"为什么编码器输出的每个向量与输入位置一一对应,却又不止是那个词的向量"。答案落在自注意力的加权混合与逐层精炼上——对应位置承载,全句信息供料。
六层不是六次同样的重复。对训练好的模型做逐层探测(例如逐层探针与注意力可视化),能看到清晰的分工轮廓:第一二层的注意力头多聚焦局部相邻与词法搭配;中间层开始浮现句法结构——依存关系、短语边界在注意力模式里成形;最上两层承载更抽象的语义关系与指代信息。信息逐层"精炼"的比喻由此有了实证支撑:每一层消费上一层的产出,产出更抽象的表示。
这个分工对工程也有指导意义。迁移学习时冻结浅层、只调深层常能以更小成本保住大部分效果——浅层学的是通用语言属性,跨任务稳定;深层与任务目标绑定更紧。出错分析时同理:低级错误(搭配混乱)查浅层相关配置,高级错误(语义颠倒)把注意力放在深层与数据分布上。
完整的两塔机器翻译只是一种用法。其一,理解任务:只用编码器,塔顶表示接池化或特殊符位置后进分类头,BERT 路线的骨架。其二,检索与匹配:两个句子分别过编码器,用表示的相似度排序,向量检索系统的核心组件。其三,多模态编码:把图像切块、音频分帧后当作"词元"喂进编码器,同一架构处理不同模态——Vision Transformer 的思路。三种用法共享本节讲的全部内部机制,差别只在塔外怎么接。
⚠️ 再补一条复现细节:批内填充位的掩码要与损失屏蔽配套——注意力掩码只管"不看",损失屏蔽管"不计分",两处常写在不同代码路径里,改了一处忘了另一处是高频事故。建议把两处的掩码张量同源生成,一处定义两处引用。
💡 一分钟的自我检测:向自己解释"为什么编码器顶层输出的第 k 个向量,既是第 k 个词的表示、又包含全句信息"。答案要点:残差流保位置对应、注意力混合全句内容、逐层精炼两者叠加。能组织出这三层因果,本节即可收官。
能,而且这正是它最大的应用外溢。塔顶每个位置的向量是深度上下文化的词义表示,整句表示可由池化(平均或取特殊符位置)得到,广泛用于检索、聚类、语义匹配。与静态词向量相比,它的区分度来自双向上下文——同一个词在褒贬两句里的池化向量能拉开距离。工程上这类表示已是向量检索系统的标配底座,理解了编码器的内部机制,排查"检索不准"的问题时才知道该从表示质量还是从索引参数下手。
编码器只会读不会写。下一节看解码器——多一个子层的塔,如何带着因果约束逐词写出目标序列。
问:BERT 为什么只要编码器,不要解码器?
答:任务性质决定的。BERT 做的是理解类任务(分类、抽取、句间关系),只需要把源句编码成富含上下文的表示,不涉及逐词生成——双向可见的编码器恰好是理解的最优形态,解码器的因果限制反而是累赘。反过来,生成任务需要解码器的自回归结构。编码器与解码器的分家(BERT 与 GPT 两条路线)是「按任务取半边」的经典示范,也是理解当代模型谱系的钥匙。
问:编码器一层里子层的顺序可以换吗?
答:子层逻辑顺序(注意力→前馈)基本是共识,因为「先交流再加工」的次序有道理;但归一化放层前还是层后(Pre-Norm 与 Post-Norm 之争)正是 2.5 节讨论过的可换项。架构图上看似固定的连线,其实每个位置的设计都有理由与替代方案——能说出「为什么这样、还能怎样、换了会怎样」,才算真读懂了架构。