1. Encoder-Decoder 架构
Encoder-Decoder 架构最早由 Sutskever 等人在 2014 年的论文《Sequence to Sequence Learning with Neural Networks》中提出。这个架构最初是为了解决序列到序列(Seq2Seq)任务,尤其是机器翻译问题。该架构使用了基于 RNN(尤其是 LSTM)的编码器和解码器结构:
- 编码器(Encoder):将输入序列(如句子)编码为一个固定长度的隐藏状态。
- 解码器(Decoder):根据编码器生成的隐藏状态逐步生成目标序列(如翻译后的句子)。
这种结构解决了传统机器学习方法难以处理变长输入和输出序列的问题,并且迅速被应用到机器翻译、文本摘要、问答系统等任务中。
1.1 Encoder(编码器)
编码器的任务是将输入序列编码成一个固定长度的向量表示(即上下文向量)。该部分通常由递归神经网络(RNN)、长短时记忆网络(LSTM)或门控循环单元(GRU)组成,负责逐步读取输入序列。
- 编码器逐个处理输入序列中的元素(如文本中的单词),并将每一步的信息编码进其隐藏状态。
- 最后一步的隐藏状态将作为编码器的输出,即上下文向量,它总结了整个输入序列的信息。
1.2 Decoder(解码器)
解码器接收从编码器传递过来的上下文向量,并根据该向量生成输出序列。解码器也是由RNN、LSTM或GRU等神经网络结构组成,它使用上下文向量以及之前生成的输出序列来预测接下来的输出元素。
- 解码器逐步生成输出序列,在每个时间步生成一个输出。
- 每个时间步解码器的输入是前一步生成的输出以及隐藏状态。
2. 工作流程
- 编码阶段:编码器逐步处理输入序列,将其转换为隐藏状态,最后将整个输入序列的信息编码为固定长度的上下文向量。
- 解码阶段:解码器接收上下文向量,并逐步生成目标序列。初始输入通常是特殊的开始标记
<SOS>
,然后解码器在每个时间步生成一个新的输出元素。 - 预测下一个输出:每一个新的输出都是基于上下文向量和先前生成的输出来预测的。
机器翻译代码示例:
import torch
import torch.nn as nn
import torch.optim as optim
import random
# Encoder(编码器)
class Encoder(nn.Module):
def __init__(self, input_size, hidden_size, num_layers=1):
super(Encoder, self).__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
self.embedding = nn.Embedding(input_size, hidden_size)
self.lstm = nn.LSTM(hidden_size, hidden_size, num_layers)
def forward(self, input_seq, hidden_state):
# input_seq shape: (seq_len, batch_size)
embedded = self.embedding(input_seq)
# embedded shape: (seq_len, batch_size, hidden_size)
output, (hidden, cell) = self.lstm(embedded, hidden_state)
return output, (hidden, cell)
def init_hidden(self, batch_size, device):
return (torch.zeros(self.num_layers, batch_size, self.hidden_size).to(device),
torch.zeros(self.num_layers, batch_size, self.hidden_size).to(device))
# Decoder(解码器)
class Decoder(nn.Module):
def __init__(self, output_size, hidden_size, num_layers=1):
super(Decoder, self).__init__()
self.hidden_size = hidden_size
self.num_layers = num_layers
self.embedding = nn.Embedding(output_size, hidden_size)
self.lstm = nn.LSTM(hidden_size, hidden_size, num_layers)
self.fc = nn.Linear(hidden_size, output_size)
self.softmax = nn.LogSoftmax(dim=1)
def