transformer 里的前馈网络
每一层 transformer 有一半不是注意力,而是一个逐 token 作用的小网络,模型的参数大部分都在它身上。它算什么,以及为什么事实性知识看起来就存在那里。
注意力抢走了所有注意力。但在一个 transformer 层里,自注意力只占半个模块——另外半个是前馈网络(feed-forward network),而模型的参数大约三分之二都在它身上。如果说注意力是 token 之间交换信息的地方,那么前馈网络就是每个 token 独自思考的地方。跳过它,你只理解了一半的机器。
注意力搬运,FFN 加工
把这两半区分开,最清楚的办法是:
- **注意力(attention)跨位置混合信息。**每个 token 都从其他 token 那里拉取信息。它本质上讲的是位置之间的关系。
- **前馈网络只在单个位置内工作。**一个 token 从注意力那里收集完上下文之后,独自穿过 FFN——完全不与其他 token 交互。
同一个前馈网络以相同的权重,独立地作用在每一个位置上。第 3 个 token 和第 300 个 token 分别经历完全相同的变换。注意力是序列自我交谈的地方,FFN 是每个信息变丰富的 token 被单独加工的地方。
它到底算什么
前馈网络很小,而且形状在几乎所有模型里都是固定的:两个线性层,中间夹一个非线性。
- **扩张。**把 token 的向量投影到更大的维度——通常约为模型宽度的 4 倍。一个 4,096 维的向量会变成大约 16,384 维。
- **激活。**逐元素施加一个非线性函数。这是整个模块里唯一的非线性,也正是它让这一层能表示直线变换之外的东西。
- **收缩。**再投影回原来的维度,好让输出能接进下一层。
扩张再收缩,整个设计就是这样。中间变宽,给了这一层计算大量中间特征的空间;收缩把它带回堆栈其余部分能消化的尺寸。参数量的绝大部分就落在这两个投影矩阵上。
非线性才是关键
把激活函数拿掉,两个线性层就塌成一个——一串线性运算叠加起来,无论叠多少层,仍然只是一个线性运算。整个模型将只能表示输入与输出之间的直线关系,而这对于语言来说远远不够。
中间那个非线性打破了这一点。它让 FFN 能表示「如果这个特征和那个特征同时出现,就产出这个输出」这类东西——线性映射表达不了的条件式、弯曲的关系。每一层的 FFN 都再加一轮这样的非线性加工,而层层堆叠正是深度有用的重要原因。激活函数本身(它具体的形状是一个随时间演变的设计细节)没那么重要,重要的是某种非线性确实在那里。
知识似乎住在哪里
下面这个发现让 FFN 不只是账面上的数字。当研究者探查模型把事实「存」在哪里时——巴黎在法国、水在 100 °C 沸腾——证据指向的是前馈层,而不是注意力。
浮现出来的图景是:FFN 的作用有点像一块键值记忆。扩张那一步把进来的 token 模式与大量学到的模式做匹配;收缩那一步把关联的信息写回 token 的向量。事实证明,编辑模型中的单条事实可以通过调整特定的 FFN 权重做到,这就是「大量事实关联存放在这里」的直接证据。
这就重新解释了参数量。FFN 在参数预算中占大头不是偶然,而是因为模型存储的知识必须放在那里。注意力决定看什么,FFN 装着模型知道的许多东西。
它占的参数份额为什么重要
因为 FFN 占了权重的大头,它也是前向传播中计算量的大头,是效率优化的主要目标。那些把这些层变小或变稀疏的技术——比如每个 token 只激活 FFN 的一部分——打的正是模型里最大的一块。专家混合设计就是这个思路:用许多小的前馈网络替换一个大前馈网络,每个 token 只路由到其中少数几个,这样参数量可以增长,而不必让每个 token 都为全部参数买单。
所以当有人说一个模型有多少参数时,这个数字的大部分是前馈层的权重,而大多数效率手段也都作用在它们身上。
要点回顾
- 每一层 transformer 都是注意力配一个前馈网络;FFN 装着大约三分之二的参数。
- 注意力跨位置混合信息;FFN 用共享的权重独立处理每个位置。
- 它的形状是扩张 → 非线性激活 → 收缩,而那个非线性正是它能表示非线性关系的原因。
- 有证据表明,模型的事实性知识大部分存在 FFN 的权重里,就像一块学出来的键值记忆。
- 因为它在参数量中占主导,它就成了专家混合这类效率技术的主要目标。
下一篇: 归一化层——包在每个模块外面的另一套装置。