Transformer 的位置前馈网络:为什么每个 token 独立处理却能增强表示
围绕Position-wise Feed-Forward Network,解释两个线性变换、ReLU、参数共享,以及 FFN 与自注意力的互补关系。
相关工具
位置前馈网络到底在做什么
Transformer 中的 Position-wise Feed-Forward Network,通常简称 FFN,是每个编码器层和解码器层里的重要子层。它接收每个位置经过注意力融合后的表示,对这个位置的特征做进一步变换,再把结果交给后续计算。
资料 对它的定义很明确:每个位置的词都单独经过同一个全连接前馈网络,网络由两个线性变换组成,中间使用 ReLU 激活函数。这里的“单独”指不同位置之间不在 FFN 内直接交换信息,“同一个”指所有位置共享同一组参数。
所以,FFN 不是再次计算词与词之间的关系,而是对已经融合上下文的每个位置做非线性加工。注意力负责把信息带过来,FFN 负责把当前已有的信息变换成更适合后续层使用的表示。

一个 token 向量依次经过 Linear、ReLU 和第二个 Linear,输出维度回到 d_model,并与残差路径相加。
为什么要使用两个线性变换
如果只使用一个线性层,输入和输出之间仍然是线性关系,多层线性变换叠加后也可以合并成一个线性变换,表达能力有限。中间加入 ReLU 后,网络才拥有非线性,可以对不同特征组合做更灵活的变换。
第一层通常把 d_model 维表示扩展到更大的 d_ff 维度,让网络有更宽的中间空间处理特征;经过激活函数后,第二层再把表示压回 d_model,以便接入残差路径和下一层。
这并不是把信息简单放大再缩小。中间维度提供了更多组合空间,ReLU 会保留正向响应、压低负向响应,第二个线性层再把这些经过筛选的特征重新组织起来。
ReLU 在中间做了什么
ReLU 的形式是 max(0, x)。对于正值,它保留原来的大小;对于负值,它输出 0。放在两个线性层之间后,网络可以让不同维度对不同输入模式产生或关闭响应。
这会让 FFN 具备一定的选择性:某些特征组合出现时,中间神经元被激活;其他组合则可能被压低。具体哪些神经元对应哪些语义,不能只根据公式直接指定,但非线性为这种分化提供了条件。
不同 Transformer 实现也可能使用 GELU 等其他激活函数。激活函数会影响训练和表示,但“线性变换、非线性激活、线性变换”的基本结构仍然是理解 FFN 的主线。
每个位置独立,为什么还能处理整句
FFN 对位置独立,是指它计算某个位置时只读取该位置当前的向量,不像 Self-Attention 那样直接访问其他位置。假设输入有 n 个位置,FFN 可以把同一个两层网络同时应用到 x_1、x_2、一直到 x_n。
所有位置使用相同的 W_1、W_2 和激活函数,但每个位置的输入向量不同,所以输出也不同。它更像是对一列位置向量逐行执行同一套变换,而不是给每个位置准备独立参数。
整句信息在进入 FFN 之前已经通过注意力混合过。当前 token 的向量可能已经包含其他位置的信息,因此 FFN 虽然不跨位置通信,仍然可以加工带有全局上下文的表示。

每个位置独立经过同一组 W1、ReLU、W2 参数,位置之间不在 FFN 内直接交互,计算可以并行执行。
自注意力和 FFN 是怎样分工的
Self-Attention 的主要工作是沿序列维度建立关系:当前 Query 可以读取其他位置的 Value,输出一个融合上下文的新向量。它回答的是“当前这个位置应该从哪里获取信息”。
FFN 的主要工作是沿特征维度变换:它对当前向量的各个特征进行线性组合和非线性处理。它回答的是“已经得到这些信息之后,应该怎样重新组织特征”。
如果只有注意力,模型可以交换信息,却缺少足够的逐位置非线性加工;如果只有 FFN,每个位置只能独立处理自己的输入,无法建立词与词之间的动态联系。Transformer 把两者放在同一层中,形成交互与变换的循环。

注意力在位置之间混合信息,FFN 在每个位置内部变换特征,两者共同更新序列表示。
FFN 为什么可以并行计算
因为每个位置都使用同一个网络,且位置之间没有数据依赖,所以可以把整个序列表示组成矩阵,直接进行两次矩阵乘法和一次激活。输入 [batch, seq, d_model] 可以在最后一维上批量扩展到 d_ff,再压回 d_model。
这种并行和注意力的并行不完全相同。注意力需要计算位置之间的 [seq, seq] 关系矩阵,FFN 不产生位置两两关系,只在特征维度上做变换,因此它的计算更像大量独立的全连接操作。
FFN 的中间维度通常比 d_model 大,所以它仍可能占据相当的计算量和参数量。位置独立并不代表成本很小,而是意味着它容易被规则地批量执行。
残差连接为什么要把 FFN 输出加回来
FFN 最终会把输出维度映射回 d_model,然后与子层输入通过残差连接相加。这样,FFN 学习的是在原表示上增加哪些特征变化,而不是每一层都必须从零构造完整表示。
残差路径也让信息有机会绕过 FFN 继续向后传递。对于某些位置和某些输入,如果当前 FFN 不需要做很大的调整,模型可以保留原始特征;如果需要变换,FFN 则在原表示上添加更新。
LayerNorm 通常会和这个残差结构一起出现,具体放置位置取决于模型采用的 Pre-Norm 或 Post-Norm 设计。无论哪种布局,核心都是让 FFN 的更新能够稳定地嵌入深层表示。
FFN 的参数为什么不随序列长度增加
FFN 的参数由 W_1、W_2 和可能的偏置组成,大小取决于 d_model 和 d_ff,而不是当前序列有多少个 token。无论一条序列有 20 个位置还是 200 个位置,同一层都使用同一组 FFN 参数。
序列变长会增加计算次数,因为更多位置需要经过 FFN,但不会让模型为每个新位置新增一套权重。这种参数共享让模型可以处理不同长度的序列,也避免了把位置编号直接绑定到网络参数上。
不同层通常有各自的 FFN 参数。第 1 层和第 2 层都对每个位置使用共享网络,但两层之间不必共享同一组 W_1、W_2,因此可以在深度方向上形成不同的特征变换。
如何判断 FFN 是否接入正确
先看形状:输入和输出最后一维应为 d_model,中间维度是 d_ff;第二个线性层必须把 d_ff 映射回 d_model,才能与残差相加。形状对不上时,通常是投影方向或转置出了问题。
再看位置关系:FFN 本身不应产生 [seq, seq] 的注意力矩阵,也不应把不同位置拼到一起。它可以接收带有上下文的向量,但自己的两层变换应沿最后一维独立作用。
最后看子层顺序和归一化位置,确认 FFN 前后是否存在对应的残差连接。只要结构、形状和残差路径都正确,才适合进一步比较激活函数、隐藏维度或参数规模。
把位置前馈网络放回 Transformer 主线
Transformer 一层中的 Self-Attention 负责把不同位置的信息汇聚到当前表示,Position-wise FFN 负责对每个位置的上下文表示做非线性加工。前者沿序列维度建立联系,后者沿特征维度扩展和重组信息。
资料 用“每个位置单独经过同一个前馈网络”概括了 FFN 的本质。单独处理保证没有额外的位置混合,同一网络保证参数共享,两个线性层和激活函数则提供了逐位置的表达能力。
记住三个关键词即可:位置独立、参数共享、非线性变换。把它和注意力、残差以及 LayerNorm 放回同一层结构中,FFN 就不再是一个藏在架构图里的普通全连接层,而是 Transformer 表示更新的重要一半。
常见问题
位置前馈网络会让不同 token 之间互相通信吗?
FFN 本身不会直接进行位置之间的信息交互。它对每个位置独立应用同一个网络,但输入向量可能已经通过 Self-Attention 融合了其他位置的信息。
为什么 FFN 要先扩展维度再压回来?
更大的中间维度提供更多特征组合空间,激活函数引入非线性,第二个线性层再把结果映射回 d_model,方便残差连接和后续层处理。
所有位置使用同一个 FFN 参数吗?
同一层内的所有位置共享 W1、W2 和激活函数;不同层通常有各自的 FFN 参数。