一、FNN 在 Transformer 架构中的位置

Transformer Architecture

从架构图中可以看出,FNN 层处在每一个注意力层之后,其核心功能包括:


二、代码实现分析

首先还是导入必要库:

import torch
import torch.nn as nn
import torch.nn.functional as F

定义 FNN 类,还是继承自 nn.Module

class PositionwiseFeedForward(nn.Module):

类的构造函数实现 “先升维后降维” 的结构:

def __init__(self, d_model, d_ff, dropout=0.1):
    super(PositionwiseFeedForward, self).__init__()
        
    # 第一层线性变换: d_model → d_ff (升维)
    # 将输入从 d_model 维映射到更高维的 d_ff 维
    # 这增加了网络的表示能力
    self.w_1 = nn.Linear(d_model, d_ff)
        
    # 第二层线性变换: d_ff → d_model (降维)
    # 将中间表示映射回原始维度 d_model
    self.w_2 = nn.Linear(d_ff, d_model)
        
    # Dropout 层,防止过拟合
    # 在 ReLU 激活后、第二层线性变换前应用
    self.dropout = nn.Dropout(dropout)

前向传播实现具体公式:

$$ \text{FFN}(x) = \max(0, xW_1 + b_1)W_2 + b_2 $$
def forward(self, x):
    return self.w_2(self.dropout(F.relu(self.w_1(x))))

完整代码

import torch
import torch.nn as nn
import torch.nn.functional as F

class PositionwiseFeedForward(nn.Module):
    def __init__(self, d_model, d_ff, dropout=0.1):
        super(PositionwiseFeedForward, self).__init__()

        self.w_1 = nn.Linear(d_model, d_ff)
        self.w_2 = nn.Linear(d_ff, d_model)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        return self.w_2(self.dropout(F.relu(self.w_1(x))))

带注释的完整代码

import torch
import torch.nn as nn
import torch.nn.functional as F

class PositionwiseFeedForward(nn.Module):
    """
    位置前馈神经网络
    
    功能:对每个位置独立应用相同的两层全连接网络
         "Position-wise" 意味着对序列中的每个位置使用相同的参数
    
    网络结构:
        输入 (d_model) → Linear → ReLU → Dropout → Linear → 输出 (d_model)
                        ↓
                    中间层 (d_ff)
    
    公式:
        FFN(x) = max(0, xW₁ + b₁)W₂ + b₂
        即: FFN(x) = ReLU(xW₁ + b₁)W₂ + b₂
    
    参数:
        d_model: 模型的输入/输出维度,论文中为 512
        d_ff: 中间隐藏层的维度,论文中为 2048
        dropout: Dropout 比率
    
    为什么 d_ff = 2048 (是 d_model 的 4 倍)?
        1. 增加模型的容量和表达能力
        2. 先升维再降维,可以学习更复杂的特征变换
        3. 论文实验表明这个比例效果最好
    
    示例 (batch_size=2, seq_len=10, d_model=512, d_ff=2048):
        输入: (2, 10, 512)
        中间: (2, 10, 2048)
        输出: (2, 10, 512)
    
    注意:
        这个网络对序列中的每个位置独立处理,不同位置之间没有交互
        位置之间的信息交互完全由注意力机制完成
    """
    def __init__(self, d_model, d_ff, dropout=0.1):
        super(PositionwiseFeedForward, self).__init__()
        
        # 第一层线性变换: d_model → d_ff (升维)
        # 将输入从 d_model 维映射到更高维的 d_ff 维
        # 这增加了网络的表示能力
        self.w_1 = nn.Linear(d_model, d_ff)
        
        # 第二层线性变换: d_ff → d_model (降维)
        # 将中间表示映射回原始维度 d_model
        self.w_2 = nn.Linear(d_ff, d_model)
        
        # Dropout 层,防止过拟合
        # 在 ReLU 激活后、第二层线性变换前应用
        self.dropout = nn.Dropout(dropout)

    def forward(self, x):
        """
        前向传播
        
        参数:
            x: 输入张量,形状 (batch_size, seq_len, d_model)
        
        返回:
            输出张量,形状 (batch_size, seq_len, d_model)
        
        计算流程:
            1. w_1(x): 线性变换,形状 (batch, seq_len, d_ff)
            2. ReLU: 激活函数,引入非线性
            3. dropout: 随机置零,正则化
            4. w_2: 线性变换,形状 (batch, seq_len, d_model)
        
        为什么使用 ReLU?
            1. 计算简单高效
            2. 缓解梯度消失问题
            3. 产生稀疏激活,有助于模型学习更鲁棒的特征
        
        代码等价于:
            hidden = self.w_1(x)           # (batch, seq, d_ff)
            hidden = F.relu(hidden)        # ReLU 激活
            hidden = self.dropout(hidden)  # Dropout
            output = self.w_2(hidden)      # (batch, seq, d_model)
            return output
        """
        return self.w_2(self.dropout(F.relu(self.w_1(x))))