四点半·技术博客

从注意力到长程记忆:把 Transformer 从机制讲到它做不到什么

阿信 · 北京四点半软件工作室 #Transformer #LLM #深度学习 #架构

上一篇讲 agent 的时候提了一句:Transformer 的注意力是”平面”的,没有程序计数器,所以长任务会迷路。这句话背后有一堆没展开的东西。这篇把 Transformer 本身讲透——它为什么 2017 年出来之后统治了 NLP,为什么现在的大模型都是它堆出来的,以及它的结构性短板到底在哪。

先回到 2017 年之前

要理解 Transformer 为什么是个突破,得先知道它解决了什么问题。

在它之前,处理序列文本的主流是 RNN / LSTM。你给它一句话,它一个词一个词地读,每读一个词更新一次内部状态:

state_0 = 初始
state_1 = f(state_0, "我")
state_2 = f(state_1, "今天")
state_3 = f(state_2, "去")
...

这个架构有两个致命问题。

第一,无法并行。 state_3 必须等 state_2 算完,state_2 必须等 state_1。GPU 再快也没用,因为计算是串行的。想训练一个读 10 万 token 的模型,就得真的跑 10 万步。

第二,长程依赖会衰减。 “我从小在法国长大……(中间隔了 50 个词)……所以我会流利地说____。” RNN 要靠 state 一路把”法国”这个信息传到最后一个词。信息经过 50 次非线性变换,早被磨没了。LSTM 的门控机制缓解了一点,但本质问题没解决——两个词之间的路径长度是 50,信息要穿过 50 层变换才能碰面。

Transformer 出来,就是同时解决这两件事。

Self-Attention:每个 token 直接看其他所有 token

Transformer 的核心是 self-attention。直觉上你可以这么理解:一句话里每个 token,都要决定”我现在该关注句子里的哪些其他词”。不是按顺序读,是一次性地、全局地互相看。

具体怎么算?三个向量:

  • Query(问):我现在在找什么?
  • Key(键):我这里有什么?
  • Value(值):我实际携带的信息是什么?

每个 token 都生成自己的 Q、K、V。然后拿一个 token 的 Q,去和所有 token 的 K 算相似度(点积),softmax 归一化成一组权重。这组权重就是”注意力分布”——我应该把多少注意力分给每个其他 token。最后用这组权重对所有 token 的 V 加权求和,得到这个 token 的输出。

attention(Q, K, V) = softmax(Q · K^T / √d) · V

一个具体例子。句子”动物没有过马路,因为它太累了”。处理”它”这个 token 时:

  • Q(“它”) 和 K(“动物”) 高度匹配 → 注意力权重高
  • Q(“它”) 和 K(“马路”) 低匹配 → 权重低

于是”它”的输出里主要融入了”动物”的信息。模型自己学到了代词指代关系。

关键特性在这里:不管两个 token 在句子里隔多远,它们之间的”路径长度”都是 1。“动物”在第 1 个词,“它”在第 11 个词,中间隔了 10 个词,attention 让它们直接交互。没有 50 步信息衰减的问题。

而且所有 token 同时算,天然并行。这就是为什么 Transformer 能在 GPU 上训得动——2017 年之后模型规模爆炸式增长,靠的就是这个并行性。

Multi-head:一个注意力不够,就来 N 个

刚才说的是一个注意力头。但一个头只能学一种关系。一句话里同时有好几种关系:

  • 语法上:“它”指代谁
  • 语义上:“过马路”的宾语是什么
  • 修饰关系:“太累了”说的是谁

Transformer 的做法是把 Q/K/V 切成多份,每份独立做 attention,这就是 multi-head attention。8 个头就有 8 组独立的 Q/K/V,各自关注不同的关系,最后拼起来。

你可以想象成:同一句话,8 个不同视角同时看,每个视角抓不同的模式,最后汇总。

Positional Encoding:模型不知道词序

到这里有个问题:attention 是”对称”的。它对所有 token 一视同仁,不偏不倚。但”狗咬人”和”人咬狗”字一样、词一样,意思完全相反。模型怎么知道词的顺序?

Transformer 的解法很直白:既然 attention 本身不带位置信息,那就把位置信息直接加到 embedding 里。每个 token 的向量 = 词向量 + 位置向量。位置向量是按固定公式算出来的(正弦余弦),每个位置有独特的编码。

这样 attention 在算 Q·K 的时候,自然就把位置信息带进去了。模型知道”第 1 个词和第 10 个词位置不同”。

后来的 RoPE、ALiBi 这些改进,本质都是在重新设计位置编码方式,让模型能更好地处理长序列。

一个完整的 Transformer Block

把上面的零件拼起来,一个 Transformer block 长这样:

输入 embedding
  → Multi-Head Self-Attention
  → 残差连接 + LayerNorm
  → Feed-Forward Network(两层 MLP)
  → 残差连接 + LayerNorm
输出

GPT 是把这个 block 堆 96 层(GPT-4),Gemini 堆到 128 层。每一层都在做一件事:把上一层的表示再”清洗”和”加工”一遍。

  • 残差连接:让信息可以跳过层直接传,不然几十层堆下去梯度就消失了。
  • LayerNorm:稳定数值,让训练不会炸。
  • Feed-Forward Network:attention 负责”混合不同 token 的信息”,FFN 负责”对每个 token 自己的表示做非线性变换”。一个管 token 间交互,一个管 token 内部处理。

这就是为什么 Transformer 是”深度”模型——堆 96 层不是堆着玩的。浅层学语法,中层学语义,深层学任务逻辑。

为什么它能 Scaling

RNN 时代也有大模型,但没人敢把它做到 GPT-3 那个量级。原因就是 Transformer 解决了并行性和长程依赖两件事,让”堆参数、堆数据、堆算力”变成了有效动作。

Scaling Law 说的是:模型性能随参数量、数据量、算力的增长是平滑可预测的幂律曲线。你敢堆,它就敢涨。这背后的物理基础就是 attention 的计算图是规整的、GPU 友好的。没有 Transformer,就没有今天的大模型。

回到 agent 问题:注意力的结构性短板

讲到这里,Transformer 看起来近乎完美。但它有几个结构性的短板,恰恰是 agent 场景下最疼的地方。

注意力是”平面”的,没有程序计数器

RNN 虽然慢,但它有一个东西:state。state 就是模型的”当前位置”,一步一步往前走,有明确的推进感。

Transformer 没有这个。它每次 forward pass 都看完整的上下文,从第 1 个 token 到第 N 个 token 全部过一遍。没有”我现在执行到第 3 步了”这个内部状态。

你在上下文里写:

任务:订机票
计划:1, 2, 3, 4, 5
当前进度:正在做第 2 步

模型读到这段,它”知道”你在做第 2 步。但这个”知道”是每次 forward pass 重新从上下文里读出来的,不是模型内部维护的计数器。如果中间塞进来一堆工具返回结果,把”当前进度”这行字挤到了注意力分布的边缘,模型就可能”忘了”自己在做第几步。

这就是为什么 agent 需要 scratchpad——把状态显式写在上下文里,而不是指望模型内部记住。

Lost in the Middle:注意力不是均匀的

研究人员发现一个现象:当上下文变长时,模型对开头和结尾的信息利用得好,对中间的信息利用得差。就像人看书会记住开头和结尾,中间会忘一样。

这意味着即使你把任务清单放在上下文里,如果上下文长到几千个 token,清单被夹在中间,模型也可能读不准它。这就是为什么长任务 agent 需要主动管理上下文——把重要的状态放到开头或结尾,把无关信息压缩或移出。

上下文窗口是黑板,不是内存

很多人以为上下文窗口 = 模型的内存。不对。它是一块每次 forward pass 都要全部扫一遍的黑板。

黑板大小有限(128k 还是 1M token),而且每多一个 token,attention 的计算量是平方级增长。黑板上写的东西越多,每一步决策的成本越高、信息密度越低。

这就是为什么 agent 需要外部记忆(向量库、文件系统、数据库)——把不相关的中间结果从黑板上移走,需要时再检索回来。不是模型”记不住”,是它的黑板物理上放不下那么多东西。

错误会沿着上下文累积

这一点和上一篇的错误模型连起来。每一步 agent 把 observation 写进上下文,下一步模型基于这一整段做决策。如果某一步 observation 是错的,或者模型理解偏了,这个偏差会留在上下文里,影响后面所有步骤。

RNN 的 state 是每一步更新的,错了能部分纠正;Transformer 的上下文是只增不减的(除非你主动删),错误一旦写进去就一直在那里。这就是为什么长程 agent 必须有反思机制——定期回头检查之前的步骤有没有跑偏。

工程视角:KV Cache 和推理成本

最后讲一个工程细节,它能解释为什么大模型推理这么贵。

训练时你可以并行算所有 token。但推理时,你是一个 token 一个 token 往外蹦。每生成一个新 token,就要重新做一次 attention。attention 需要每个 token 的 Q/K/V。K 和 V 在前面的 token 上是不变的——这就是 KV Cache 的由来:把历史 token 的 K/V 缓存下来,只算新 token 的 Q,然后和所有缓存的 K 做 attention。

KV Cache 让推理从 O(n²) 每步摊还成 O(n),但代价是显存占用随上下文长度线性增长。这就是为什么长上下文推理这么贵——128k 上下文的 KV Cache 可能占几十 GB 显存。

一句话总结

Transformer 的革命性在于:用 self-attention 把”任意两个 token 直接交互”变成 O(1) 路径,同时把计算变成 GPU 友好的并行结构。这是它统治大模型时代的原因。

但它的代价是:模型没有内部状态,没有程序计数器,所有”记忆”都得放在上下文这块黑板上;注意力对长上下文的利用不均匀,中间的信息容易被忽略;错误一旦写进上下文就会累积。

理解了这一层,你就明白为什么 agent 不能只是”给 LLM 一堆工具”——你必须在 LLM 外面包一层架构,帮它管黑板、帮它维护进度、帮它从错误里恢复。这不是 LLM 不够聪明,是 Transformer 这个架构本身的物理限制。

评论区未配置。在 blog/.env 填入PUBLIC_GISCUS_* 后重新构建即可启用。