大模型原理与实践:第二章-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)
相关推荐
出门吃三碗饭1 小时前
如何在LLM大语言模型上微调来优化数学推理能力?
android·人工智能·语言模型
小白狮ww1 小时前
清华联合字节推出 HuMo,实现三模态协同生成人物视频
人工智能·深度学习·机器学习·音视频·视频生成·多模态模型·人物视频
RAG专家3 小时前
【Mixture-of-RAG】将文本和表格与大型语言模型相结合
人工智能·语言模型·rag·检索增强生成
星期天要睡觉6 小时前
自然语言处理(NLP)——自然语言处理原理、发展历程、核心技术
人工智能·自然语言处理
低音钢琴6 小时前
【人工智能系列:机器学习学习和进阶01】机器学习初学者指南:理解核心算法与应用
人工智能·算法·机器学习
大千AI助手7 小时前
Hoeffding树:数据流挖掘中的高效分类算法详解
人工智能·机器学习·分类·数据挖掘·流数据··hoeffding树
新知图书7 小时前
大模型微调定义与分类
人工智能·大模型应用开发·大模型应用
山烛7 小时前
一文读懂YOLOv4:目标检测领域的技术融合与性能突破
人工智能·yolo·目标检测·计算机视觉·yolov4
大千AI助手7 小时前
独热编码:分类数据处理的基石技术
人工智能·机器学习·分类·数据挖掘·特征工程·one-hot·独热编码
钱彬 (Qian Bin)8 小时前
项目实践4—全球证件智能识别系统(Qt客户端开发+FastAPI后端人工智能服务开发)
人工智能·qt·fastapi