🎯 课程主题
前馈神经网络(Feed Forward Network)——Transformer 编码器/解码器中的非线性变换层。
📝 核心知识点
1. 前馈神经网络的定义
- 概念说明:前馈神经网络是一种无循环、信息单向流动的多层神经网络。
- 关键细节:
- 无循环:信息从输入到输出单向流动,不回头
- 多层:由多个神经网络层堆叠
- 是一种结构类型,而非特定网络
- 中间层可以是卷积层、全连接层等,但不能是 RNN 等循环网络
- AlexNet、ResNet 等都属于前馈神经网络
2. Transformer 中的前馈神经网络
- 概念说明:Transformer 使用全连接层构建 FFN。
- 关键细节:
- 由两层全连接神经网络组成
- 第一层:输入 $X$ → 全连接层 → 中间输出
- 第二层:中间输出 → 全连接层 → 最终输出 $Y$
- 类似 QKV 计算中的权重矩阵映射
3. 维度变换与残差连接
- 概念说明:FFN 的输入输出维度需一致,便于残差连接。
- 关键细节:
- 输入维度:$L \times D$(如 $3 \times 4$)
- 经第一层全连接映射
- 经第二层全连接映射
- 输出维度:$L \times D$(如 $3 \times 4$)
- 输入输出维度一致,才能做残差连接(Add)
4. FFN 在 Transformer 中的位置
- 概念说明:FFN 位于注意力机制之后。
- 关键细节:
- 编码器:
多头注意力 → Add & Norm → FFN → Add & Norm - 解码器结构类似
- FFN 提供非线性变换能力
- 编码器:
🧮 核心公式与推导
FFN 计算公式:
$$\text{FFN}(x) = \max(0, x W_1 + b_1) W_2 + b_2$$
其中:
- $x \in \mathbb{R}^{L \times D}$:输入
- $W_1 \in \mathbb{R}^{D \times D_{ff}}$:第一层权重($D_{ff}$ 通常为 $4D$,如 2048)
- $b_1 \in \mathbb{R}^{D_{ff}}$:第一层偏置
- $\max(0, \cdot)$:ReLU 激活函数
- $W_2 \in \mathbb{R}^{D_{ff} \times D}$:第二层权重
- $b_2 \in \mathbb{R}^{D}$:第二层偏置
- 输出 $\in \mathbb{R}^{L \times D}$
维度变化: $$L \times D \xrightarrow{W_1} L \times D_{ff} \xrightarrow{\text{ReLU}} L \times D_{ff} \xrightarrow{W_2} L \times D$$
物理意义:先升维($D \to D_{ff}$)引入非线性,再降维($D_{ff} \to D$)恢复维度,增强模型表达能力。
🏗️ 模型架构与数据流向
完整编码器层流程:
Input → Multi-Head Attention → Add & Norm → FFN → Add & Norm → Output
💻 代码实战
import torch
import torch.nn as nn
import torch.nn.functional as F
class FeedForward(nn.Module):
"""Transformer中的前馈神经网络"""
def __init__(self, d_model, d_ff, dropout=0.1):
"""
d_model: D, 输入输出维度 (如512)
d_ff: 中间层维度 (通常为4*D, 如2048)
"""
super().__init__()
self.w1 = nn.Linear(d_model, d_ff) # 第一层: D → Dff
self.w2 = nn.Linear(d_ff, d_model) # 第二层: Dff → D
self.dropout = nn.Dropout(dropout)
def forward(self, x):
"""
x: [batch, L, D]
return: [batch, L, D]
"""
# 两层全连接 + ReLU激活
return self.w2(self.dropout(F.relu(self.w1(x))))
# 示例
d_model = 512
d_ff = 2048 # 通常为4倍d_model
ffn = FeedForward(d_model, d_ff)
x = torch.randn(2, 4, d_model) # [batch, L, D]
output = ffn(x)
print(output.shape) # torch.Size([2, 4, 512]) 输入输出维度一致
# 完整的编码器层 (含残差连接和层归一化)
class EncoderLayer(nn.Module):
def __init__(self, d_model, n_heads, d_ff, dropout=0.1):
super().__init__()
self.attn = nn.MultiheadAttention(d_model, n_heads, dropout=dropout, batch_first=True)
self.norm1 = nn.LayerNorm(d_model)
self.ffn = FeedForward(d_model, d_ff)
self.norm2 = nn.LayerNorm(d_model)
self.dropout = nn.Dropout(dropout)
def forward(self, x, mask=None):
# 1. 多头注意力 + 残差 + 层归一化
attn_out, _ = self.attn(x, x, x, key_padding_mask=mask)
x = self.norm1(x + self.dropout(attn_out))
# 2. 前馈神经网络 + 残差 + 层归一化
ffn_out = self.ffn(x)
x = self.norm2(x + self.dropout(ffn_out))
return x
⚠️ 常见问题与避坑指南
- FFN 输入输出维度必须一致($L \times D$),否则无法做残差连接
- 中间层维度 $D_{ff}$ 通常为 $4 \times D$(如 $512 \to 2048 \to 512$)
- 前馈神经网络中不能使用 RNN 等循环结构
- ReLU 激活函数提供非线性变换能力
💡 个人总结与延伸
前馈神经网络是 Transformer 中提供非线性变换的关键组件,通过"升维-激活-降维"的两层全连接结构增强模型表达能力。虽然结构简单,但占据了 Transformer 大部分参数量。现代大模型中,FFN 常被改进为 SwiGLU(LLaMA)、GeGLU 等变体,使用门控机制进一步提升性能。