你本来可以发明 Transformer:把每一层都看成记忆库
一篇 HN 热文把线性层、MLP、自注意力统一成「联想记忆库」,用干扰-信号比算出容量从 64 到 2700 再到 120 万。本期访谈聊这套推导为什么漂亮、评论区为什么吵公式太多,以及它对理解长上下文的真正启发。

这周 HN 头条被一篇数学味很浓的长文占了:alexwang.ai 上的《You could have invented transformers from memory》,891 个赞、130 多条评论。作者干了一件很漂亮的事——从"联想记忆"这一个朴素想法出发,一路推导出线性层、MLP,最后落到自注意力,顺手算出每一层的"记忆容量":64、2700、120 万。我请来老朋友程墨——机器学习理论博士出身,现在一家大模型公司做预训练架构,天天跟这些层打交道。我们聊了聊这套推导到底新在哪、评论区为什么为公式吵起来,以及它对理解长上下文有什么真正的启发。
本文由每日自动管线生成:选题与素材来自 Hacker News 公开讨论,访谈内容由 AI 基于原文整理,嘉宾为角色设定,观点不代表任何真实人物。
小听: 程墨,先从标题开刀。"你本来可以发明 Transformer"——评论区最高亮的回复之一就四个字:"Who? Me?"(我?)。这标题是不是太狂了?
程墨: 狂,但狂得有底气。作者的论证路线是这样的:先定义一个"记忆库"——存 M 条记忆,每条是一个键值对 (k_i, v_i);查询时拿输入 x 跟所有键比相似度,按相似度加权求和取出值。然后他问:最朴素的记忆库长什么样?答案是线性层。把每条记忆做外积 v_i k_i^T 全部加起来得到权重矩阵 W,查询就是一次矩阵乘法 Wx。好,Transformer 的三大件已经出场了第一件,而且你确实"发明"了它——因为这是任何人都会想到的第一版。
小听: 但第一版有个致命问题,文章管它叫干扰。评论区有人说想看伪代码不想看公式,那我替他问:什么叫干扰?
程墨: 翻译成大白话就是:你往记忆库里存了 100 条记忆,想取第 7 条,结果另外 99 条的键都跟你的查询"有点像",每条都往结果里掺了一勺自己的值。作者算了一笔很漂亮的账:如果键是随机的单位向量,干扰和信号的比值大约是 √(M/d_k),d_k 是键的维度。比值到 1,信号就被淹没了——所以线性记忆库的容量就是 M ≈ d_k。键维度 64,只能存 64 条记忆。这就是为什么文章里那张交互图里,你把记忆数拖过 64,召回立刻崩掉。
小听: 等等,这里有个很妙的旁注我得拎出来:梯度下降的一步,本身就是在"写记忆"。W ← W − ηȳx^T,键是输入 x,值是 −η ȳ。训练模型 = 往记忆库里一条一条写?
程墨: 对,这是全文我最喜欢的一个 aside。它把"学习"翻译成了"记忆写入":每一步梯度下降,都是一次带键 x、值 −ηȳ 的外积写入。反向传播突然就有了物理意义——你不是在"优化参数",你是在往一本大账本里追加条目。当然这是线性视角下的简化,真正的训练还有非线性和优化器的戏份,但这个视角一旦建立,后面全顺了。

记忆越多,取回时彼此越是噪音。
小听: 好,容量只有 64 不够用。文章的第二步是 MLP——怎么从"加个非线性"推出容量从 64 跳到 2700 的?
程墨: 思路是"召回时更挑剔"。线性召回的问题是:哪怕某个键跟查询只有 0.1 的相似度,它也要按 0.1 的权重掺一勺。MLP 的做法是把每个相似度先过一个非线性 σ 再加权:f(x) = V^T σ(Kx)。第一层权重矩阵的行就是键,第二层的列就是值——你刚"重新发明"了两层 MLP。用平方 ReLU 时,0.1 的相似度被压成 0.01,而 1 的相似度还是 1,干扰项被几何级数地掐掉。算下来容量变成约 2d_k²/3,d_k=64 时就是 2700,40 倍。用三次方 ReLU 能到 35000。今天 LLM 里人人都在用的 SwiGLU,容量跟平方 ReLU 基本一个量级——你每天调用的 FFN,本质上是一个精心调过的非线性记忆库。
小听: 这里有个考古彩蛋我得问:文章说 Transformer 原论文的 v2、v3 版本里,作者自己就写过"前馈层是一种注意力",后来删了?
程墨: 真的。Vaswani 他们在早期版本里把 FFN 描述成"a kind of attention",键和值就是两个权重矩阵的行和列,softmax 换成 ReLU,还试过拿注意力直接替换 FFN,翻译质量差不多。后来这段话在正式版里消失了。历史开了个玩笑:2017 年随手写的一句类比,2026 年有人用一整篇文章把它算成了定理。
小听: 第三步是高潮:自注意力。指数函数是"无穷多个幂的加权和",所以容量从多项式级跳到指数级——120 万。这一步你买账吗?
程墨: 买账,而且这是全文最漂亮的一跳。前面是 σ(s) = ReLU(s)^p,容量 ≈ d_k^p;作者问:如果不用某一个 p,而是把所有 p 都用上呢?e^{βs} 展开就是所有幂的加权和,β 控制高次项的权重。算下来容量 ≈ e^{2β(1−β/d_k)},取注意力常用的 β=√d_k=8,得到 e^14 ≈ 120 万,是线性库的 2 万倍。而 softmax 只是"数值稳定版"的指数——直接算指数会上下溢,但干扰只跟权重的"比值"有关,除以总和不改变比值,还顺手保证了数值稳定。于是 这就是自注意力。文章里这句话是斜体单独成段,确实配得上。
小听: 还有一句关键区别:MLP 的键值是训练好的参数,自注意力的键值是上下文里的 token 现算的。所以自注意力是"非参记忆库",记忆数量 = 上下文长度。这是不是直接解释了长上下文的那些缩放 trick?
程墨: 完全正确,这是我觉得对工程师最有用的一段。β 决定了你压制"不太像的键"有多狠;上下文越长(M 越大),干扰项越多,就需要更大的 β。Qwen 的 LogN-Scaling、YaRN 的注意力缩放,本质上都是让 β 跟着 log M 涨。以前这些是炼丹经验,现在它们有了第一性原理的出生证明:β ∝ log M 时,200 万条记忆的干扰信号比能压到 0.004。长上下文不再是玄学,是记忆库容量公式的推论。

把指数推到极限,噪音就让位给了聚焦。
小听: 回到评论区。craftkiller 的批评拿了不少赞:公式太多,"这是软件开发网站,能不能写成伪代码"。你作为天天写代码的人,觉得这批评成立吗?
程墨: 半成立。作者的目标读者其实不是"软件工程师",而是"想理解模型内部的人",公式在这里是压缩过的伪代码——W = Σ v_i k_i^T 翻译成代码就是三行。但批评里真正有价值的是后半句:缺一个具体例子。整篇文章的容量估计都是对"随机向量"的,随机键、随机值。可真实模型的键值是训练数据塑造的,根本不是随机的。作者自己在结尾也承认了:这只是看架构的一种方式,训练数据决定了键值里真正存的是什么。Allen-Zhu 的估计是语言模型每参数存约 2 比特的事实知识——那才是"模型记住了多少事"的答案,跟 120 万这个数是两码事。
小听: sailfast 那条评论也很有意思:智能是不是就等于压缩、等于把知识存进权重?
程墨: 这是评论区最哲学的一条,也是最容易滑坡的一条。记忆库视角确实诱人:智能 = 存得多 + 取得准。但别忘了,存进去的是什么、取出来怎么组合,公式一个字没讲。120 万的容量说的是"不互相干扰地存下",不是"理解"。一个能背下整本词典但不会组词的人,你不会说他聪明。记忆是智能的必要条件,不是充分条件——这篇文章漂亮就漂亮在,它把必要条件算清楚了,没有僭越去碰充分条件。
小听: 最后一问,给天天调模型的开发者一句话 takeaway,你会说什么?
程墨: 下次再看模型结构图,别只看到"层",看到"记忆库":线性层是只能记 d_k 条的笨账本,FFN 是带挑剔门卫的卡片柜(SwiGLU 是门卫最精的一个版本),注意力是记忆直接长在上下文里的活页夹。但记住容量公式的适用边界——它算的是随机向量,不是你的训练数据。真想知道模型记住了什么,去看探针和编辑的工作(ROME、MEMIT 那些),别拿着 120 万这个数字当圣经。一句话:这篇文章给了你一副新眼镜,但别指望一副眼镜看清所有东西。本文观点仅代表访谈角色设定,原始讨论见参考链接。