第十一部分 · 从一个神经元到现代 AI
注意力与 Transformer:现代 AI 的引擎
上一课,卷积把"局部性"这个先验写进了网络——每个神经元只盯着一小块固定的邻域。可语言不是这样的:一句话里的"它",指代的名词可能在十几个词之外,而且每句话指向的地方都不一样。模型能不能自己决定"该看哪里"?本课就造出这台能自己找联系的引擎——而你会发现,它整台机器全是本课程学过的老零件拼出来的。
留下的问题:语言要的恰恰相反——长程、而且随内容而变的连接。"猫累了,因为它跑了一天"里的"它"要连到很远的"猫";下一句这个词可能又连到别处。固定邻域看不了那么远,一层层顺着传(早期的 RNN)又慢又容易把远处的信息传丢。模型怎么自己决定每个词该看哪些词?
本课新增:注意力——一种基于内容的"软查表":用一个 query 去和所有位置的 key 算内积(第 20 课!),过一道 softmax(第 33 课)变成一组权重,再对 value 做加权平均。自注意力让每个位置一步就能看到所有位置;把它和 MLP 堆起来就是 Transformer——GPT、BERT 和今天几乎所有大模型的骨架。而它没有一件新零件:内积、softmax、加权平均,全是老朋友。
该看哪里?
先把上一课的先验摆正。卷积的赌注是局部性:一个输出只看它周围固定的一小圈邻居。这个先验对图像近乎完美——相邻像素往往属于同一条边、同一块颜色。可一旦换成语言,这个假设就绷不住了。看这句:
"猫 累了,因为 它 跑了 一天。"
要理解"它",模型必须把它连到句首的"猫"——两者隔着好几个词。更麻烦的是,这种连接随内容而变:换一句"狗追猫,因为它饿了","它"又该连到"狗"。没有一个固定的邻域能同时接住所有这些情况。
早期处理序列的做法是 RNN:像读书一样从左到右一个词一个词地读,把"读到现在的记忆"一路往后传。可要把"猫"的信息传到很远的"它",得穿过中间每一个词——路径长、信息容易在传递中被稀释甚至丢掉,而且天生是串行的(必须读完前一个才能读下一个),没法并行,训练慢。
我们真正想要的是一台能自己判断"每个词此刻该看哪些词"的机器:连接不是写死的邻域,而是根据内容临时算出来的;而且最好每个词一步就能够到任何一个别的词。这台机器叫注意力(attention)。
注意力 = 软字典查表
想象一本字典(查找表):你拿一个键(key)去查,命中了就取回对应的值(value)。普通字典是"硬"的——要么精确命中,要么查不到。注意力把它变"软":不再要求精确命中,而是按相似程度,把所有条目的值加权混合起来返回。
具体地,给序列里每个位置都算出三样东西(先只管含义,怎么算下一节说):一个查询 query(q)——"我想找什么";一个键 key(k)——"我能提供什么样的匹配";一个值 value(v)——"匹配上了就把我这份信息交出去"。现在某个位置发出它的 q,想知道该从别人那里取多少信息。第一步,量它和每个 key 有多像。用什么量相似?第 20 课的老工具——内积:
相似度 sᵢ = q · kᵢ (读作"query 与第 i 个 key 的内积")
内积大,说明 q 和 kᵢ 方向一致、匹配度高(第 20 课:内积编码了夹角,方向越一致值越大)。这样每个位置都得到一串"匹配分数"。可这些分数有正有负、大小不一,不能直接当权重。我们要把它们变成一组和为 1 的权重——正是第 33 课的 softmax:
wᵢ = softmax(sᵢ) = e^{sᵢ} / ∑ⱼ e^{sⱼ}
读作"第 i 个权重等于:把第 i 个分数取指数,再除以所有分数取指数之和"。softmax 把任意一串实数压成一个概率分布——每个 wᵢ ≥ 0,全部加起来正好等于 1,而且分数越大的位置分到的权重越大。最后一步,用这组权重对所有 value 做加权平均,就是这个位置的输出:
输出 = ∑ᵢ wᵢ · vᵢ = ∑ᵢ softmax(q · kᵢ / √d) · vᵢ
读作"输出等于:对每个位置,把'query 与它的 key 的内积除以 √d、再过 softmax'得到的权重,乘上它的 value,全部加起来"。这就是注意力的全部公式。翻译成人话:用 query 去匹配所有 key,按匹配程度决定从每个 value 里取多少,混合成一份答案。它是一本可微的字典——因为全程只有内积、指数、加权和,处处可导,于是能被反向传播(第 26 课)一路优化。
自注意力:人人都能看到人人
上一节的 q、k、v 从哪来?在自注意力(self-attention)里,它们全都来自同一句话。把每个词先变成一个向量(词嵌入),再各自乘上三个可学的线性映射(就是第 11 课的矩阵乘法,q = W_q x、k = W_k x、v = W_v x),就得到了这个词的 query、key、value。三个 W 是训练出来的参数——模型自己学会"该拿什么去匹配、该提供什么、该交出什么信息"。
于是发生了一件关键的事:句子里每一个词都发出自己的 query,去和所有词(包括自己)的 key 算内积、过 softmax、对所有 value 加权平均。也就是说——每个位置一步就能"看"到序列里任何其它位置。回到"它"和"猫":不管它们隔多远,"它"的 query 只要和"猫"的 key 内积够大,softmax 就会给"猫"分到大权重,一步把"猫"的信息取过来。
Transformer = 注意力块堆起来
把自注意力当成一块积木,Transformer 就是用它搭起来的塔。一个标准块里除了自注意力,还有几样配件,每一样我们都见过:
- 前馈 MLP(第 38 课):注意力负责"在词之间搬运、混合信息",之后每个位置再各自过一个小的多层感知机,做非线性的加工。搬运 + 加工,交替进行。
- 位置编码:注意力本身是一堆内积加权和,不知道谁在前谁在后——把词序打乱,输出只是跟着换个位置,值不变(这叫对顺序不敏感)。可"猫追狗"和"狗追猫"意思相反!所以必须显式地把每个词的位置信息(比如一组随位置变化的向量)加到词嵌入上,模型才知道语序。
- 残差连接:每个子层的输出都加回它的输入(x + 子层(x)),让梯度有一条"高速公路"直通底层——这正是上一部分讲深网络时的老办法,堆得越深越离不开它。
- 层归一化:每层对激活值做标准化(减均值、除标准差,回扣第 30 课把数据"正规化"的思路),让数值尺度稳定、训练不容易发散。
一块 = 自注意力 + MLP(配上残差与层归一化),把这样的块堆几十到上百层,再喂进海量文本训练,就是 GPT、BERT 这些模型的骨架。名字听起来像黑科技,可请数一数用到的零件:
下面这个小实验,把注意力最核心的一步——"一个 query 在关注谁"——摊开给你看。
注意力的归纳偏置:最弱、最灵活
把注意力和卷积摆到一起对比,才看得清它的真正特点。第 40 课说过,卷积带着一个很强的先验:局部性 + 权重共享——它假定了"重要的联系都发生在近邻之间"。这个假设省数据(结构已经替你规定好了),但也画了牢笼:远处的东西,它天生看不到。
注意力几乎反过来:它的归纳偏置是最弱的——"任何位置都可以关注任何位置"。它几乎不预设哪些连接重要,而是把"到底该连什么"这件事完全交给数据去学(通过那三个可学的 W)。灵活性拉满,代价也随之而来:先验越弱,模型能自由拟合的东西越多,就越吃数据、吃算力——你得用海量样本,才能让它从零学出那些卷积一开始就免费送你的结构。
还有一处代价必须点破:自注意力里每个词都要和所有词算内积,n 个词就有 n × n 对,计算量是 O(n²)——序列一长就很贵。它之所以还能赢,是因为这 n² 个内积高度并行,正好被 GPU 吃满;用"贵但可并行"换来了"看得远、堆得大"。这笔账,在规模时代划算。
常见误解
- "注意力是一种全新的、高深的机制。"
不是。它就是三样老零件的组合:用内积算 query 与 key 的相似度(第 20 课)、用 softmax 把相似度归一成权重(第 33 课)、再对 value 做加权平均。公式 ∑ softmax(q·kᵢ/√d)·vᵢ 里没有一件你没学过的东西。 - "Transformer 真的'读懂'了语言。"
它做的是按相似度加权平均——一种极其强大的模式匹配。它能把"它"连到"猫",是因为训练让相应向量的内积变大,不是因为它理解了指代。强大 ≠ 理解,别把统计上的关联当成语义上的懂。 - "注意力 O(n²) 太贵了,所以其实没什么用。"
确实贵——序列越长越贵。但这 n² 个内积可以同时算,高度并行,正好喂饱 GPU。配上规模,它用"贵但可并行"打败了"便宜但串行"的 RNN。贵,不等于没用。 - "自注意力自带语序信息。"
恰恰不。注意力是内积的加权和,对顺序不敏感——打乱词序,输出只是跟着挪位、值不变。"猫追狗"和"狗追猫"在纯注意力眼里几乎一样,所以必须显式加上位置编码,模型才分得清前后。