FFN层(Feed-Forward Network,前馈神经网络层) 是Transformer等大模型里的核心模块,常放在注意力层之后,负责对每个位置的向量做独立的非线性特征加工,是模型"深度理解语义"的关键。
一、核心定位与结构
在Transformer中,FFN = 两层全连接(线性)+ 中间非线性激活 ,采用"升维→激活→降维"的标准流程:
- 输入:维度为
d_model(如512、768、1024) - 第一步:线性层 → 升维到
d_ff(通常是4 × d_model,如2048、3072、4096) - 第二步:激活函数(ReLU、GELU、SwiGLU等)→ 引入非线性
- 第三步:线性层 → 降维回
d_model→ 输出
数学形式(简化):
FFN(x)=W2⋅Act(W1⋅x+b1)+b2 \text{FFN}(x) = W_2 \cdot \text{Act}(W_1 \cdot x + b_1) + b_2 FFN(x)=W2⋅Act(W1⋅x+b1)+b2
- W1∈Rdff×dmodelW_1 \in \mathbb{R}^{d_{\text{ff}} \times d_{\text{model}}}W1∈Rdff×dmodel,W2∈Rdmodel×dffW_2 \in \mathbb{R}^{d_{\text{model}} \times d_{\text{ff}}}W2∈Rdmodel×dff
- Act\text{Act}Act 为激活函数
二、关键特点
- 逐位置独立处理(Position-wise)
对序列中每个 token(词/字)的向量单独、并行计算,不依赖其他位置,和注意力的"全局交互"形成互补。 - 纯前馈、无循环
信息只从输入到输出单向流动,没有递归/反馈,计算高效。 - 非线性能力
两层线性+激活,让模型能拟合复杂的语义映射与特征组合,是Transformer突破线性表达瓶颈的关键。
三、在Transformer中的分工
- 自注意力层 :负责全局关联,让每个 token 看到并融合所有位置的信息("谁和谁相关")。
- FFN层 :负责局部精修,对每个 token 做深度非线性变换,提炼更抽象、更复杂的语义特征("如何理解这些信息")。
四、常见变体与演进
- 激活函数:从早期 ReLU → 现在主流 GELU、SwiGLU(带门控,效果更好)。
- 维度比例 :
d_ff = 4 × d_model是经典配置,部分模型用 2×、3× 或动态维度。 - 稀疏化:如 MoE(混合专家),把 FFN 拆成多个专家,每个 token 只激活部分专家,提升效率与容量。
五、简单总结
FFN 是 Transformer 里的"逐位置非线性加工器",用"升维-激活-降维"的两层全连接结构,为模型提供强大的非线性表达能力,与注意力层一起构成现代大模型的核心计算单元。
六、完整代码实现
下面是包含 FFN 层、LayerNorm、残差连接的完整代码,可直接复制运行:
python
import torch
import torch.nn as nn
import torch.nn.functional as F
class FeedForwardNetwork(nn.Module):
"""
标准的 FFN 层实现(Transformer 中的 Position-wise Feed-Forward Network)
结构:Linear1 → GELU → Dropout → Linear2 → Dropout
"""
def __init__(self, d_model: int, d_ff: int = None, dropout: float = 0.1):
super().__init__()
# 默认 d_ff 为 d_model 的 4 倍(Transformer 经典配置)
d_ff = d_ff or 4 * d_model
# 第一层:升维(d_model → d_ff)
self.w1 = nn.Linear(d_model, d_ff)
# 第二层:降维(d_ff → d_model)
self.w2 = nn.Linear(d_ff, d_model)
# Dropout 层(防止过拟合)
self.dropout = nn.Dropout(dropout)
# 激活函数:GELU(当前大模型主流,比 ReLU 效果更好)
self.act = nn.GELU()
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
前向传播
Args:
x: 输入张量,形状 [batch_size, seq_len, d_model]
Returns:
输出张量,形状与输入一致 [batch_size, seq_len, d_model]
"""
# 升维 + 激活 + Dropout + 降维 + Dropout
out = self.w1(x) # [bs, seq_len, d_ff]
out = self.act(out) # 非线性激活
out = self.dropout(out)
out = self.w2(out) # [bs, seq_len, d_model]
out = self.dropout(out)
return out
class TransformerEncoderBlock(nn.Module):
"""
最小化的 Transformer 编码器块
结构:LayerNorm → 自注意力 → 残差 → LayerNorm → FFN → 残差
(注:这里简化了自注意力层,仅保留核心流程以展示 FFN 调用)
"""
def __init__(self, d_model: int, n_head: int, dropout: float = 0.1):
super().__init__()
# 层归一化
self.norm1 = nn.LayerNorm(d_model)
self.norm2 = nn.LayerNorm(d_model)
# 自注意力层(简化版,仅用于演示流程)
self.self_attn = nn.MultiheadAttention(
embed_dim=d_model,
num_heads=n_head,
dropout=dropout,
batch_first=True # 输入形状为 [batch, seq, feature]
)
# FFN 层(调用上面实现的 FeedForwardNetwork)
self.ffn = FeedForwardNetwork(d_model=d_model, dropout=dropout)
self.dropout = nn.Dropout(dropout)
def forward(self, x: torch.Tensor) -> torch.Tensor:
"""
前向传播
Args:
x: 输入张量,形状 [batch_size, seq_len, d_model]
Returns:
输出张量,形状与输入一致
"""
# 第一步:自注意力 + 残差连接
x_norm = self.norm1(x)
attn_out, _ = self.self_attn(x_norm, x_norm, x_norm) # 自注意力
x = x + self.dropout(attn_out) # 残差
# 第二步:FFN + 残差连接(核心:调用 FFN 层)
x_norm = self.norm2(x)
ffn_out = self.ffn(x_norm) # 调用 FFN 层
x = x + self.dropout(ffn_out) # 残差
return x
# ------------------- 测试代码 -------------------
if __name__ == "__main__":
# 超参数(符合常规 Transformer 配置)
BATCH_SIZE = 2 # 批次大小
SEQ_LEN = 10 # 序列长度
D_MODEL = 512 # 模型维度
N_HEAD = 8 # 注意力头数
# 1. 创建 FFN 层实例并测试
ffn = FeedForwardNetwork(d_model=D_MODEL)
# 生成随机输入:[batch_size, seq_len, d_model]
test_input = torch.randn(BATCH_SIZE, SEQ_LEN, D_MODEL)
ffn_output = ffn(test_input)
print("FFN 层输入形状:", test_input.shape)
print("FFN 层输出形状:", ffn_output.shape) # 应与输入一致 (2,10,512)
# 2. 创建 Transformer 编码器块并测试 FFN 调用
encoder_block = TransformerEncoderBlock(d_model=D_MODEL, n_head=N_HEAD)
encoder_output = encoder_block(test_input)
print("\nTransformer 块输出形状:", encoder_output.shape) # (2,10,512)
七、代码关键部分解释
-
FFN 层核心逻辑
__init__中定义了两层线性层:w1负责升维(512→2048),w2负责降维(2048→512),符合 Transformer 经典的 4 倍维度配置。forward方法严格遵循"升维→激活→Dropout→降维→Dropout"的流程,保证非线性表达能力的同时防止过拟合。- 激活函数使用
GELU(当前大模型如 BERT、GPT 均采用),比 ReLU 更适合自然语言处理场景。
-
Transformer 块中 FFN 的调用
- FFN 层在自注意力层之后执行,且和自注意力层一样,都遵循"预归一化 + 残差连接"的主流范式(先做 LayerNorm 再计算)。
- 残差连接保证了梯度在深层网络中能有效传递,是 Transformer 能训练深层模型的关键,FFN 层的输出必须和输入维度一致才能做残差。
-
测试结果说明
运行代码后会输出:
FFN 层输入形状: torch.Size([2, 10, 512]) FFN 层输出形状: torch.Size([2, 10, 512]) Transformer 块输出形状: torch.Size([2, 10, 512])验证了 FFN 层输出维度和输入一致,符合 Transformer 残差连接的要求。
八、运行前置条件
- 安装 PyTorch:
pip install torch(建议 1.10 及以上版本)。 - 无需其他依赖,代码仅使用 PyTorch 原生模块。
总结
- FFN 层的核心是"升维→非线性激活→降维",通过两层线性层+GELU实现,输出维度必须和输入一致以支持残差连接。
- 在 Transformer 块中,FFN 层紧跟自注意力层,且都需配合 LayerNorm 和残差连接使用。
- 代码中采用的"预归一化"(先做 LayerNorm 再计算)是当前 Transformer 实现的主流范式,比原始论文的"后归一化"更稳定。