多头注意力、位置编码与一个完整的 Transformer Block
讲清多头为什么要「分头再合并」、注意力本身为什么不含位置信息而需要位置编码,最后把多头注意力、残差连接、层归一化、前馈网络按数据流组装成一个标准 Block
- 理解多头注意力的分工与拼接机制
- 知道注意力为何对词序无感,以及位置编码的作用与常见方案
- 掌握残差连接、层归一化、前馈网络各自的职责
- 能默画一个标准 Transformer Block 的内部数据流
多头:让模型在不同「子空间」同时关注
单头注意力只能学出一种关注模式。多头注意力把 Q/K/V 切成若干组(若干个「头」),每个头在自己的低维子空间里独立做一次注意力:有的头关注语法搭配、有的关注指代关系、有的关注邻近词。最后把各头输出拼接,再用一个线性层融合。
把 Q/K/V 按维度切成 h 个头
每个头独立做缩放点积注意力
各自学不同关系,互不干扰
拼接所有头的输出
输出线性层融合为统一表示
多头在总计算量上与一个同等维度的单头相近,却让模型拥有多视角的关系建模能力,是 Transformer 表达力的关键来源之一。
为什么「分头」比一个大注意力更强?因为一个注意力头在给某个词算权重时,只能套用一种相似度度量,学到一种关联模式。但一句话里需要同时建模的关系是多方面的:它既需要知道「它」指代前面哪个名词(指代关系),又需要知道动词和主语的语法搭配,还需要捕捉相邻词的局部约束。把表示拆成几个子空间、让每个头各自学一种关系,最后再汇合,模型就能同时持有多重视角。这就像开会:一个人既当记录员又当计时员又当联络员,往往样样稀松;不如把任务拆给几个专人,最后汇总。工程上观察到,不同头确实会自发地关注不同类型的关系——有的盯着相邻词,有的盯着远距离指代。多头不是玄学,是在有限算力内并行建模多种关系的高性价比设计。
位置编码:注意力本身是「不认顺序」的
自注意力对一组词做的是两两加权求和,它本质上是集合操作:打乱词的顺序,输出只是跟着换序、内容不变——也就是说它分不清「狗咬人」和「人咬狗」。必须显式注入位置信息。
这个「集合性质」值得再想透一层。把句子看成一袋无序的词,注意力对这袋词做的运算和词摆的先后顺序完全无关:你把词袋倒过来,每个词拿到的加权平均只是把权重也倒过来重新分配,词袋本身的内容一点没变。可语言里语序就是意义——「猫追狗」和「狗追猫」用的是同一袋词,意思却完全相反。网络如果不知道谁在前谁在后,就永远学不会这种区别。位置编码就是给每个位置发一个独特的「座位号向量」,加到词向量上,于是网络一眼能看出谁坐哪。关键不在于用正弦还是用可学习,而在于理解:顺序不是注意力白送的,必须由人补进去;补得好不好,直接决定模型对长程语序、语法结构的把握。
正弦位置编码
用不同频率的正余弦给每个位置一个唯一向量,加到词向量上,可外推到更长序列(原始 Transformer)
可学习位置嵌入
每个位置一个可训练向量,简单直接但超出训练长度外推弱(早期 BERT/GPT)
旋转位置编码 RoPE
通过旋转把相对位置注入 Q/K,外推性好、现代大模型主流
不必推导正弦公式。抓住两点:注意力对顺序天然无感,所以要额外加位置信息;趋势是从「固定/绝对」走向「可学习相对位置(RoPE)」,以支持更长上下文。
组装一个标准 Block
多头注意力只是 Block 的一半。一个标准编码器 Block 内部按如下顺序流动,并且每个子层外面都包了残差连接和层归一化:
一个容易混淆的点:注意力负责「token 之间通信」,FFN 负责「每个 token 各自思考」。注意力层不改变每个位置的独立加工,FFN 层不跨位置混合,两者交替堆叠,构成大模型一层又一层的基本节奏。现代大模型还会调整归一化位置(Pre-Norm)、用 SwiGLU 替换 FFN、引入 RMSNorm,但骨架不变。
为什么要在每个子层外包残差和归一化,而不是直接串起来?裸堆几十层时,前向信号和反向梯度都会在层层变换中要么消失要么爆炸,这就是 m2 讲过的深层训练难题。残差给每条通路留了一条「什么都不变」的直达捷径,让信号至少能无损地传过去;归一化则把每层输出拉回稳定的数值范围,防止某一层数值越滚越大。Pre-Norm(先归一化再进子层)比最初的 Post-Norm 训练更稳、能堆得更深,是现代实现的默认。FFN 常被称作「知识存储区」:它对每个位置独立做一次「先升维再降维」的非线性变换,参数量往往比注意力层还大,模型记住的大量事实与规律就落在这些逐位置的权重里。记住这个分工:注意力负责「词与词交换信息」,FFN 负责「每个词消化自己刚换来的信息」,一交流一思考,层层往复。
为什么必须给 Transformer 加入位置编码?
资深工程师加餐
底层原理 · 大厂视角 · 工程经验,点卡片展开
先查数据:标签是否正确、有没有特征和标签泄漏、是否做了归一化;再查损失与学习率:学习率太大震荡、太小几乎不动;接着看批量大小和优化器,最后才怀疑模型结构。一个极好用的冒烟测试是:用极少量样本训练,看损失能不能被压到接近零——能,说明整条代码链路是通的;不能,问题一定在实现里,先别谈调参。