最近有读者提问:Transformer模型,是一个单词跟其他的每个单词都建立关系,假设一篇文章由100万个单词,这种关系矩阵则是巨大的,怎么可能存储下这么大的数呢?我感觉这种思想跟one-hot类似啊。

这个问题问得很到位,直击了 Transformer 初学者的最大困惑。你的直觉非常敏锐——“跟 one-hot 类似” 这个感觉,恰恰是误解的根源。

我来帮你拆解这个“巨大矩阵”到底是怎么处理的,你担心的“存储爆炸”在现实中根本不会发生。


1. 核心误解:我们并不存储“单词 × 单词”矩阵

你想象的“100 万 × 100 万”的关系矩阵,确实存在,但它不是存储下来的静态数据,而是即时计算的中间结果。

Transformer 里的“注意力分数”矩阵,不是针对词典(100 万个不同单词),而是针对当前输入的这一句话。假设一篇文章有 100 万个单词(即序列长度 $N = 1{,}000{,}000$),那么这个注意力矩阵的大小确实是:

$$ N \times N = 10^{12} $$

(即万亿级)。

但关键在于:这个矩阵只在当前批次的前向传播中临时生成,计算完梯度后就立即释放了,不会被存进硬盘或显存里长期保存。


2. 那显存里到底存了什么?(参数量)

真正被“存储下来”并不断更新的,是模型的权重参数(Weights),而不是那个临时的关系矩阵。

这些权重是可学习的“关系提取器”,而不是“关系记录表”。它包括:

  • Q、K、V 投影矩阵:假设模型维度是 $d = 768$,这三者的参数量大约是:

$$ 3 \times (768 \times 768) \approx 177 \text{ 万} $$

  • 前馈网络(FFN):约:

$$ 2 \times (768 \times 3072) \approx 472 \text{ 万} $$

结论:对于标准的 Transformer,参数量只取决于“模型维度 $d$”和“层数”,与“文章长度 $N$”毫无关系。无论你输入 10 个字还是 100 万字,模型的权重文件大小是固定的(比如几百 MB)。


3. 那 100 万字的矩阵怎么算?——分块(Chunking)

你担心的“万亿级矩阵显存放不下”,现实中也确实放不下。所以工程上根本不会一次性算 $10^{12}$ 的矩阵,而是采用稀疏注意力(Sparse Attention)或分块计算:

  • 局部窗口:每个单词只看前后几百个词(滑动窗口),复杂度从 $O(N^2)$ 降到 $O(N \times \text{窗口大小})$。
  • Longformer / BigBird:使用“全局词(如 [CLS])+ 局部窗口 + 随机抽样”的组合,把复杂度降到线性。
  • 梯度检查点(Gradient Checkpointing):计算前向时,不保存这个巨大的注意力矩阵,只保存部分关键节点;反向传播时重新计算一遍。用“计算时间”换“显存空间”。

4. 为什么说它不像 One-Hot?

这是你问题里最精彩的部分,我来对比一下:

  • One-Hot(独热编码):
    是静态的、高维稀疏的。第 5000 个词就是第 5000 位为 1,其他全是 0。

词与词之间没有语义关系,“苹果”和“香蕉”的向量点积为 0。

  • Transformer 的自注意力:
    是动态的、低维稠密的。它通过 Query 和 Key 的点积,算出的是“权重系数”(比如 $0.8, 0.1, 0.05, \dots$)。

这个系数代表了语义相似度,而且是上下文相关的——“苹果”在“吃苹果”和“苹果手机”里的关系向量完全不同。

一句话总结差异:

One-Hot 存的是“身份证号”(你是谁),
Transformer 存的是“关系评分”(你和谁像,该听谁的)。
前者存的是死数据,后者算的是活分数。


5. 终极武器:FlashAttention(不存储完整矩阵)

现在的 GPT-4、Llama 3 等大模型,根本不把 $N \times N$ 矩阵算出来放显存里。

它们使用 FlashAttention(闪存注意力) 技术,将 Q、K、V 切分成小块(Tiles),在 GPU 的 SRAM(高速缓存)里算完注意力分数,立即加权求和得到最终输出,直接把中间结果($N \times N$ 矩阵)丢弃,只把最终的输出向量存入显存。

这使得显存占用从:

$$ O(N^2) $$

降到了:

$$ O(N) $$


总结

你可以这样理解:

大脑在思考“我和你关系有多近”时,不需要把全世界所有人的关系图都写在纸上,而是看了一眼,心里立刻算了个分数,记下结果,就把草稿纸扔了。Transformer 正是如此。


每个单词只看前后几百个词(滑动窗口),这样会出现问题,如果一个单词A在文章开头出现,然后间隔了1万字,又出现一次,transformer是怎么记住前面的单词A的?

滑动窗口下,Transformer 如何“记住”1 万字前的单词 A?

这个问题非常棒,你敏锐地抓住了局部注意力(滑动窗口)和全局上下文之间的矛盾。

先说结论:Transformer 本身不主动“记住”任何东西,它的记忆完全依赖于输入。 如果只看滑动窗口,当 A 间隔 1 万字再出现时,它确实会“忘记”开头那个 A。

但是,现代大模型通过以下 4 种机制 巧妙地解决了这个问题,让 A 即使隔了 1 万字,也能被有效关联。


1. 深层网络的“接力传递”(隐式记忆)

这是最根本的机制。虽然 第 1 层 的每个词只看前后几百个词,但信息会随着网络深度 逐层传递。

  • 在第 1 层,开头的 A 的信息传给了它附近的词 B。
  • 在第 2 层,B 结合了 A 的信息后,又传给了更远的词 C。
  • 经过几十层(如 Llama 3 有 80 层)的堆叠,信息像“传话游戏”一样,一步步从开头传递到了 1 万字后的位置。

虽然这个传递会有信息衰减,但足够让模型感知到“前面有个 A”。这种机制被称为 感受野(Receptive Field)的扩大。


2. 全局“哨兵”Token(显式记忆)

这是目前最主流、最有效的方法。模型不会让所有词都只看局部,而是会设置一些 特殊的全局 Token,它们可以 无视距离,看到整篇文章的所有词。

  • 比如 BERT 开头的 [CLS] 标记,或 GPT 中某些特定的注意力头。
  • 这些全局 Token 会“阅读”整篇文章,把关键信息(比如“开头有一个 A”)压缩进自己的向量中。
  • 当 1 万字后的 A 出现时,它只需要 看这一眼全局 Token,就能知道“开头有个 A”,不需要自己去翻 1 万字的距离。

3. 位置编码的“相对偏差”(显式提示)

滑动窗口虽然限制了范围,但在计算注意力分数时,模型依然能看见窗口内所有词的 相对位置。

  • 如果模型设置了 窗口大小为 4096,而你的文章有 1 万字,那么第 10000 个词确实看不见第 1 个词。
  • 但现在的模型(如 Mistral 和 Llama 3)使用了 “滑动窗口 + 全局注意力层” 的混合架构。它们会在每隔几层(比如第 4 层、第 8 层)插入一层全注意力(Full Attention),专门用来打破窗口限制,充当“记忆锚点”。

4. 外挂记忆库(RAG 和 KV Cache 的变种)

对于极端长的文本(比如 100 万字),模型干脆 放弃内部记忆,采用外部检索。

  • 当处理到 1 万字后的 A 时,模型会 实时检索 之前的文本。
  • 如果发现当前词与开头的 A 相关,系统会 直接从外部缓存(KV Cache) 中把开头 A 的向量“拽”到当前窗口内,强行让它们建立关系。这就好比你看书忘了前面内容,回头翻了一下目录,而不需要把整本书背下来。

总结:为什么你看不到开头的 A?

模型并非“记住”了开头的 A,而是:

  • 浅层:通过 层间传递,把 A 的信息“搬”到了距离当前词较近的位置;
  • 深层:通过 全局 Token,直接提取了 A 的精简摘要;
  • 架构层:通过 混合注意力,定期打破窗口限制。

所以,即使 A 在开头,它在计算当前这个词时,注意力分数矩阵里可能没有直接包含开头的 A,但它 确实接收到了开头 A 经过多层提炼后的“精神”。这就是为什么现在的模型能处理几十万字的上下文,而不仅仅是局部片段。