文章目录
- 1.编码器解码器架构
-
- 1.1、概念
- 1.2、代码
-
- 1.2.1、编码器(Encoder)
- 1.2.2、解码器(Decoder)
- 1.2.3.合并编码器和解码器
- 2、seq2seq模型
-
- 2.1、编码器
- 2.2、解码器
- 2.3.编码器-解码器细节
- 2.4、训练&推理
- 2.5 评价指标-BLEU
- 3.机器翻译代码
-
- 3.1.机器翻译数据集
-
- 3.1.1.下载和预处理数据集
- 3.1.2、词元化
- 3.1.3、词表
- 3.1.4.截断和填写文本序列
- 3.1.构建数据迭代器
- 3.2、seq2seq模型与训练
-
- 3.2.1、编码器
- 3.2.2、解码器
- 3.2.编码器-解码器
- 3.2.4、损失函数
- 3.2.5、训练
- 3.2.6、预测
- 3.2.7.评估预测词元
- 3.2.8、代码整合
英语输入:They” “are” “watching” “.”
法语输出:Ils” “regardent” “.”
当输入和输出是不确定的长序列时,可以使用编码器-解码器(encoder-decoder)架构或seq2seq模型。两者都由编码器和解码器两部分组成。编码器用于分析输入序列,解码器用于生成输出序列。
编码器-解码器(encoder-decoder)架构和seq2seq模型有什么联系和区别?
我的理解是,编码器-解码器更像是模型的抽象和大架构seq2seq模型是用这种架构翻译机器的模型。因为现在比较火transformer编码器-解码器架构也被使用,所以我认为编码器-解码器是抽象结构,seq2seq和transformer使用这种架构来构建具体的任务模型。图像处理CNN也可视为编码器-解码器架构。
1.编码器解码器架构
1.1、概念
在卷积神经网络中,图片首先通过卷积层,然后通过线性层,最终输出分类结果。卷积层用于特征提取,线性层用于结果预测。从另一个角度来看,特征提取可以看作是一个编码器,将原始图片编码成有利于机器学习的中间表达,解码器将中间表达转换为另一种表达。
- 编码器:将输入编程成中间表达特征。
- 解码器:将中间表示解码成输出。
RNN也可以看作是编码器-解码器结构,编码器将文本编码成向量,解码器将向量解码成我们想要的输出。
- 编码器:将文本表示为向量。
- 解码器:将向量表示为输出。
所以我可以把编码器-解码器抽象成一个框架。模型可分为两部分,一部分称为编码器,另一部分称为解码器。编码器将输入编码成中间状态(或中间语义表示)。解码器处理这些中间状态并输出。当然,解码器也可以获得输入并与中间状态一起生成输出。编码器和解码器也可以由多个编码器组成,即多层组成。编码器可以是各种网络,如线性网络,CNN,RNN,亦或是像transformer编码器那种复杂的结构;解码器与编码器也可以是一样的网络,也可以是不同的网络。
后面介绍的seq2seq也使用这种架构,seq2seq实现的功能是将一个序列转换为另一个序列。以英文翻译为例,seq2seq编码器是处理输入的英语句子,处理输入的英语句子 X X X通过非线性转换将输入句转换为中间语义表示C: C = f ( x 1 , x 2 , ? ? , x m ) C=f(x_1,x_2,\cdots ,x_m) C=f(x1,x2,⋯,xm) 而对于解码器是根据中间语义表示C和之前已经生成的历史信息 y 1 , y 2 , y 3 , ⋯ , y i − 1 y_1,y_2,y_3,\cdots ,y_{i-1} y1,y2,y3,⋯,yi−1来生成i时刻要生成的法语单词 y i y_i yi: y i = g ( C , y 1 , y 2 , y 3 , ⋯ , y i − 1 ) y_i = g(C,y_1,y_2,y_3,\cdots ,y_{i-1}) yi=g(C,y1,y2,y3,⋯,yi−1) 每个 y i y_i yi依次生成,就将英文句子 X X X翻译成了法语句子 Y Y Y
编码器-解码器模型应用十分的广泛,其应用场景也很多,比如对于机器翻译来说, < X , Y > <X,Y> <X,Y>就是对应的不同语言的句子;对于文本摘要来说, X X X为一篇文章, Y Y Y就是对应的摘要;对于问答系统来说, X X X为问题, Y Y Y是给出的回答。
1.2、代码
因为编码器-解码器是一种抽象的结构,因此它没有具体的实现,只有使用这种结构用于具体的业务中时才有具体实现,因此下面看看编码器-解码器的相应的抽象接口。
1.2.1、编码器(Encoder)
下面是编码器的接口实现,使用指定长度可变的序列作为编码器的输入,然后得到中间语义编码。
from torch import nn
#编码器-解码器的基本编码器接口
class Encoder(nn.Module):
def __init__(self):
super(Encoder, self).__init__()
def forward(self,X,*args):
#子类需要实现,不然报错
raise NotImplementedError
1.2.2、解码器(Decoder)
在解码器结构中,新增一个init_state
函数,用于将编码器的输出(enc_output)转换解码器输入的状态。解码器中有时可能需要额外的输入。解码器在每个时间步都会将输入(前一个时间步生成的词元)和编码后的中间状态解码成当前时间步的输出词元。
#编码器-解码器的基本解码器接口
class Decoder(nn.Module):
def __init__(self,**kwargs):
super(Decoder, self).__init__()
def init_state(self,enc_outputs,*args):
raise NotImplementedError
def forward(self,X,state):
raise NotImplementedError
1.2.3、合并编码器和解码器
编码器和解码器中包含了一个编码器和一个解码器。在前向传播过程中,编码器的输出用于生成中间编码状态,这个状态又被解码器作为其输入的一部分。
#编码器-解码器架构的基类
class EncoderDecoder(nn.Module):
def __init__(self,encoder,decoder,**kwargs):
super(EncoderDecoder, self).__init__()
self.encoder = encoder
self.decoder = decoder
def forward(self,enc_X,dec_X,*args):
enc_outputs = self.encoder(enc_X,*args)
dec_state = self.decoder.init_state(enc_outputs,*args)
return self.decoder(dec_X,dec_state)
2、seq2seq模型
按照我的理解,seq2seq模型就是编码器-解码器抽象架构的一种具体实现,它主要用来做机器翻译的任务(也可以用于其他任务),包含了一个编码器和一个解码器。上一节对其进行了一个简单的描述,下面详细介绍seq2seq模型,并使用它实现一个机器翻译的任务。
下图为使用seq2seq模型将英文翻译成法语句子的过程图。在训练过程中,为每个句子后附加上特殊符号 < e o s > ( e n d o f s e q u e n c e ) <eos>(end of sequence) <eos>(endofsequence)表示序列的终止,在解码器的最初时间步用一个表示序列开始的特殊符号 < b o s > ( b e g i n i n g o f s e q u e n c e ) <bos>(begining of sequence) <bos>(beginingofsequence)。
-
编码器使用RNN处理输入信息,把最终时间步的隐藏状态作为输入信息的表征或编码信息。编码器由于能够看到句子的全部,因此编码器可以是双向的。
-
解码器也同样是一个RNN,它在各个时间步中使用输入句子的编码信息(编码器的输出)和解码器上个时间步输出合并作为输入,用来预测当前时间步的输出。由于解码器不能看到句子的全部,因此它不能是双向的。
2.1、编码器
编码器的作用是将长度可变的输入序列转换成一个定长的中间语义表示 c c c,可以用循环神经网络设计编码器。
考虑由一个序列组成的样本(批量大小为1)。假设输入序列是 x 1 , ⋯ , x T x_1,\cdots ,x_T x1,⋯,xT,其中 x t x_t xt是输入文本序列中的第 t t t个词元。在时间步 t t t,循环神经网络将词元 x t x_t xt的输入特征向量 x t x_t xt和上一时间步的隐状态 h t − 1 h_{t-1} ht−1,计算当前时间步的隐状态 h t h_t ht。使用函数 f f f表示循环神经网络的循环层所做的变换: h t = f ( x t , h t − 1 ) h_t=f(x_t,h_{t-1}) ht=f(xt,ht−1) 而编码器所生成的中间语义表示 c c c为最后一个时间步 T T T的隐藏状态,,即: c = h T c=h_T c=hT
2.2、解码器
对于来自训练数据集的输出序列 y 1 , y 2 , ⋯ , y T y_1,y_2,\cdots ,y_T y1,y2,⋯,yT,对于每个时间步 t t t,解码器的输出 y t y_t yt的概率取决于先前的输出子序列 y 1 , y 2 , ⋯ , y t − 1 y_1,y_2,\cdots ,y_{t-1} y1,y2,⋯,yt−1和中间语义表示 c c c,即: P ( y t ∣ y 1 , ⋯ , y t − 1 , c ) P(y_t|y_1,\cdots ,y_{t-1},c) P(yt∣y1,⋯,yt−1,c) 为此,可以使用另一个循环神经网络作为解码器。在输出序列的时间步 t t t,解码器将上一时间步的输出 y t − 1 y_{t-1} yt−1以及中间语义表示 c c c作为输入,并将它们与上一时间步的隐藏状态 s t − 1 s_{t-1} st−1变换成当前时间步的隐藏状态 s t s_t st。用函数g表示解码器隐藏层的变换: s t = g ( y t − 1 , c , s t − 1 ) s_t = g(y_{t-1},c,s_{t-1}) st=g(yt−1,c,st−1) 在获得解码器的隐状态之后,就可以使用输出层和softmax操作来计算在时间步 t t t的输出 y t y_t yt和 y t y_t yt的条件概率分布 P ( y t ∣ y 1 , ⋯ , y t − 1 , c ) P(y_t|y_1,\cdots ,y_{t-1},c) P(yt∣y 标签: y2p连接器