本文主要是介绍9-6 编码器-解码器架构,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
正如我们在 9-5节中所讨论的, 机器翻译是序列转换模型的一个核心问题, 其输入和输出都是长度可变的序列。 为了处理这种类型的输入和输出, 我们可以设计一个包含两个主要组件的架构: 第一个组件是一个编码器(encoder): 它接受一个长度可变的序列作为输入, 并将其转换为具有固定形状的编码状态。 第二个组件是解码器(decoder): 它将固定形状的编码状态映射到长度可变的序列。 这被称为编码器-解码器(encoder-decoder)架构, 如 图9.6.1 所示。
我们以英语到法语的机器翻译为例: 给定一个英文的输入序列:“They”“are”“watching”“.”
。 首先,这种“编码器-解码器”架构将长度可变的输入序列编码成一个“状态”, 然后对该状态进行解码, 一个词元接着一个词元地生成翻译后的序列作为输出: “Ils”“regordent”“.”
。 由于“编码器-解码器”架构是形成后续章节中不同序列转换模型的基础, 因此本节将把这个架构转换为接口方便后面的代码实现。
编码器
在编码器接口中,我们只指定长度可变的序列作为编码器的输入X
。 任何继承这个Encoder基类的模型将完成代码实现。
from torch import nnclass Encoder(nn.Module):"""编码器-解码器架构的基本编码器接口"""def __init__(self, **kwargs):# **kwargs 允许传递任意数量的关键字参数给 __init__,这样可以灵活地配置编码器。super(Encoder, self).__init__(**kwargs)# super(Encoder, self).__init__(**kwargs) 调用了父类 nn.Module 的构造函数,确保 nn.Module 的初始化过程正常执行,并传递 kwargs 中的参数。通过 super() 调用父类的构造函数是继承类中常见的做法,目的是确保继承的特性和行为能够正确初始化def forward(self, X, *args):# *args 允许传入任意数量的其他位置参数,这些参数可能在实际实现中需要。raise NotImplementedError# raise NotImplementedError 表示这个方法还没有实现,它是一个占位符,提醒开发者在使用该类时必须在子类中实现这个方法。如果有人直接调用 Encoder 类的 forward 方法,程序将抛出 NotImplementedError 错误,提示这个方法未被实现。
解码器
在下面的解码器接口中,我们新增一个init_state
函数, 用于将编码器的输出(enc_outputs
)转换为编码后的状态。 注意,此步骤可能需要额外的输入,例如:输入序列的有效长度, 这在 9.5节中进行了解释。 为了逐个地生成长度可变的词元序列, 解码器在每个时间步都会将输入 (例如:在前一时间步生成的词元)和编码后的状态 映射成当前时间步的输出词元。
#@save
class Decoder(nn.Module):"""编码器-解码器架构的基本解码器接口"""def __init__(self, **kwargs):super(Decoder, self).__init__(**kwargs)def init_state(self, enc_outputs, *args):raise NotImplementedErrordef forward(self, X, state):raise NotImplementedError
合并编码器和解码器
总而言之,“编码器-解码器”架构包含了一个编码器和一个解码器, 并且还拥有可选的额外的参数。 在前向传播中,编码器的输出用于生成编码状态, 这个状态又被解码器作为其输入的一部分。
class EncoderDecoder(nn.Module):"""编码器-解码器架构的基类"""def __init__(self, encoder, decoder, **kwargs):super(EncoderDecoder, self).__init__(**kwargs)self.encoder = encoderself.decoder = decoderdef forward(self, enc_X, dec_X, *args):# enc_X 是编码器的输入,通常是一个序列数据,例如句子或时间序列。# dec_X 是解码器的输入,通常是目标序列的起始部分,或在某些情况下是整个目标序列。enc_outputs = self.encoder(enc_X, *args)# 调用编码器的 forward 方法,将 enc_X 及其他参数传递给编码器。enc_outputs 是编码器的输出,通常是对输入 enc_X 的一种编码表示(例如,隐藏状态)。dec_state = self.decoder.init_state(enc_outputs, *args)# 调用解码器的 init_state 方法,以编码器的输出 enc_outputs 为输入,初始化解码器的状态。dec_state 通常包含解码器的初始隐藏状态或其他需要在解码过程中使用的上下文信息。return self.decoder(dec_X, dec_state)# 将解码器的输入 dec_X 和初始状态 dec_state 传递给解码器,调用解码器的 forward 方法,输出解码结果。这通常是目标序列的预测或生成。# forward 方法描述了输入数据如何在整个架构中流动。编码器首先处理输入数据,生成编码输出。然后解码器基于编码输出和解码器的初始状态生成目标序列的输出。
“编码器-解码器”体系架构中的术语状态 会启发人们使用具有状态的神经网络来实现该架构。 在下一节中,我们将学习如何应用循环神经网络, 来设计基于“编码器-解码器”架构的序列转换模型。
小结
-
“编码器-解码器”架构可以将长度可变的序列作为输入和输出,因此适用于机器翻译等序列转换问题。
-
编码器将长度可变的序列作为输入,并将其转换为具有固定形状的编码状态。
-
解码器将具有固定形状的编码状态映射为长度可变的序列。
这篇关于9-6 编码器-解码器架构的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!