大模型原理与实践:第二章-Transformer架构_第2部分Encoder-Decoder架构

第二章 Transformer架构-从注意力机制到完整实现

总目录

  1. 第一章 NLP基础概念完整指南

    1. 第1部分-概念和发展历史
    2. 第2部分-各种任务(实体识别、关系抽取、文本摘要、机器翻译、自动问答)
    3. 第3部分-文本表示(词向量、语言模型、ELMo)
  2. [第二章 Transformer 架构原理](#第二章 Transformer 架构原理)

    1. 第1部分-注意力机制
    2. 第2部分Encoder-Decoder架构
    3. 第3部分-完整Transformer模型)
  3. [第三章 预训练语言模型](#第三章 预训练语言模型)

    1. 第1部分-待写
  4. [第四章 大语言模型](#第四章 大语言模型)

    1. 第1部分-待写
  5. [第五章 动手搭建大模型](#第五章 动手搭建大模型)

    1. 第1部分-待写
  6. [第六章 大模型训练实践](#第六章 大模型训练实践)

    1. 第1部分-待写
  7. [第七章 大模型应用](#第七章 大模型应用)

    1. 第1部分-待写

目录

  • [3. Encoder-Decoder架构](#3. Encoder-Decoder架构)
    • [3.1 Seq2Seq任务介绍](#3.1 Seq2Seq任务介绍)
    • [3.2 前馈神经网络](#3.2 前馈神经网络)
    • [3.3 层归一化](#3.3 层归一化)
      • [3.3.1 为什么需要归一化](#3.3.1 为什么需要归一化)
      • [3.3.2 Batch Norm vs Layer Norm](#3.3.2 Batch Norm vs Layer Norm)
      • [3.3.3 Layer Norm的计算](#3.3.3 Layer Norm的计算)
    • [3.4 残差连接](#3.4 残差连接)
      • [3.4.1 什么是残差连接](#3.4.1 什么是残差连接)
      • [3.4.2 为什么残差连接有效](#3.4.2 为什么残差连接有效)
      • [3.4.3 Transformer中的残差连接](#3.4.3 Transformer中的残差连接)
    • [3.5 Encoder的实现](#3.5 Encoder的实现)
      • [3.5.1 Encoder Layer实现](#3.5.1 Encoder Layer实现)
      • [3.5.2 完整Encoder实现](#3.5.2 完整Encoder实现)
    • [3.6 Decoder的实现](#3.6 Decoder的实现)
      • [3.6.1 Decoder Layer实现](#3.6.1 Decoder Layer实现)
      • [3.6.2 完整Decoder实现](#3.6.2 完整Decoder实现)

3. Encoder-Decoder架构

3.1 Seq2Seq任务介绍

**序列到序列(Sequence-to-Sequence, Seq2Seq)**任务是NLP中最基础也最重要的任务类型。它指的是:

  • 输入:一个序列 input = ( x 1 , x 2 , . . . , x n ) \text{input} = (x_1, x_2, ..., x_n) input=(x1,x2,...,xn)
  • 输出:另一个序列 output = ( y 1 , y 2 , . . . , y m ) \text{output} = (y_1, y_2, ..., y_m) output=(y1,y2,...,ym)

典型的Seq2Seq任务包括:

  • 机器翻译:输入中文,输出英文
  • 文本摘要:输入长文本,输出摘要
  • 对话生成:输入问题,输出回答

Seq2Seq模型通常采用**编码-解码(Encode-Decode)**框架:

  1. Encoder(编码器):将输入序列编码成固定的向量表示
  2. Decoder(解码器):根据编码结果生成输出序列

Transformer就是一个经典的Encoder-Decoder模型。

3.2 前馈神经网络

前馈神经网络(Feed-Forward Neural Network, FFN)是Transformer中的一个重要组件。每个Encoder层和Decoder层都包含一个FFN。

Transformer中的FFN由两个线性层和一个激活函数组成:

FFN ( x ) = ReLU ( x W 1 + b 1 ) W 2 + b 2 \text{FFN}(x) = \text{ReLU}(xW_1 + b_1)W_2 + b_2 FFN(x)=ReLU(xW1+b1)W2+b2

实现代码:

python 复制代码
import torch
import torch.nn as nn
import torch.nn.functional as F

class FeedForward(nn.Module):
    """前馈神经网络模块"""
    
    def __init__(self, n_embd, hidden_dim, dropout=0.1):
        """
        参数:
            n_embd: 输入和输出维度
            hidden_dim: 隐藏层维度(通常是n_embd的4倍)
            dropout: dropout比率
        """
        super().__init__()
        # 第一个线性层:n_embd -> hidden_dim
        self.w1 = nn.Linear(n_embd, hidden_dim, bias=False)
        # 第二个线性层:hidden_dim -> n_embd
        self.w2 = nn.Linear(hidden_dim, n_embd, bias=False)
        # Dropout层,用于防止过拟合
        self.dropout = nn.Dropout(dropout)
    
    def forward(self, x):
        """
        前向传播
        
        参数:
            x: 输入,shape = (batch_size, seq_len, n_embd)
        返回:
            输出,shape = (batch_size, seq_len, n_embd)
        """
        # x -> 线性变换1 -> ReLU激活 -> 线性变换2 -> Dropout
        return self.dropout(self.w2(F.relu(self.w1(x))))

3.3 层归一化

层归一化(Layer Normalization)是深度学习中常用的归一化技术。

3.3.1 为什么需要归一化?

在深度神经网络中,随着层数的增加,每一层的输入分布可能会发生较大变化(称为内部协变量偏移)。这会导致:

  • 训练不稳定
  • 梯度消失或爆炸
  • 需要更小的学习率

归一化可以稳定训练过程,加快收敛速度。

3.3.2 Batch Norm vs Layer Norm

Batch Normalization(批归一化)

  • 在一个mini-batch上统计均值和方差
  • 对每个特征维度分别归一化

Layer Normalization(层归一化)

  • 在每个样本上统计均值和方差
  • 对所有特征维度一起归一化

Layer Norm的优势:

  • 不依赖batch size,适合小batch训练
  • 更适合序列模型(RNN、Transformer)
  • 测试时不需要保存训练统计量
3.3.3 Layer Norm的计算

对于输入 x ∈ R d x \in \mathbb{R}^d x∈Rd,Layer Norm的计算步骤:

  1. 计算均值: μ = 1 d ∑ i = 1 d x i \mu = \frac{1}{d}\sum_{i=1}^d x_i μ=d1∑i=1dxi
  2. 计算方差: σ 2 = 1 d ∑ i = 1 d ( x i − μ ) 2 \sigma^2 = \frac{1}{d}\sum_{i=1}^d (x_i - \mu)^2 σ2=d1∑i=1d(xi−μ)2
  3. 归一化: x ^ = x − μ σ 2 + ϵ \hat{x} = \frac{x - \mu}{\sqrt{\sigma^2 + \epsilon}} x^=σ2+ϵ x−μ
  4. 缩放和平移: y = γ x ^ + β y = \gamma \hat{x} + \beta y=γx^+β

其中 γ \gamma γ 和 β \beta β 是可学习的参数, ϵ \epsilon ϵ 是一个小常数(防止除零)。

实现代码:

python 复制代码
import torch
import torch.nn as nn

class LayerNorm(nn.Module):
    """层归一化模块"""
    
    def __init__(self, n_embd, eps=1e-6):
        """
        参数:
            n_embd: 特征维度
            eps: 防止除零的小常数
        """
        super().__init__()
        # 可学习的缩放参数,初始化为1
        self.gamma = nn.Parameter(torch.ones(n_embd))
        # 可学习的平移参数,初始化为0
        self.beta = nn.Parameter(torch.zeros(n_embd))
        self.eps = eps
    
    def forward(self, x):
        """
        前向传播
        
        参数:
            x: 输入,shape = (batch_size, seq_len, n_embd)
        返回:
            归一化后的输出,shape与输入相同
        """
        # 在最后一个维度上计算均值和标准差
        # mean shape = (batch_size, seq_len, 1)
        mean = x.mean(dim=-1, keepdim=True)
        # std shape = (batch_size, seq_len, 1)
        std = x.std(dim=-1, keepdim=True)
        
        # 归一化、缩放和平移
        # 广播机制会自动处理维度
        return self.gamma * (x - mean) / (std + self.eps) + self.beta

3.4 残差连接

残差连接(Residual Connection)最早在ResNet中提出,用于解决深度网络的退化问题。

3.4.1 什么是残差连接?

残差连接的思想很简单:让每一层的输出包含原始输入

普通网络: y = F ( x ) y = F(x) y=F(x)

残差网络: y = F ( x ) + x y = F(x) + x y=F(x)+x

3.4.2 为什么残差连接有效?
  1. 缓解梯度消失:梯度可以直接通过残差连接回传
  2. 更容易训练 :如果某一层不需要,可以学习为恒等映射( F ( x ) ≈ 0 F(x) \approx 0 F(x)≈0)
  3. 允许更深的网络:信息可以跨越多层直接传递
3.4.3 Transformer中的残差连接

在Transformer中,每个子层(注意力层、前馈网络)后面都有残差连接:

output = LayerNorm ( x + SubLayer ( x ) ) \text{output} = \text{LayerNorm}(x + \text{SubLayer}(x)) output=LayerNorm(x+SubLayer(x))

注意:这里采用的是Pre-Norm 结构,即先做Layer Norm,再做残差连接。原始论文使用的是Post-Norm(先残差连接,再Layer Norm),但实践证明Pre-Norm更稳定。

代码实现:

python 复制代码
# 在Encoder/Decoder层中的使用
def forward(self, x):
    # 残差连接 + Layer Norm + 注意力
    x = x + self.attention(self.layer_norm1(x))
    
    # 残差连接 + Layer Norm + 前馈网络
    x = x + self.ffn(self.layer_norm2(x))
    
    return x

3.5 Encoder的实现

Encoder由多个Encoder层堆叠而成,每个Encoder层包含:

  1. 多头自注意力层
  2. 前馈神经网络
  3. 层归一化和残差连接
3.5.1 Encoder Layer实现
python 复制代码
import torch
import torch.nn as nn

class EncoderLayer(nn.Module):
    """Transformer Encoder层"""
    
    def __init__(self, n_embd, n_heads, hidden_dim, dropout=0.1):
        """
        参数:
            n_embd: 嵌入维度
            n_heads: 注意力头数
            hidden_dim: FFN隐藏层维度
            dropout: dropout比率
        """
        super().__init__()
        # 第一个Layer Norm(用于注意力层之前)
        self.attention_norm = LayerNorm(n_embd)
        # 多头自注意力层(不使用掩码)
        self.attention = MultiHeadAttention(n_embd, n_heads, dropout, is_causal=False)
        
        # 第二个Layer Norm(用于FFN之前)
        self.ffn_norm = LayerNorm(n_embd)
        # 前馈神经网络
        self.ffn = FeedForward(n_embd, hidden_dim, dropout)
    
    def forward(self, x):
        """
        前向传播
        
        参数:
            x: 输入,shape = (batch_size, seq_len, n_embd)
        返回:
            输出,shape = (batch_size, seq_len, n_embd)
        """
        # 第一个子层:Layer Norm -> 自注意力 -> 残差连接
        norm_x = self.attention_norm(x)
        x = x + self.attention(norm_x, norm_x, norm_x)
        
        # 第二个子层:Layer Norm -> FFN -> 残差连接
        x = x + self.ffn(self.ffn_norm(x))
        
        return x
3.5.2 完整Encoder实现
python 复制代码
class Encoder(nn.Module):
    """Transformer Encoder"""
    
    def __init__(self, n_layers, n_embd, n_heads, hidden_dim, dropout=0.1):
        """
        参数:
            n_layers: Encoder层的数量
            n_embd: 嵌入维度
            n_heads: 注意力头数
            hidden_dim: FFN隐藏层维度
            dropout: dropout比率
        """
        super().__init__()
        # 堆叠多个Encoder层
        self.layers = nn.ModuleList([
            EncoderLayer(n_embd, n_heads, hidden_dim, dropout)
            for _ in range(n_layers)
        ])
        # 最后的Layer Norm
        self.norm = LayerNorm(n_embd)
    
    def forward(self, x):
        """
        前向传播
        
        参数:
            x: 输入,shape = (batch_size, seq_len, n_embd)
        返回:
            编码结果,shape = (batch_size, seq_len, n_embd)
        """
        # 依次通过每个Encoder层
        for layer in self.layers:
            x = layer(x)
        
        # 最后的归一化
        return self.norm(x)


3.6 Decoder的实现

Decoder与Encoder类似,但有三个关键区别:

  1. 第一个注意力层使用掩码:防止看到未来信息
  2. 第二个注意力层是交叉注意力:Query来自Decoder,Key和Value来自Encoder输出
  3. 用于自回归生成:逐个生成输出token
3.6.1 Decoder Layer实现
python 复制代码
class DecoderLayer(nn.Module):
    """Transformer Decoder层"""
    
    def __init__(self, n_embd, n_heads, hidden_dim, dropout=0.1):
        """
        参数:
            n_embd: 嵌入维度
            n_heads: 注意力头数
            hidden_dim: FFN隐藏层维度
            dropout: dropout比率
        """
        super().__init__()
        # 第一个子层:掩码自注意力
        self.self_attn_norm = LayerNorm(n_embd)
        self.self_attn = MultiHeadAttention(n_embd, n_heads, dropout, is_causal=True)
        
        # 第二个子层:交叉注意力(与Encoder输出交互)
        self.cross_attn_norm = LayerNorm(n_embd)
        self.cross_attn = MultiHeadAttention(n_embd, n_heads, dropout, is_causal=False)
        
        # 第三个子层:前馈网络
        self.ffn_norm = LayerNorm(n_embd)
        self.ffn = FeedForward(n_embd, hidden_dim, dropout)
    
    def forward(self, x, encoder_output):
        """
        前向传播
        
        参数:
            x: Decoder输入,shape = (batch_size, tgt_seq_len, n_embd)
            encoder_output: Encoder输出,shape = (batch_size, src_seq_len, n_embd)
        返回:
            输出,shape = (batch_size, tgt_seq_len, n_embd)
        """
        # 第一个子层:掩码自注意力
        # Query、Key、Value都来自x(目标序列)
        norm_x = self.self_attn_norm(x)
        x = x + self.self_attn(norm_x, norm_x, norm_x)
        
        # 第二个子层:交叉注意力
        # Query来自x,Key和Value来自encoder_output
        norm_x = self.cross_attn_norm(x)
        x = x + self.cross_attn(norm_x, encoder_output, encoder_output)
        
        # 第三个子层:前馈网络
        x = x + self.ffn(self.ffn_norm(x))
        
        return x
3.6.2 完整Decoder实现
python 复制代码
class Decoder(nn.Module):
    """Transformer Decoder"""
    
    def __init__(self, n_layers, n_embd, n_heads, hidden_dim, dropout=0.1):
        """
        参数:
            n_layers: Decoder层的数量
            n_embd: 嵌入维度
            n_heads: 注意力头数
            hidden_dim: FFN隐藏层维度
            dropout: dropout比率
        """
        super().__init__()
        # 堆叠多个Decoder层
        self.layers = nn.ModuleList([
            DecoderLayer(n_embd, n_heads, hidden_dim, dropout)
            for _ in range(n_layers)
        ])
        # 最后的Layer Norm
        self.norm = LayerNorm(n_embd)
    
    def forward(self, x, encoder_output):
        """
        前向传播
        
        参数:
            x: Decoder输入,shape = (batch_size, tgt_seq_len, n_embd)
            encoder_output: Encoder输出,shape = (batch_size, src_seq_len, n_embd)
        返回:
            解码结果,shape = (batch_size, tgt_seq_len, n_embd)
        """
        # 依次通过每个Decoder层
        for layer in self.layers:
            x = layer(x, encoder_output)
        
        # 最后的归一化
        return self.norm(x)
相关推荐
liliangcsdn3 小时前
基于ollama运行27b gemma3解决ffmpeg命令生成问题
人工智能·ffmpeg
云雾J视界3 小时前
算法偏见的解药:将敏捷“灵魂”注入AI伦理
人工智能·算法·谷歌·伦理债·算法偏见·高效程序员·ai决策系统
码界奇点4 小时前
京东JoyAgent-JDGenie开源多智能体系统如何重塑AI应用落地新范式
人工智能·ai·智能手机·开源
镰刀韭菜4 小时前
【AI4S】大语言模型与化学的未来,以及整合外部工具和聊天机器人的潜力
llm·transformer·大语言模型·药物设计·分子发现·chemchat·smiles
小Tomkk4 小时前
AI 提效:利用 AI 从前端 快速转型为UI/UX设计师和产品
前端·人工智能·ui
王哥儿聊AI4 小时前
CompLLM 来了:长文本 Q&A 效率革命,线性复杂度 + 缓存复用,推理速度与效果双丰收
人工智能·深度学习·机器学习·语言模型
minhuan4 小时前
构建AI智能体:四十六、Codebuddy MCP 实践:用高德地图搭建旅游攻略系统
人工智能·mcp·codebuddy·高德api
青云交4 小时前
Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频语义理解与智能检索进阶
java·深度学习·监控系统·行为识别·智能安防·智能检索·视频语义理解
不当菜鸡的程序媛5 小时前
https://duoke360.com/post/35063
人工智能