【深度学习笔记】10_9 编码器—解码器(seq2seq)

注:本文为《动手学深度学习》开源内容,部分标注了个人理解,仅为个人学习记录,无抄袭搬运意图

10.9 编码器—解码器(seq2seq)

我们已经在前两节中表征并变换了不定长的输入序列。但在自然语言处理的很多应用中,输入和输出都可以是不定长序列。以机器翻译为例,输入可以是一段不定长的英语文本序列,输出可以是一段不定长的法语文本序列,例如

英语输入:“They”、“are”、“watching”、“.”

法语输出:“Ils”、“regardent”、“.”

当输入和输出都是不定长序列时,我们可以使用编码器—解码器(encoder-decoder)[1] 或者seq2seq模型 [2]。这两个模型本质上都用到了两个循环神经网络,分别叫做编码器和解码器。编码器用来分析输入序列,解码器用来生成输出序列。

图10.8描述了使用编码器—解码器将上述英语句子翻译成法语句子的一种方法。在训练数据集中,我们可以在每个句子后附上特殊符号“<eos>”(end of sequence)以表示序列的终止。编码器每个时间步的输入依次为英语句子中的单词、标点和特殊符号“<eos>”。图10.8中使用了编码器在最终时间步的隐藏状态作为输入句子的表征或编码信息。解码器在各个时间步中使用输入句子的编码信息和上个时间步的输出以及隐藏状态作为输入。我们希望解码器在各个时间步能正确依次输出翻译后的法语单词、标点和特殊符号"<eos>“。需要注意的是,解码器在最初时间步的输入用到了一个表示序列开始的特殊符号”<bos>"(beginning of sequence)。

在这里插入图片描述

图10.8 使用编码器—解码器将句子由英语翻译成法语。编码器和解码器分别为循环神经网络

接下来,我们分别介绍编码器和解码器的定义。

10.9.1 编码器

编码器的作用是把一个不定长的输入序列变换成一个定长的背景变量 c \boldsymbol{c} c,并在该背景变量中编码输入序列信息。常用的编码器是循环神经网络。

让我们考虑批量大小为1的时序数据样本。假设输入序列是 x 1 , … , x T x_1,\ldots,x_T x1,,xT,例如 x i x_i xi是输入句子中的第 i i i个词。在时间步 t t t,循环神经网络将输入 x t x_t xt的特征向量 x t \boldsymbol{x}_t xt和上个时间步的隐藏状态 h t − 1 \boldsymbol{h}_{t-1} ht1变换为当前时间步的隐藏状态 h t \boldsymbol{h}_t ht。我们可以用函数 f f f表达循环神经网络隐藏层的变换:

h t = f ( x t , h t − 1 ) . \boldsymbol{h}_t = f(\boldsymbol{x}_t, \boldsymbol{h}_{t-1}). ht=f(xt,ht1).

接下来,编码器通过自定义函数 q q q将各个时间步的隐藏状态变换为背景变量

c = q ( h 1 , … , h T ) . \boldsymbol{c} = q(\boldsymbol{h}_1, \ldots, \boldsymbol{h}_T). c=q(h1,,hT).

例如,当选择 q ( h 1 , … , h T ) = h T q(\boldsymbol{h}_1, \ldots, \boldsymbol{h}_T) = \boldsymbol{h}_T q(h1,,hT)=hT时,背景变量是输入序列最终时间步的隐藏状态 h T \boldsymbol{h}_T hT

以上描述的编码器是一个单向的循环神经网络,每个时间步的隐藏状态只取决于该时间步及之前的输入子序列。我们也可以使用双向循环神经网络构造编码器。在这种情况下,编码器每个时间步的隐藏状态同时取决于该时间步之前和之后的子序列(包括当前时间步的输入),并编码了整个序列的信息。

10.9.2 解码器

刚刚已经介绍,编码器输出的背景变量 c \boldsymbol{c} c编码了整个输入序列 x 1 , … , x T x_1, \ldots, x_T x1,,xT的信息。给定训练样本中的输出序列 y 1 , y 2 , … , y T ′ y_1, y_2, \ldots, y_{T'} y1,y2,,yT,对每个时间步 t ′ t' t(符号与输入序列或编码器的时间步 t t t有区别),解码器输出 y t ′ y_{t'} yt的条件概率将基于之前的输出序列 y 1 , … , y t ′ − 1 y_1,\ldots,y_{t'-1} y1,,yt1和背景变量 c \boldsymbol{c} c,即 P ( y t ′ ∣ y 1 , … , y t ′ − 1 , c ) P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \boldsymbol{c}) P(yty1,,yt1,c)

为此,我们可以使用另一个循环神经网络作为解码器。在输出序列的时间步 t ′ t^\prime t,解码器将上一时间步的输出 y t ′ − 1 y_{t^\prime-1} yt1以及背景变量 c \boldsymbol{c} c作为输入,并将它们与上一时间步的隐藏状态 s t ′ − 1 \boldsymbol{s}_{t^\prime-1} st1变换为当前时间步的隐藏状态 s t ′ \boldsymbol{s}_{t^\prime} st。因此,我们可以用函数 g g g表达解码器隐藏层的变换:

s t ′ = g ( y t ′ − 1 , c , s t ′ − 1 ) . \boldsymbol{s}_{t^\prime} = g(y_{t^\prime-1}, \boldsymbol{c}, \boldsymbol{s}_{t^\prime-1}). st=g(yt1,c,st1).

有了解码器的隐藏状态后,我们可以使用自定义的输出层和softmax运算来计算 P ( y t ′ ∣ y 1 , … , y t ′ − 1 , c ) P(y_{t^\prime} \mid y_1, \ldots, y_{t^\prime-1}, \boldsymbol{c}) P(yty1,,yt1,c),例如,基于当前时间步的解码器隐藏状态 s t ′ \boldsymbol{s}_{t^\prime} st、上一时间步的输出 y t ′ − 1 y_{t^\prime-1} yt1以及背景变量 c \boldsymbol{c} c来计算当前时间步输出 y t ′ y_{t^\prime} yt的概率分布。

10.9.3 训练模型

根据最大似然估计,我们可以最大化输出序列基于输入序列的条件概率

P ( y 1 , … , y T ′ ∣ x 1 , … , x T ) = ∏ t ′ = 1 T ′ P ( y t ′ ∣ y 1 , … , y t ′ − 1 , x 1 , … , x T ) = ∏ t ′ = 1 T ′ P ( y t ′ ∣ y 1 , … , y t ′ − 1 , c ) , \begin{aligned} P(y_1, \ldots, y_{T'} \mid x_1, \ldots, x_T) &= \prod_{t'=1}^{T'} P(y_{t'} \mid y_1, \ldots, y_{t'-1}, x_1, \ldots, x_T)\\ &= \prod_{t'=1}^{T'} P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \boldsymbol{c}), \end{aligned} P(y1,,yTx1,,xT)=t=1TP(yty1,,yt1,x1,,xT)=t=1TP(yty1,,yt1,c),

并得到该输出序列的损失

− log ⁡ P ( y 1 , … , y T ′ ∣ x 1 , … , x T ) = − ∑ t ′ = 1 T ′ log ⁡ P ( y t ′ ∣ y 1 , … , y t ′ − 1 , c ) , -\log P(y_1, \ldots, y_{T'} \mid x_1, \ldots, x_T) = -\sum_{t'=1}^{T'} \log P(y_{t'} \mid y_1, \ldots, y_{t'-1}, \boldsymbol{c}), logP(y1,,yTx1,,xT)=t=1TlogP(yty1,,yt1,c),

在模型训练中,所有输出序列损失的均值通常作为需要最小化的损失函数。在图10.8所描述的模型预测中,我们需要将解码器在上一个时间步的输出作为当前时间步的输入。与此不同,在训练中我们也可以将标签序列(训练集的真实输出序列)在上一个时间步的标签作为解码器在当前时间步的输入。这叫作强制教学(teacher forcing)。

小结

  • 编码器-解码器(seq2seq)可以输入并输出不定长的序列。
  • 编码器—解码器使用了两个循环神经网络。
  • 在编码器—解码器的训练中,可以采用强制教学。

参考文献

[1] Cho, K., Van Merriënboer, B., Gulcehre, C., Bahdanau, D., Bougares, F., Schwenk, H., & Bengio, Y. (2014). Learning phrase representations using RNN encoder-decoder for statistical machine translation. arXiv preprint arXiv:1406.1078.

[2] Sutskever, I., Vinyals, O., & Le, Q. V. (2014). Sequence to sequence learning with neural networks. In Advances in neural information processing systems (pp. 3104-3112).


注:本节与原书基本相同,原书传送门

  • 12
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是使用 TensorFlow 2 编写编码器解码器,并封装为 Seq2Seq 模型的示例代码: ```python import tensorflow as tf from tensorflow.keras.layers import Input, LSTM, Dense from tensorflow.keras.models import Model class Seq2Seq(Model): def __init__(self, num_encoder_tokens, num_decoder_tokens, latent_dim): super(Seq2Seq, self).__init__() # 定义编码器 self.encoder_inputs = Input(shape=(None, num_encoder_tokens)) self.encoder_lstm = LSTM(latent_dim, return_state=True) _, self.encoder_state_h, self.encoder_state_c = self.encoder_lstm(self.encoder_inputs) self.encoder_states = [self.encoder_state_h, self.encoder_state_c] # 定义解码器 self.decoder_inputs = Input(shape=(None, num_decoder_tokens)) self.decoder_lstm = LSTM(latent_dim, return_sequences=True, return_state=True) self.decoder_outputs, _, _ = self.decoder_lstm(self.decoder_inputs, initial_state=self.encoder_states) self.decoder_dense = Dense(num_decoder_tokens, activation='softmax') self.decoder_outputs = self.decoder_dense(self.decoder_outputs) # 定义模型 self.model = Model([self.encoder_inputs, self.decoder_inputs], self.decoder_outputs) def call(self, inputs): encoder_inputs, decoder_inputs = inputs # 编码器 _, state_h, state_c = self.encoder_lstm(encoder_inputs) encoder_states = [state_h, state_c] # 解码器 decoder_outputs, _, _ = self.decoder_lstm(decoder_inputs, initial_state=encoder_states) decoder_outputs = self.decoder_dense(decoder_outputs) return decoder_outputs ``` 在这个示例代码中,我们定义了一个 `Seq2Seq` 类,它继承自 `Model` 类,并在 `__init__` 方法中定义了编码器解码器的结构。在 `call` 方法中,我们重新定义了编码器解码器,以便在推理时使用。 注意,我们使用了 `Model` 类来定义模型,而不是 `Sequential` 类。这是因为 Seq2Seq 模型是一个具有多个输入和输出的模型,无法使用 `Sequential` 类来定义。 接下来,你可以使用以下代码来创建一个 Seq2Seq 模型: ```python model = Seq2Seq(num_encoder_tokens, num_decoder_tokens, latent_dim) model.compile(optimizer='adam', loss='categorical_crossentropy') model.summary() ``` 其中,`num_encoder_tokens` 和 `num_decoder_tokens` 分别表示编码器解码器的输入序列和输出序列的词汇表大小,`latent_dim` 表示编码器解码器 LSTM 层的隐藏状态维度。你需要根据实际情况进行调整。 最后,你可以使用 `fit` 方法来训练模型: ```python model.fit([encoder_input_data, decoder_input_data], decoder_target_data, batch_size=batch_size, epochs=epochs, validation_split=0.2) ``` 其中,`encoder_input_data`、`decoder_input_data` 和 `decoder_target_data` 分别表示编码器的输入序列、解码器的输入序列和解码器的目标序列,`batch_size` 和 `epochs` 分别表示训练时的批次大小和迭代次数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值