seq2seq到加上attention机制,再整合成transformer

Samira ·
更新时间:2024-11-11
· 604 次阅读

时间问题,,开个好头。

1.机器翻译有一个大问题,就是输入输出的序列长度不一定相等。于是设计出Encoder-Decoder模型

*

在这里插入图片描述
于是就有了Sequence to Sequenceseq模型
在这里插入图片描述
简答来说就是在输出的时候:先输入bos,然后以eos为结束标记。
总结:

Sequence to Sequence encoder、decoder的网络可以是任意RNN网络:LSTM,双向RNN等; 这里Encoder不需要用到每一个单元的output,只需把H传到Decoder作为初始输入; 注意embedding X的shape(batch_size, seq_len, embed_size),但是RNN的第一个输入维度要是时间步数,所以要转换一下 X = X.transpose(0, 1) # (seq_len, batch_size, embed_size)

补充:
数据集预处理的时候除了要padding每一个sample,还要记录每一个sample的vail_len,因为参数更新的时候无需更新padding的单元,记录了就直接在padding的位置值-无穷;那么exp(tokens)就为0,不影响softmax

array = torch.tensor([pad(line, max_len, vocab.pad) for line in lines]) valid_len = (array != vocab.pad).sum(1) # 取出vail_len的单元 maxlen = X.size(1) # 每一行的最大 就是这一行有多少列 mask = torch.arange(maxlen)[None, :].to(X_len.device) < X_len[:, None] X[~mask]=value(value为0或-1)

还有就是数据预处理其实也不好做,之前上课做的很多情况,特别是处理中文文本,要考虑超级无敌多情况,要学会熟练使用re正则表达式。 还有一些符号,比如空格 的编码字符不能被计算机识别,要转换成可以识别的状况,这个是真的大坑啊!

t.replace('\u202f', ' ').replace('\xa0', ' ') 1.2.Beam Search*

在这里插入图片描述
decoder的时候,经过dense选取得分最高的一个是贪婪搜索,将当前每一个最高分的token输出,这样可能会错过最优解;
但是全局搜索(维特比算法,经典算法)的话计算量又很大,不符合现实;
于是取中,选取每一次得分最高的N个进行搜索解。
比如最高的两个:
在这里插入图片描述
注意 :集束算法也不一定是最优解,也属于贪心算法。

2.Attention机制 2.1 在机器翻译中,较长的句子很难寄希望encoder保留有效信息,与此同时,解码的目标词语可能只与原输入的部分词语有关,而并不是与所有的输入有关

在seq2seq模型中,解码器只能隐式地从编码器的最终状态中选择相应的信息。然而,注意力机制可以将这种选择过程显式地建模。记起老师说好像也可以作用在cnn上,不一定只应用于机器翻译。
Attention三大部分:query values keys

总结:

增加单词组合之间的联系,这里直接关心他是怎么实现的。 简单的线性计算就能产生很好的效果,真的是太厉害了。

在这里插入图片描述

2.2 引入到seq2seq模型

在这里插入图片描述

Encoder的每一个单元的outputs作为key-value (这里就运用到output了)
Encoder最后的H作为Query
attention的输出就当成 content vector. 然后和输入Dt cat一起

在代码中加入:

# select hidden state of the last rnn layer as query query = hidden_state[0][-1].unsqueeze(1) # np.expand_dims(hidden_state[0][-1], axis=1) # context has same shape as query # print("query enc_outputs, enc_outputs:\n",query.size(), enc_outputs.size(), enc_outputs.size()) context = self.attention_cell(query, enc_outputs, enc_outputs, enc_valid_len) # Concatenate on the feature dimension #print("context.size:",context.size()) x = torch.cat((context, x.unsqueeze(1)), dim=-1) # Reshape x to (1, batch_size, embed_size+hidden_size) out, hidden_state = self.rnn(x.transpose(0,1), hidden_state) outputs.append(out) outputs = self.dense(torch.cat(outputs, dim=0)) 3.Transformer

实现两大难点:处理长序列信息;实现并行化(q,k,v维度一致)

3.1多头注意力层 multi_head attention(多头并行一起计算)

注意:Decoder 部分的第二个注意力层不是自注意力,key-value来自编码器而query来自解码器
在这里插入图片描述
多头注意力层:简单理解就是每一个输入都是Q,K,V(shape一样) [补充:attention 本质就是 q,k,v的简单线性输出 w*x,好像没有b] 这里每一个计算出来就concat起来到dense
在这里插入图片描述

3.2 基于位置的前馈网络(PositionWiseFFN)

他等效于一个1*1的卷积,因为他接受的输入(batch_size,seq_length, feature_size)的三维张量

class PositionWiseFFN(nn.Module): def __init__(self, input_size, ffn_hidden_size, hidden_size_out, **kwargs): super(PositionWiseFFN, self).__init__(**kwargs) self.ffn_1 = nn.Linear(input_size, ffn_hidden_size) self.ffn_2 = nn.Linear(ffn_hidden_size, hidden_size_out) def forward(self, X): return self.ffn_2(F.relu(self.ffn_1(X)))

与多头注意力层相似,FFN层同样只会对最后一维的大小进行改变;除此之外,对于两个完全相同的输入,FFN层的输出也将相等。这个很重要,后面计算一定要注意维度!!

3.3 Add and Norm

这里涉及到 layer_norm和batch_norm
参考:https://zhuanlan.zhihu.com/p/54530247
Transformer还有一个重要的相加归一化层,它可以平滑地整合输入和其他层的输出,因此我们在每个多头注意力层和FFN层后面都添加一个含残差连接的Layer Norm层。

(这里 Layer Norm 与7.5小节的Batch Norm很相似,唯一的区别在于Batch Norm是对于batch size这个维度进行计算均值和方差的,而Layer Norm则是对最后一维进行计算。层归一化可以防止层内的数值变化过大,从而有利于加快训练速度并且提高泛化性能。)

BN:对同一个batch里的样本(不同样本)的同一个channel进行归一化;
LN:对同一个样本的不同channel进行归一化 ;就是一个seq_len的所有时间步进行归一化。
补充:还有其他三种归一化。。遇见的时候再补充

exmaples: layernorm = nn.LayerNorm(normalized_shape=2, elementwise_affine=True) batchnorm = nn.BatchNorm1d(num_features=2, affine=True) X = torch.FloatTensor([[1,2], [3,4]]) print('layer norm:', layernorm(X)) # 对行 print('batch norm:', batchnorm(X)) # 对列 3.4 位置编码

与循环神经网络不同,无论是多头注意力网络还是前馈神经网络都是独立地对每个位置的元素进行更新,这种特性帮助我们实现了高效的并行,却丢失了重要的序列顺序的信息。为了更好的捕捉序列信息,Transformer模型引入了位置编码去保持输入序列元素的位置。

涉及到(seq_len, embedding_size) 更新的时候并行更新,会丢失序列信息
i:序列中的顺序(就是i在seq_len中第几个), j:embedding vector内部的索引(第几个embeding vector)

我的理解是:并行运算 原来的seq embedding vector计算的时候不是一个个vector进行计算的,或者说可其他vector组合了(multi_head attention),原来的序列就找不到了。
在这里插入图片描述

3.5 Decoder层

也是多了add_norm层等等

唯一注意就是:由于t位置的输入可以观测到所有序列,这样不能进行预测,所以要通过将第t个时间步所对应的可观测长度设置为t,以消除不需要看到的未来的信息。 通过valid_length进行处理
valid_length = torch.FloatTensor(np.tile(np.arange(1, seq_len+1), (batch_size, 1)))

Sequence Mask只在Decoder端进行,目的是为了使得decoder不能看见未来的信息.也就是对于一个序列中的第i个token,解码的时候只能够依靠i时刻之前(包括i)的的输出,而不能依赖于i时刻之后的输出.

关于mask,还有:

对key中padding进行的mask操作 对query中的padding进行的mask操作

目前没理解错的话,这两种都是为了对padding的内容进行运算后置0的操作,不对他进行attention(更新)。
在这里插入图片描述

内容太多了!!参考资料太少,我还要好多理解还没写上去,时间问题!!好多坑啊,好多不懂的 懂得总结还没写上去。。以后要补充!!!


作者:陈浩天就是我



attention

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章
Frieda 2021-05-03
910