1. Encoder 和 Decoder 概述
在序列到序列(sequence-to-sequence,简称 seq2seq)的模型中,整个系统通常分为两大部分:Encoder(编码器)和 Decoder(解码器)。
- Encoder(编码器):
输入一段序列(例如一句话),经过编码器(一般使用 RNN、LSTM、GRU 或者 Transformer Encoder)后,将整个序列的信息压缩成一个(或多个)固定维度的向量表示,这个向量通常被认为是整个输入序列的语义表示。 - Decoder(解码器):
解码器接收 Encoder 输出的向量信息(可以是单个向量,也可以是多个隐藏状态组成的序列),再利用另一个 RNN(或其他结构)生成目标序列。例如,对于机器翻译任务,Decoder 会利用编码得到的源语言表示生成目标语言句子。
这种 Encoder-Decoder 结构最初由 Sutskever 等人(2014)的 seq2seq 模型成功应用于机器翻译任务,开创了自然语言生成新纪元。
2. Encoder 部分详细说明
2.1 Encoder 的基本角色
Encoder 的任务是将输入序列(如一整句话、一个段落或整个文档)转换成一个紧凑的向量表示。这一步骤通常有以下特点:
- 信息汇聚:
Encoder 通常会逐步处理输入序列(例如使用 RNN 从左到右遍历),在每一步都更新隐藏状态,最终在序列结束时获得一个包含全局信息的向量(或一组向量)。 - 使用最后的隐藏状态或全序列:
有时,简单的做法是取 RNN 在最后一个时刻的隐藏状态作为整个序列的表示(此时称为"accepter",即仅用最后一个状态来生成预测)。但更复杂的 Encoder 会输出整个隐藏状态序列,并将其提供给 Decoder,在解码过程中每一步都可利用(见第 5 部分)。
2.2 双向 Encoder 和多层结构
- 双向 RNN:
为了更全面地捕捉输入句子的上下文信息,Encoder 常常使用双向 RNN(BiRNN),即同时利用从左到右的(正向)和从右到左的(反向)信息,最后将两边隐藏状态拼接或加权融合,得到更为丰富的文本表示。 - 多层模型:
为了提高表示能力,还会采用多层(堆叠)RNN,每一层可以捕捉不同层次(如语法、语义)的信息。实际系统中可能会有 2 层、甚至更多层的 Encoder。
例如,机器翻译中常见的设置包括 2 个 Encoder 层(甚至可以把双向 RNN 作为第一层,后面再堆叠一层单向或双向 RNN),以确保充分捕捉输入句子的细节信息。
3. Decoder 部分详细说明
3.1 Decoder 的基本角色与架构
Decoder 的任务是生成一个序列(例如翻译句子、摘要、对话回复或代码),其工作流程如下:
- 接收 Encoder 的输出:
Decoder 通常接收 Encoder 输出的向量表示(可能是单个向量,也可能是整个隐藏状态序列),然后将其作为生成目标序列的初始信息或在每一步的条件输入。 - RNN 解码器:
最常见的 Decoder 是基于 RNN(或其变种,如 LSTM、GRU),每一步输出一个 token,并更新自身状态,然后继续生成下一个 token。
3.2 解码时如何决定停止
生成序列时需要一个停止条件来决定何时结束输出。常用的策略有两种:
- 固定长度输出:
预先设定生成的 token 数量,例如固定生成 10 个词。这种方法简单但缺乏灵活性。 - 特殊停止符( <EOS> 或 "stop")输出:
在训练时设置一个特殊的停止标记,当 Decoder 生成这个标记时,认为序列结束。这种方法更灵活,适应于长度可变的任务,如机器翻译和对话生成。
实际模型往往采用后者,即在输出词汇表中包含一个停止符,解码过程持续直到生成停止符。
3.3 Teacher Forcing (教师强制)
- 定义:
教师强制是一种训练技巧,在训练 Decoder 时,将真实的目标 token 作为下一步的输入(而不是使用 Decoder 自己生成的 token),以加速训练收敛并稳定训练过程。 - 作用:
通过教师强制,Decoder 在初期训练阶段可以直接获得正确的上下文信息,提高生成序列的质量;同时,训练过程中可以逐渐减少教师强制的比例,逼迫模型在推断时依赖自身生成的序列。
3.4 Beam Search 与评分调整
在测试时,为了生成高质量的输出序列,常用的方法是 Beam Search:
- Beam Search:
在每一步解码时,保留概率最高的 N 个候选序列而非仅选取最优的一个。这样可以在后续生成过程中通过搜索多个候选序列来避免局部最优。 - 长度归一化:
由于较长的序列总的概率(对数和)较小,实际评分时通常会调整为每个词的平均概率(例如 1 ∣ w o r d s ∣ ∑ log P ( w o r d ∣ c o n t e x t ) ∣ w o r d s ∣ \frac{1}{|words|}\sum \log P(word|context)∣words∣ ∣words∣1∑logP(word∣context)∣words∣这样能避免偏好生成太短的序列,从而获得更符合人类语言习惯的输出。
4. 典型应用场景
Encoder-Decoder 架构被广泛应用于各类 NLP 任务,以下是几个典型案例:
4.1 机器翻译
- 过程:
Encoder 将源语言句子转换成向量表示,Decoder 利用该表示生成目标语言句子。 - 成功案例:
Google 的 Neural Machine Translation(NMT)系统就是基于 Encoder-Decoder 架构实现的。
4.2 文本摘要
- 过程:
输入为长文档,通过 Encoder 编码,Decoder 生成该文档的简短摘要,保留关键信息。
4.3 对话生成(如 GPT)
- 过程:
模型接收对话历史(作为上下文输入),Decoder 生成下一句回复。这里往往采用较深的 Decoder 和大规模预训练方法。
4.4 代码生成
- 过程:
将自然语言的需求描述(request)经过 Encoder 得到语义表示,Decoder 根据该表示生成对应的代码。
5. 模型设计中的补充细节
5.1 Encoder 与 Decoder 的信息传递
最初的 Encoder-Decoder 模型存在"瓶颈"问题,即所有信息必须通过 Encoder 输出的那个固定向量传递,这往往导致信息不足。为了解决这一问题,后续改进方法包括:
- 注意力机制(Attention):
在每一步解码时,Decoder 不仅使用最后的编码向量,而是通过注意力机制对 Encoder 的所有输出进行加权求和,动态获取对当前生成最有帮助的信息。这样大大缓解了信息瓶颈问题,也使得模型能够更好地处理长序列。
5.2 多层和混合结构
- 多层 Encoder-Decoder:
可以使用多层(例如2层 Encoder 与 1层 Decoder、甚至 Encoder 与 Decoder 都为多层)提高模型表达能力,每一层可以处理不同层次的语言信息。 - 双向 Encoder:
尤其在 Encoder 部分,使用双向 RNN 可以更全面捕捉上下文;而 Decoder 部分通常采用单向生成,但可在输入(通过注意力)中引入 Encoder 输出的双向信息。
5.3 训练与推理的差异
- 训练阶段:
采用教师强制,使用真实的 token 作为 Decoder 的输入,可以快速收敛。 - 推断阶段:
则完全依赖模型自身生成的 token,由于前后依赖性强,每一步必须依据前一步的输出,导致难以并行计算。为此使用 Beam Search 等解码策略,但推断效率相对较低,是目前 Encoder-Decoder 模型在实际应用中常见的计算瓶颈问题。
6. 模型存在的问题与改进挑战
6.1 瓶颈问题
如前所述,Encoder-Decoder 模型要求 Encoder 将所有输入信息压缩到一个固定大小的向量中,这往往造成信息丢失。注意力机制、记忆网络以及使用层次化编码器都被提出用以缓解这一问题。
6.2 推理过程难以并行化
由于 Decoder 每一步生成都依赖前一步的输出(自回归),解码过程是串行的,因此难以利用现代硬件(如 GPU)的并行能力进行快速推理。Transformer 模型的设计在一定程度上可以并行化编码部分,但解码依旧存在自回归问题。
6.3 训练难度和解码策略
教师强制虽然加速了训练,但可能导致训练期间与推断期间的分布不匹配;同时,为了生成较优解码序列需要精心设计 Beam Search 的参数(如 beam width、长度归一化策略),这也增加了系统的复杂性与调优成本。
7. 实际案例演示(以机器翻译为例)
以机器翻译为例,我们可以看到 Encoder-Decoder 模型如何工作:
- Encoder 部分:
-- 输入:英文句子 "I love natural language processing."
-- 使用双向 LSTM 将整句话编码成一组隐藏状态(或经过注意力整合为一个上下文向量)。 - Decoder 部分:
-- 接收 Encoder 输出,开始生成目标语言句子(例如中文翻译)。 -- 采用教师强制在训练中,指导模型在每一步预测正确的中文词汇。 -- 在推断时,使用 Beam Search 选择多个候选词,直到生成特殊的结束符 ""。 - 训练与推断:
-- 训练中计算损失函数时通常采用交叉熵损失,对每个生成词的概率取对数后求和再求平均。
-- 在 Beam Search 时,对候选序列的总分调整为平均每个词的对数概率,以防短序列得分偏高。
这种完整的流程是机器翻译的早期成功案例,也为后来的对话生成、文本摘要等任务提供了模型框架。