大模型学习入门——Day4: Encoder-Decoder

笔记参考教程------DataWhale快乐学习LLM

Seq2Seq,即序列到序列,是一种经典 NLP 任务。具体而言,是指模型输入的是一个自然语言序列 x 1 , x 2 , x 3 , . . . x_1, x_2, x_3,... x1,x2,x3,...,然后输出一个序列(长度可能不等)。事实上,Seq2Seq 是 NLP 最经典的任务,几乎所有的 NLP 任务都可以视为 Seq2Seq 任务。但这一看似直观的任务背后,隐藏着怎样的技术实现?为什么 Encoder-Decoder 结构会成为 Seq2Seq 的标配设计?本文将拆解这一经典框架,探讨它是如何通过'编码-解码'的协作,让机器学会'理解'并'生成'自然语言的。

Encoder 和 Decoder

一、把大象装进冰箱:核心思想

想象一下,你要请一位不精通中文的朋友帮你取一本中文书,书名是《时间简史》。你该怎么做?

  1. 你(编码器 - Encoder) :你不会逐字翻译"时间简史"的发音。相反,你会先理解这本书的核心信息------"一本关于宇宙和时间的科普书,作者是霍金,封面是黑色的,有个漩涡"。你把这些复杂的信息编码成一个简洁、浓缩的"思想摘要"。
  2. 思想摘要(上下文向量 - Context Vector):这就是你脑中的那个核心概念。
  3. 你的朋友(解码器 - Decoder) :他接收到你这个"思想摘要"后,开始解码。他根据这个摘要,在他的知识库里搜索,然后用他自己的语言(比如肢体动作和简单的英文单词)来一步步确认:"Ah, physics book? Hawking? Black cover?"。最终,他找到了正确的书。

这个过程完美地诠释了Encoder-Decoder架构的精髓:

  • Encoder (编码器) :负责"阅读"和"理解"输入序列(比如一句中文),并将其压缩成一个固定长度的、蕴含了全部语义的数学向量------上下文向量 (Context Vector, C)。这个向量就像是我们前面提到的"思想摘要"。
  • Decoder (解码器):负责接收这个"思想摘要" (Context Vector),然后根据它,一步步地生成目标序列(比如一句英文)。

这个"一压一解"的框架,可以处理一个可变长度的输入序列,并生成一个可变长度的Output序列,因此在许多领域大放异彩:

  • 机器翻译:输入中文,输出英文。
  • 文本摘要:输入长篇文章,输出简短摘要。
  • 对话系统(聊天机器人):输入一个问题,输出一个回答。
  • 图像描述:输入一张图片(用CNN编码),输出一句话描述。

二、深入内部:Encoder和Decoder的实现

一、基础模块:前馈神经网络 (FFN)

前馈神经网络是最基本的神经网络结构之一,由若干层神经元组成,信息单向流动。每一个 Encoder Layer 都包含一个上文讲的注意力机制和一个前馈神经网络。在Encoder-Decoder架构中,FFN常用于对每个位置的隐藏状态进行非线性变换。

公式:

一个两层的前馈神经网络可以表示为:

FFN ( x ) = W 2 ⋅ σ ( W 1 ⋅ x + b 1 ) + b 2 \text{FFN}(x) = W_2 \cdot \sigma(W_1 \cdot x + b_1) + b_2 FFN(x)=W2⋅σ(W1⋅x+b1)+b2

其中:

  • x x x 是输入向量。
  • W _ 1 W\_1 W_1 和 b _ 1 b\_1 b_1 是第一层线性变换的权重矩阵和偏置向量。
  • s i g m a \\sigma sigma 是激活函数(例如 ReLU)。
  • W _ 2 W\_2 W_2 和 b _ 2 b\_2 b_2 是第二层线性变换的权重矩阵和偏置向量。

PyTorch代码:

以下是一个简单的两层前馈神经网络的PyTorch实现:

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


class FeedForwardNetwork(nn.Module):
 def __init__(self, input_dim, hidden_dim, output_dim, dropout=0.1):
 super(FeedForwardNetwork, self).__init__()
 self.linear1 = nn.Linear(input_dim, hidden_dim)
 self.relu = nn.ReLU()
 self.dropout = nn.Dropout(dropout)
 self.linear2 = nn.Linear(hidden_dim, output_dim)


 def forward(self, x):
 x = self.linear1(x)
 x = self.relu(x)
 x = self.linear2(x)
 x = self.dropout(x)
 return x


# 示例
input_dim = 512
hidden_dim = 2048
output_dim = 512
ffn = FeedForwardNetwork(input_dim, hidden_dim, output_dim)
input_tensor = torch.randn(32, 50, input_dim) # (batch_size, seq_len, input_dim)
output_tensor = ffn(input_tensor)
print("FFN Output Shape:", output_tensor.shape)

在Encoder和Decoder的每个块中,FFN通常应用于注意力机制的输出,以进一步提取特征。

二、加速训练:层归一化 (Layer Normalization)

归一化核心是为了让不同层输入的取值范围或者分布能够比较一致。由于深度神经网络中每一层的输入都是上一层的输出,因此多层传递下,对网络中较高的层,之前的所有神经层的参数变化会导致其输入的分布发生较大的改变。也就是说,随着神经网络参数的更新,各层的输出分布是不相同的,且差异会随着网络深度的增大而增大。但是,需要预测的条件分布始终是相同的,从而也就造成了预测的误差。

层归一化是一种归一化技术,它在每个样本的特征维度 上计算均值和标准差,并对该样本的所有特征进行归一化。这有助于缓解内部协变量偏移问题,加速模型收敛,并提高训练的稳定性。
公式:

给定一个输入向量 x = ( x _ 1 , x _ 2 , . . . , x _ D ) x = (x\_1, x\_2, ..., x\_D) x=(x_1,x_2,...,x_D),层归一化的计算过程如下:

  1. 计算均值 μ \mu μ:
    μ = 1 D ∑ x i \mu = \frac{1}{D} \sum{x_i} μ=D1∑xi

  2. 计算标准差 σ \sigma σ:
    σ = 1 D ∑ ( x i − μ ) 2 \sigma = \sqrt{\frac{1}{D} \sum{(x_i-\mu)^2}} σ=D1∑(xi−μ)2

  3. 归一化:
    x ^ _ i = x _ i − μ σ 2 + ϵ \hat{x}\_i = \frac{x\_i - \mu}{\sqrt{\sigma^2+\epsilon}} x^_i=σ2+ϵ x_i−μ
    ϵ \epsilon ϵ是一个很小的正数,防止除数为0

  4. 缩放和平移(可选,模型学习参数 γ \gamma γ 和 β \beta β):
    y _ i = γ x ^ _ i + β y\_i = \gamma \hat{x}\_i + \beta y_i=γx^_i+β

PyTorch代码:

python 复制代码
class LayerNorm(nn.Module):
 def __init__(self, features_dim, epsilon=1e-6):
 super(LayerNorm, self).__init__()
 self.gamma = nn.Parameter(torch.ones(features_dim))
 self.beta = nn.Parameter(torch.zeros(features_dim))
 self.epsilon = epsilon


 def forward(self, x):
 mean = x.mean(-1, keepdim=True)
 std = x.std(-1, keepdim=True)
 normalized_x = (x - mean) / (std + self.epsilon)
 return self.gamma * normalized_x + self.beta


# 示例
features_dim = 512
layer_norm = LayerNorm(features_dim)
input_tensor = torch.randn(32, 50, features_dim)
output_tensor = layer_norm(input_tensor)
print("LayerNorm Output Shape:", output_tensor.shape)

在Encoder-Decoder架构中,LayerNorm通常应用于每个子层的输入和输出(例如,在自注意力之后、FFN之后),并结合残差连接。

三、构建更深的网络:残差连接 (Residual Connection)

随着网络层数的加深,梯度消失或梯度爆炸以及网络退化问题会变得更加严重。残差连接通过将浅层的输入直接添加到深层的输出来缓解这些问题。

公式:

一个包含残差连接的模块可以表示为:

Output = SubLayer ( x ) + x \text{Output} = \text{SubLayer}(x) + x Output=SubLayer(x)+x

其中:

  • x x x 是子层的输入。

  • SubLayer ( x ) \text{SubLayer}(x) SubLayer(x) 是子层(例如,自注意力或FFN)的输出。

  • Output \text{Output} Output 是残差连接后的输出。

    注意力计算

    h = x + self.attention.forward(self.attention_norm(x))

    经过前馈神经网络

    out = h + self.feed_forward.forward(self.fnn_norm(h))

在Transformer等现代Encoder-Decoder架构中,每个Encoder和Decoder层都包含多个子层(如自注意力和FFN),并且每个子层都使用了残差连接和层归一化。

在上文代码中,self.attention_norm 和 self.fnn_norm 都是 LayerNorm 层,self.attn 是注意力层,而 self.feed_forward 是前馈神经网络。

四、上下文的理解者:Encoder

Encoder的任务是将输入的序列(例如,一个句子中的单词序列)转换成一个固定长度的上下文向量(或一系列上下文向量),该向量应该捕获输入序列的关键信息。

在Transformer中,Encoder由多个相同的Encoder层堆叠而成。每个Encoder层通常包含以下子层:

  1. 多头自注意力 (Multi-Head Self-Attention):让模型能够关注输入序列中的不同位置,捕捉词语之间的依赖关系。
  2. 残差连接和层归一化 (Add & Norm):将自注意力的输出与输入相加,并进行层归一化。
  3. 前馈神经网络 (Feed-Forward Network):对每个位置的表示进行独立的非线性变换。
  4. 残差连接和层归一化 (Add & Norm) :将FFN的输出与输入相加,并进行层归一化。
    简化版Encoder代码框架:
python 复制代码
class EncoderLayer(nn.Module):
 def __init__(self, dim, self_attn, feed_forward, dropout):
 super(EncoderLayer, self).__init__()
 self.self_attn = self_attn
 self.feed_forward = feed_forward
 self.norm1 = LayerNorm(dim)
 self.norm2 = LayerNorm(dim)
 self.dropout = nn.Dropout(dropout)


 def forward(self, x, mask):
 # Self-Attention
 attn_output = self.self_attn(x, x, x, mask=mask) # Query, Key, Value
 x = x + self.dropout(self.norm1(attn_output))
 # Feed-Forward
 ff_output = self.feed_forward(x)
 x = x + self.dropout(self.norm2(ff_output))
 return x


class Encoder(nn.Module):
 def __init__(self, num_layers, dim, self_attn, feed_forward, dropout):
 super(Encoder, self).__init__()
 self.layers = nn.ModuleList([
 EncoderLayer(dim, self_attn, feed_forward, dropout)
 for _ in range(num_layers)
 ])
 self.norm = LayerNorm(dim)


 def forward(self, x, mask):
 for layer in self.layers:
 x = layer(x, mask)
 return self.norm(x)


# 注意:这里的 self_attn 和 feed_forward 需要是实际的模块实例

Encoder的最终输出是一系列与输入序列长度相同的上下文向量,每个向量都编码了输入序列在对应位置周围的信息。

五、目标序列的生成器:Decoder

Decoder的任务是接收Encoder产生的上下文信息,并逐步生成目标输出序列。

与Encoder类似,Transformer中的Decoder也由多个相同的Decoder层堆叠而成。每个Decoder层通常包含以下子层:

  1. 带掩码的多头自注意力 (Masked Multi-Head Self-Attention):与Encoder的自注意力类似,但引入了掩码机制,防止Decoder在生成某个位置的词语时"看到"未来的词语。这保证了Decoder是自回归的。
  2. 残差连接和层归一化 (Add & Norm)
  3. 多头注意力 (Multi-Head Attention) 对Encoder输出的注意力:Decoder的这一层接收Decoder前一层的输出和Encoder的输出,让Decoder能够关注输入序列的相关部分,类似于传统Encoder-Decoder模型中的注意力机制。
  4. 残差连接和层归一化 (Add & Norm)
  5. 前馈神经网络 (Feed-Forward Network)
  6. 残差连接和层归一化 (Add & Norm)

简化版Decoder代码框架:

python 复制代码
class DecoderLayer(nn.Module):
 def __init__(self, dim, self_attn, cross_attn, feed_forward, dropout):
 super(DecoderLayer, self).__init__()
 self.self_attn = self_attn
 self.cross_attn = cross_attn
 self.feed_forward = feed_forward
 self.norm1 = LayerNorm(dim)
 self.norm2 = LayerNorm(dim)
 self.norm3 = LayerNorm(dim)
 self.dropout = nn.Dropout(dropout)


 def forward(self, x, memory, src_mask, tgt_mask):
 # Masked Self-Attention
 attn1_output = self.self_attn(x, x, x, mask=tgt_mask)
 x = x + self.dropout(self.norm1(attn1_output))
 # Attention over Encoder output (memory)
 attn2_output = self.cross_attn(x, memory, memory, mask=src_mask)
 x = x + self.dropout(self.norm2(attn2_output))
 # Feed-Forward
 ff_output = self.feed_forward(x)
 x = x + self.dropout(self.norm3(ff_output))
 return x


class Decoder(nn.Module):
 def __init__(self, num_layers, dim, self_attn, cross_attn, feed_forward, dropout):
 super(Decoder, self).__init__()
 self.layers = nn.ModuleList([
 DecoderLayer(dim, self_attn, cross_attn, feed_forward, dropout)
 for _ in range(num_layers)
 ])
 self.norm = LayerNorm(dim)


 def forward(self, x, memory, src_mask, tgt_mask):
 for layer in self.layers:
 x = layer(x, memory, src_mask, tgt_mask)
 return self.norm(x)


# 注意:这里的 self_attn, cross_attn 和 feed_forward 需要是实际的模块实例

Decoder的输入是目标序列的embedding(通常会进行移位和掩码操作),以及Encoder的输出(通常称为memory)。Decoder逐步生成目标序列,直到生成结束符。

相关推荐
warm3snow4 天前
AI 核心技能系列:12 篇文章带你系统掌握大模型岗位必备技能
ai·transformer·agent·skill·mcp·fine-tunning
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
homelook5 天前
Transformer与电池管理系统(BMS)的结合是当前 智能电池管理 的前沿研究方向
人工智能·深度学习·transformer
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码5 天前
嵌入式学习路线
学习
毛小茛5 天前
计算机系统概论——校验码
学习
babe小鑫5 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms5 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下5 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。5 天前
2026.2.25监控学习
学习