Seq2Seq模型(Sequence to Sequence)

引言

Seq2Seq模型可以被认为是一种Encoder-Decoder模型的变体,其特别适用于处理序列到序列的任务,编码器将输入序列映射为一个固定长度的向量表示,解码器则使用这个向量表示来生成输出序列。它已被广泛应用于机器翻译、对话系统、语音识别等自然语言处理任务。Seq2Seq模型的结果框架如图1所示。

图片[1]-Seq2Seq模型(Sequence to Sequence)-点头深度学习网站
图1 Seq2Seq模型架构

以机器翻译任务为例来讲解图1的Seq2Seq结构。假设现在的模型输入是文本序列“hello world”,想得到输出结果为“你好,世界”。

Seq2Seq编码器

在Seq2Seq模型中,编码器负责将输入序列映射到一个特征向量 c,如图2所示。

图片[2]-Seq2Seq模型(Sequence to Sequence)-点头深度学习网站
Seq2Seq 编码器

希望通过训练可以让该向量提取到输入信息的语义特征,将来送入解码器中作为解码器的一部分输入信息。编码器通常采用循环神经网络或卷积神经网络来处理输入序列。

以RNN为例,编码器由多个时间步组成,每个时间步接受输入序列中的一个元素,并生成一个隐藏状态。这个隐藏状态通过一个非线性函数,例如tanh或ReLU激活函数,被映射到一个连续的向量表示。这个过程可以表示为:

$$
h_t=f_{\text {enc }}\left(x_t, h_{t-1}\right)
$$

其中, \(x_t\) 是输入序列的第 \(t\) 个元素, \(h_t\) 是编码器在时间步 \(t\) 的隐藏状态, \(h_{t-1}\) 是前一个时间步的隐藏状态, \(f_{\text {enc }}\) 是一个非线性函数, 它将输入序列的元素和前一个隐藏状态作为输入,并返回当前隐藏状态。

在许多Seq2Seq模型中,最后一个时间步的隐藏状态被视为输入序列的向量表示,即:\(z=h_T\)。

其中,T 是输入序列的长度。这个向量将用作解码器的部分输入信息。除了基本的RNN编码器,还有许多其它类型的编码器,例如双向RNN编码器,它可以同时处理正向和反向序列,以捕捉输入序列中的双向信息。此外,还有一些基于卷积神经网络的编码器和Transformer编码器。这些编码器在不同的任务和数据集上具有不同的优点和局限性。简单来说,编码器只是一种概念,具体使用哪种算法没有规定,只要能对输入数据进行特征提取即可,可以根据当前输入信息的特点来选择适合的算法作为编码器。

Seq2Seq解码器

在Seq2Seq模型中,解码器负责生成输出序列,它通常也采用循环神经网络或其它变体来处理输出序列。与编码器类似,解码器也由多个时间步组成。在每个时间步,解码器使用前一个时间步的输出元素和当前时间步的隐藏状态来生成当前时间步的输出元素。这个过程可以表示为:

$$
y_t=f_{d e c}\left(y_{t-1}, s_t\right)
$$

其中, \(y_t\) 是输出序列的第 \(t\) 个元素, \(s_t\) 是解码器在时间步 \(t\) 的隐藏状态, \(y_{t-1}\) 是前一个时间步的输出元素, \(f_{d e c}\) 是一个非线性函数, 它将前一个输出元素和当前隐藏状态作为输入, 并返回当前输出元素。如图 3 所示。

图片[3]-Seq2Seq模型(Sequence to Sequence)-点头深度学习网站
图3 Seq2Seq解码器

在解码器的初始时刻, 通常会将编码器输出的特征向量 \(c\) 和起始向量 \(<b o s>\) 拼接到一起,作为初始时刻的隐藏状态 \(s_0\), 并计算得到当前时刻的输出信息 \(y_1\) 。在下一时刻 \(t_1\), 会将编码器输出的特征向量 \(\boldsymbol{c}\) 和 \(y_1\) 拼接成一个向量作为当前时刻 \(t_1\) 的输入, 并计算得到当前时刻的输出 \(y_2\) 以此类推下去, 直到模型预测出结束向量 \(<{eos}>\) 时停止。不难发现, 解码器的输入信息不仅包含了当前要翻译的单词,还包含了当前这句话的上下文语义信息;解码器会结合这两部分信息预测当前的输出,这就是解码器翻译文本为什么有效。

Seq2Seq的attention机制

在Seq2Seq模型中,Attention机制通常是在解码器端使用的。具体来说,解码器在生成每个输出时,会根据当前的解码状态和编码器中每个输入的状态计算一个注意力权重,用于表示当前输出与输入序列中每个位置的相关程度,如图4所示。

图片[1]-Seq2Seq模型(Sequence to Sequence)-点头深度学习网站
图4 Seq2Seq模型

图 4 表示的是一个常规的 Seq2Seq 模型, 只是将编码器的两个中间时刻输出的隐藏状态 \(\left(O_1, O_2\right)\) 也画在图中。考虑这样一个问题: 将编码器最后时刻输出的隐藏状态, 即特征向量 \(c\) 作为上下文语义信息送入解码器, 并在解码器翻译每一个单词时都用这同一个特征向量 \(c\) 作为输入, 这种方式真的是最佳方案吗?


(1) 首先考虑将特征向量 \(c\) 作为上下文语义信息是否合适?

由于 \(\mathrm{RNN}\) 的特性, 特征向量 \(\boldsymbol{c}\) 实际上可以包含之前时刻隐藏状态 \(O_1, O_2\) 的信息。但这是因为此序列较短。如果是一个很长的序列的话, 例如 \(O_1, O_2, O_3, \cdots, O_n\), 此时, 特征向量 \(c\) 并不能很好的保存之前所有中间时刻隐藏状态的信息。有一种解决方案是将之前所有输出的隐藏状态加起来作为特征向量 \(c\), 而不是简单的只用最后一个时刻的隐藏状态。即 \(c=O_1+O_2+O_3+\cdots+O_n\) 。


(2) 其次, 考虑在解码器翻译每一个单词时都用这同一个特征向量 \(\boldsymbol{c}\) 作为输入是否合适?

语言这种数据其实很复杂,既存在局部相关性,即跟某个单词关系最大的单词肯定是它附近的单词;又存在远距离依赖,即当前单词也可能与较远处的单词相关。不管是哪种情况,都说明句子中,单词之间是按照不同重要程度进行相互依赖的。例如将图5-10的例句稍微变长一些,将“hello,world is beautiful” 翻译成“你好,世界是美好的”。在翻译“美丽”时,它可以与“beautiful”本身90%相关,跟“is“这个词1%相关,跟“world”这个词7%相关等等,即翻译“美丽”时,抓取原文的语言信息可能是“0.9×beautiful + 0.01×is + 0.07×world +…”。而在翻译“是”这个单词时,抓取原文的语言信息是不同的,可能是“0.001×beautiful + 0.99×is + 0.005×world +…”。

所以, 在解码器翻译每个翻译每一个单词时都用这同一个特征向量 \(\boldsymbol{c}\) 作为上下文信息送进输入中是不合适的。如图 5-10 所示,在翻译得到 \(y_1\) 时,特征向量 \(\boldsymbol{c}\) 可能等于 \(0.82 \times O_1+0.18 \times O_2\), 在翻译得到 \(y_2\) 时, 特征向量 \(\boldsymbol{c}\) 可能等于 \(0.07 \times O_1+0.93 \times O_2\) 。推广下去, 在翻译得到 \(y_n\) 时, 特征向量 \(\boldsymbol{c}\) 可能等于 \(a_1 \times O_1+a_2 \times O_2+a_3 \times O_3+\cdots+a_n \times O_n\) 。其中 \(\left[a_1, a_2, a_3, \cdots, a_n\right]\) 就是要求解的注意力分数, 它应该与要翻译的输入文本的向量 \(\left[O_1, O_2, O_3, \cdots, O_n\right]\) 维度一致, 这样才能通过相乘的方法进行重要程度的重分配。

问题的关键来了, 如何对注意力分数 \(\left[a_1, a_2, a_3, \cdots, a_n\right]\) 进行建模?

假设输入序列是 \(x_1, x_2, \cdots, x_n\), 对应的编码器隐藏状态为 \(h_1, h_2, \cdots, h_n\) 。解码器的当前隐藏状态为 \(s_t\) 。首先计算解码器隐藏状态 \(s_t\) 与所有编码器隐藏状态 \(h_i\) 之间的相似性分数 \(e_{t, i}\) 。可以通过点积、加权点积或其他相似性度量来计算, 具体的 score 函数具体详见介绍 Attention-Based RNN 模型的博文:
$$
e_{t, i}= {score}\left(s_t, h_i\right)
$$

此步骤意味着解码器在翻译当前时刻的输入序列 \(x\) 时, 要用解码器上一时刻输出的隐藏状态 \(s\) 和编码器编码的所有时刻输出的隐藏状态 \(h\) 进行比较, 这些隐藏状态其实是一句话中某部分的语义信息。 \(x\) 和某些 \(h\) 越相似, 意味着这些语义信息 \(h\) 就是在翻译文本 \(x\) 时应该着重关注的地方。就像在翻译“美丽”时, 应该着重注意输入信息“beautiful”, 当然也不能忽略“world”等其它单词。接下来, 将这些分数转换为权重, 通过 softmax 函数进行归一化:
$$
\alpha_{t, i}=\frac{\exp \left(e_{t, i}\right)}{\sum_{j=1}^n \exp \left(e_{t, i}\right)}
$$

其中 \(\alpha_{t, i}\) 可以看作是解码器在时间步 \(t\) 时关注编码器隐藏状态 \(h_i\) 的权重。接着计算加权和的

上下文向量 \(c_t\), 公式如下:
$$
c_t=\sum_{i=1}^n \alpha_{t, i} h_i
$$

上下文向量 \(c_t\) 捕获了输入序列与解码器当前时间步的相关性。然后, 将上下文向量 \(c_t\) 与解码器的隐藏状态 \(s_t\) 结合起来, 以生成下一个输出单词的概率分布, 公式如下:
$$
y_t= {softmax}\left(W\left(s_t, c_t\right)+b\right)
$$

其中 W 和 b 是需要学习的权重和偏置项。通过这种方式,注意力机制帮助Seq2Seq模型关注输入序列的重要部分,从而提高了预测性能。

Seq2Seq的teacher forcing策略

Teacher Forcing用于训练阶段,主要针对Seq2Seq模型的解码器结构,神经元的输入包括了上一个神经元的输出,如果上一个神经元的输出是错误的,则下一个神经元的输出也很容易错误,导致错误会一直传递下去并累加。而Teacher Forcing可以在一定程度上缓解上面的问题。

在Teacher Forcing策略中,模型在训练期间以一定的概率使用真实的输出序列作为输入,而不是使用前一个时间步的输出元素。这个策略可以加速模型的收敛,因为它强制模型学习如何生成正确的输出序列,而不是仅仅学习如何在没有真实输出的情况下生成下一个输出元素。

具体来说, 假设有一个输入序列 \(x\) 和相应的输出序列 \(y\), 则在训练期间, 使用以下步骤:首先将输入序列 \(x\) 输入编码器, 生成一个向量表示 \(z\) 。接着将 \(z\) 作为解码器的初始隐藏状态 \(s_0\), 并使用 Teacher Forcing 策略, 将 \(y_{t-1}\) 作为解码器在时间步 \(t\) 的输入, 生成当前时间步的输出元素 \(y_t\) 。如图 5-11 所示, 解码器三个时刻 \(t_0, t_1, t_2\) 的输出结果分别是 \(y_1, y_2, y_3\) 。假设, 这训练过程中, \(t_1\) 时刻的输入应该是 “ concat \(\left(y_1, c\right)\) ” , 但是在 Teacher Forcing 策略的训练过程中, \(t_1\) 时刻的输入应该是 “concat (你好, \(c\) ) ”。这样做的好处是即使在 \(t_0\) 时刻模型预测的 \(y_1\) 不准, 也不会影响到 \(y_2\) 时刻的预测。

图片[5]-Seq2Seq模型(Sequence to Sequence)-点头深度学习网站
图5 使用
Teacher Forcing策略的Seq2Seq

在测试期间不能使用Teacher Forcing策略,因为在测试期间没有真实的输出序列来用作输入。因此,在测试期间,需要使用一些其他的策略,例如Beam Search来生成输出序列。具体来说,在生成目标序列时,模型为每个时间步生成一个词。Beam Search 的核心思想是在每个时间步保留固定数量(Beam宽度)的最优部分序列,而不是仅保留单个最优序列。通过这种方法,Beam Search能够在搜索空间中更有效地探索,并有更大的概率找到全局最优的目标序列。

Beam Search的过程如下:

(1)初始化:将开始符号(如<bos>)作为输入传递给模型,并设置Beam宽度 K。

(2)首次扩展:计算以开始符号为输入时,所有可能的下一个词的概率,并保留概率最高的 K 个词,形成 K 个部分序列。

(3)迭代扩展:对于每个部分序列,计算其下一个可能的词的概率,再将这些概率与之前的部分序列概率相乘。然后从所有可能的扩展中选择概率最高的 K 个,作为新的部分序列。重复这个过程直到达到预定的最大长度,或者所有部分序列都已经生成了结束符号(如<eos>)。

(4)结果选择:当搜索过程结束时,从 K 个部分序列中选择概率最高的一个作为最终的目标序列。

需要注意的是,Beam Search是一种启发式搜索方法,并不能保证找到全局最优解。但在实际应用中,相较于贪婪搜索(Greedy Search)或穷举搜索,Beam Search 能够在较短的时间内得到更好的结果。同时,Beam宽度 K 的选择会影响搜索过程的效率和结果质量, K 值越大,搜索空间越大,结果质量可能越好,但计算成本也越高。

总之,Beam Search是一种常用的解码策略,它可以在Seq2Seq模型中用于生成输出序列。与Teacher Forcing策略不同,Beam Search用于测试阶段,以便在没有真实输出序列作为输入的情况下生成输出序列。

最后,在回归Teacher Forcing策略的讨论中,使用Seq2Seq模型的Teacher Forcing策略可以加速模型的训练,但也存在一些缺点:

(1)不稳定性:由于训练和测试的输入方式不同,模型可能在测试期间生成不稳定的输出序列。在训练期间,模型使用真实的输出序列作为输入,而在测试期间,模型必须使用前一个时间步的输出作为输入。这种差异可能会导致模型在测试期间表现不佳。

(2)训练偏差:由于Teacher Forcing策略只使用真实输出序列作为输入,它可能导致模型在训练期间出现偏差。在测试期间,模型必须学习如何在没有真实输出序列作为输入的情况下生成下一个输出元素。如果模型在训练期间没有充分地学习这种情况,那么它可能无法正确地生成输出序列。

(3)计算成本:在训练期间,使用Teacher Forcing策略可能会增加计算成本,因为每个时间步都需要计算真实输出序列的损失。在训练期间,可以通过使用最小批量训练来减少计算成本,但在测试期间不能使用这种策略。

(4)限制序列长度:使用Teacher Forcing策略可能会限制生成的输出序列的长度。因为在训练期间,必须使用真实的输出序列作为输入,这意味着只能生成与训练数据中输出序列长度相同的序列。如果需要生成更长的序列,那么必须修改模型或使用其它策略。

综上所述,虽然Teacher Forcing策略可以加速模型的训练,但也存在一些缺点。在实践中,需要根据具体的任务和数据集选择合适的解码策略,以实现更好的性能。

Seq2Seq评价指标BLEU

首先,BLEU(Bilingual Evaluation Understudy)是Seq2Seq模型的评价函数,而不是损失函数。在Seq2Seq模型中,通常使用一些常见的损失函数,例如交叉熵损失函数或均方误差损失函数,来衡量生成的输出序列与真实输出序列之间的差距。这些损失函数的目标是最小化生成序列的错误。

而BLEU是一种广泛使用的自动评价机器翻译系统性能的指标。BLEU的主要原理是通过计算机器翻译结果与人工翻译参考之间的n-gram精度(n-gram precision)来衡量翻译质量。同时,BLEU 还考虑了短句惩罚(Brevity Penalty, BP),以防止模型生成过短的翻译结果。

BLEU 的计算过程如下:

Step 1:n-gram精度

对于不同的n(如1-gram,2-gram,3-gram,4-gram等),计算机器翻译结果中 n-gram 与参考翻译中n-gram的匹配程度。具体来说,对于每个n-gram,计算其在机器翻译结果中出现的次数与参考翻译中出现的次数的最小值,然后将这些最小值相加并除以机器翻译结果中n-gram的总数。这一步骤计算出的精度表示为\(P_n\)。举例如下:

参考翻译1:The cat is on the mat.

参考翻译2:There is a cat on the mat.

机器翻译输出:The cat is on the mat.

现在,我们使用BLEU指标来计算机器翻译的质量。

首先需要确定n-gram的大小。假设选择2-gram作为n-gram的大小。然后需要计算机器翻译输出中的2-gram与参考翻译中的2-gram的重合度。

对于参考翻译1,其2-gram序列为:{The cat, cat is, is on, on the, the mat}。

对于参考翻译2,其2-gram序列为:{There is, is a, a cat, cat on, on the, the mat}。

对于机器翻译输出,其2-gram序列为:{The cat, cat is, is on, on the, the mat}。

因此,机器翻译输出中的2-gram在参考翻译1中的精确匹配率为5/5=1,参考翻译2中的精确匹配率为2/5=0.4。

需要注意的是,BLEU通常计算多个n的 n-gram 精度。为了综合这些精度,可以使用加权几何平均计算 BLEU 分数的一部分。给定权重 \(\omega_n\) (通常取对数加权, 即 \(\omega_n=1 / N\), 其中 \(N\) 为最大 n-gram 长度), 可以计算加权几何平均值 \(\prod_{n=1}^N p_n^{w_n}\) 。


Step 2:短句惩罚
为了避免过短的翻译获得高分, BLEU 引入了短句惩罚。如果机器翻译结果的长度 (c)小于参考翻译的最佳匹配长度 \((r)\), 则 \(\mathrm{BP}\) 计算如下:
$$
\mathrm{BP}= \begin{cases}1 & \text { 如果 } c>r \\ \exp \left(1-\frac{r}{c}\right) & \text { 如果 } c \leq r\end{cases}
$$

将加权几何平均值与短句惩罚相乘, 得到最终 BLEU 分数, 公式如下:
$$
\mathrm{BLEU}=\mathrm{BP} \times \prod_{n=1}^N p_n^{w_n}
$$

需要注意的是, 尽管 BLEU 是一个广泛使用的评价指标, 但它有一些局限性, 例如忽略了词序、句法和语义等因素。因此, 在实际应用中, 通常会结合其他评价指标。

Seq2Seq模型小结

Seq2Seq模型是一种广泛用于自然语言处理和其它序列生成任务的神经网络架构。Seq2Seq模型的核心思想是将一个序列(如文本)编码成一个固定大小的向量表示,然后解码成另一个序列。这种模型通常包括一个编码器和一个解码器,分别负责将输入序列编码成隐藏表示和将隐藏表示解码成输出序列。

编码器:编码器的目标是将输入序列编码为固定大小的隐藏向量表示。可能的编码器算法有以下几种:

(1)RNN(循环神经网络):如 LSTM(长短时记忆网络)或 GRU(门控循环单元),这些循环神经网络可以捕捉序列中的长距离依赖关系。

(2)CNN(卷积神经网络):使用卷积层捕获局部特征,并通过池化层降维以减少计算成本。此外,基于CNN的编码器还可以接受图片作为输入,配合基于RNN的解码器可以实现“看图说话”的应用。

(3)Transformer:通过自注意力机制和多头注意力在序列中捕捉全局依赖关系,这种架构在许多自然语言处理任务中取得了显著的性能提升。相比于基于RNN的架构,效果更好。

解码器:解码器的目标是将隐藏向量表示解码成目标序列。解码器在每个时间步生成一个输出词,然后将这个词作为下一个时间步的输入。可能的解码器算法有以下几种:

(1)RNN(循环神经网络):如 LSTM 或 GRU,可以在每个时间步接收来自编码器的隐藏表示以及前一个时间步的输出,并生成当前时间步的输出。

(2)CNN(卷积神经网络):使用卷积层捕捉输出序列中的局部特征,并通过逐步生成序列的方式进行解码。基于CNN的解码器也可以生成图片,配合基于RNN的编码器可以实现“根据文本描述,生成图片或视频”的应用。

(3)Transformer:利用自注意力机制和多头注意力生成目标序列,还可以使用编码器-解码器注意力层来关注输入序列的不同部分。

Seq2Seq模型可以应用于机器翻译、文本摘要、问答系统、聊天机器人等任务。为了生成更好的输出序列,Seq2Seq模型通常与一些搜索策略(如贪婪搜索、Beam Search)结合使用。尽管基于RNN的Seq2Seq模型取得了很多成功,但它也有一些挑战,如捕捉长距离依赖关系、处理不同长度的输入和输出序列等。为了解决这些问题,研究人员不断提出新的架构和技术,如注意力机制、Transformer等。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容