Task02 学习Attention和Transformer

1.1 seq2seq模型

定义:

一个序列到一个序列(seq2seq)模型,接收的输入是一个(单词、字母、图像特征)序列,输出的是另一个序列;在神经机器翻译中,一个序列是指一连串的单词。

模型结构:由编码器和解码器组成

编码器:用于处理输入序列中的每个元素,把这些信息转换为一个上下文向量,处理完成后,输出到解码器;

解码器:用于逐项生成输出序列中的元素。

原始模型:

编码器和解码器使用循环神经网络(RNN);

上下文向量的长度基于编码器RNN的隐藏层神经元的数量;

RNN在每个时间步接受2个输入:输入序列中的一个元素、一个隐藏层状态(hidden state)。

处理过程:

编解码器处理过程:编码器和解码器在每个时间步处理输入得到输出,RNN根据当前时间步的输入和前一个时间步的隐藏层状态,更新当前时间步的隐藏层状态。

特点:与RNN最后一次输出隐藏层状态不同,编码器和解码器在每个时间步处理输入得到输出。

1.2 Attention

效果:

在机器翻译场景,产生英语翻译之前,提供从输入序列范大相关信号的能力,使得模型产生更好的结果。

Attention与seq2seq模型的区别:

Attention中,编码器把所有时间步的隐藏层状态传递给解码器;而seq2seq中,只将最后一个隐藏层传递给解码器;

Attention的解码器在产生输出序列之前,做了一些处理:

1.查看所有接收到编码器的hidden state

2.给每个hidden state一个分数

3.将每个hidden state乘以经过softmax的对应分数,高分对应的hidden state被放大,低分的则被缩小

Attention处理过程:

解码器RNN的输入:一个embedding向量、一个初始化的解码器hidden state

处理上述输入,产生一个输出和一个新的hidden state(h4向量)

使用编码器的hidden state和上述产生的hidden state(h4向量)计算当前时间步的上下文向量(C4向量)

将h4和C4进行拼接,得到一个向量

将上述向量输入到一个前馈神经网络

前馈神经网络的输出表示这个时间步输出的单词

下一个时间步重复上述步骤(从第1步到第6步)

1.3 Transformer

主要组成部分:

由编码部分和解码部分组成

编码部分由多层编码器组成(6层),解码部分由多层解码器组成(6层)

编码器由Self-Attention Layer 和 Feed Forward Neural Network(前馈神经网络,FFNN)组成

解码器由Self-Attention Layer、Encoder-Decoder Attention、FFNN组成,其中Encoder-Decoder Attention用于帮助解码器聚焦于输入句子的相关部分

1.Transformer输入

使用词嵌入算法,将每个词转换为一个词向量,向量列表的长度为训练集中的句子最大长度。

2.Encoder(编码器)

输入:一个向量列表,上一个编码器的输出

输出:同样大小的向量列表,连接到下一个编码器

3.数据流:

每个单词转换成一个向量后,输入到self-attention层

每个位置的单词得到新向量,输入到FFNN

4.Self-Attention

直观理解,Self-Attention机制使模型不仅能够关注这个位置的词,而且能够关注句子中其他位置的词,作为辅助线索,更好地编码当前位置的词。

5.残差连接:

在编解码器的每个子层,都有一个残差连接、一个层标准化(layer-normalization)

6.Decoder(解码器)

解码阶段的每一个时间步都输出一个翻译后的单词

将解码器的输入向量,加上位置编码向量,用于指示每个词的位置

Self-Attention层:只允许关注到输出序列中早于当前位置之前的单词,在Self-Attention分数经过Softmax层之前,屏蔽当前位置之后的位置

Encoder-Decoder Attention层:和多头注意力机制类似,但是使用前一层的输出构造Query矩阵,而Key矩阵和Value矩阵来自于解码器最终的输出。

7.最后的线性层和Softmax层

线性层:把解码器输出的向量,映射到一个更长的向量(logist向量),和模型的输出词汇表长度一致

Softmax层:把logist向量中的数,转换为概率,取概率最高的那个数字对应的词,就是输出的单词

1.4 Self-Attention

Self-Attention输出的计算步骤:

对输入编码器的每个词向量,通过乘以三个参数矩阵,创建3个向量(Query向量、Key向量、Value向量)

计算Attention Score分数:计算某个词的对句子中其他位置的词放置的注意力程度,通过该词对应的Query向量与每个词的Key向量点积计算得到。

将每个分数除以\sqrt{d_{key}}dkey,d_{key}dkey是Key向量的长度

这些分数经过一个Softmax,进行归一化

将归一化的分数乘以每个Value向量

对上步的值进行相加,得到该词在Self-Attention层的输出

Self-Attention的矩阵形式计算

假设所有词向量组成矩阵XX,权重矩阵W^Q,W^K,W^VWQ,WK,WV

根据第1步,可得Q = X \cdot W^Q \\ K = X \cdot W^K \\ V = X \cdot W^VQ=X⋅WQK=X⋅WKV=X⋅WV

根据第2~6步,可得Self-Attention的输出Z = \text{softmax}\left( \frac{Q \cdot K^T}{\sqrt{d_{K}}} \right) \cdot VZ=softmax(dKQ⋅KT)⋅V

1.5 多头注意力机制(multi-head attention)

作用:增强Attention层

扩展了模型关注不同位置的能力

赋予Attention层多个“子表示空间”

处理过程:

随机初始化8组W^Q,W^K,W^VWQ,WK,WV

根据矩阵计算的第1步,词向量矩阵XX和每组W^Q,W^K,W^VWQ,WK,WV相乘,得到8组Q,K,VQ,K,V矩阵

根据矩阵计算的第2步,得到8个ZZ矩阵

将8个ZZ矩阵横向拼接,并乘以另一个权重矩阵W^OWO,得到一个矩阵ZZ

1.6 使用位置编码表示序列中单词的顺序

目的:通过对每个输入的向量,添加一个遵循特定模式向量,用于确定每个单词的我i之,或者句子中不同单词之间的距离。

特定模式向量:向量的左半部分值由sine函数产生,右半部分的值由cosine函数产生,然后拼接,得到每个位置编码向量。

优点:扩展到需要翻译句子的序列长度

1.7 损失函数

目的:由于模型的参数是随机初始化的,每个词输出的概率分布与正确的输出概率分布比较,使用反向传播调整模型权重

贪婪解码:由于模型每个时间步只产生一个输出,模型是从概率分布中选择概率最大的词,并丢弃其他词

集束搜索:每个时间步保留beam_size个概率最高的输出词,然后在下一个时间步,根据第1个词计算第2个位置的词的概率分布,对于后续位置,重复上述过程。

beam_size:用于在所有时间步保留最高频率的词的个数

top_beams:用于表示最终返回翻译结果的个数

2 实战练习

词嵌入

如上图所示,Transformer图里左边的是Encoder,右边是Decoder部分。Encoder输入源语言序列,Decoder里面输入需要被翻译的语言文本(在训练时)。一个文本常有许多序列组成,常见操作为将序列进行一些预处理(如词切分等)变成列表,一个序列的列表的元素通常为词表中不可切分的最小词,整个文本就是一个大列表,元素为一个一个由序列组成的列表。如一个序列经过切分后变为["am", "##ro", "##zi", "meets", "his", "father"],接下来按照它们在词表中对应的索引进行转换,假设结果如[23, 94, 13, 41, 27, 96]。假如整个文本一共100个句子,那么就有100个列表为它的元素,因为每个序列的长度不一,需要设定最大长度,这里不妨设为128,那么将整个文本转换为数组之后,形状即为100 x 128,这就对应着batch_size和seq_length。

输入之后,紧接着进行词嵌入处理,词嵌入就是将每一个词用预先训练好的向量进行映射。

词嵌入在torch里基于torch.nn.Embedding实现,实例化时需要设置的参数为词表的大小和被映射的向量的维度比如embed = nn.Embedding(10,8)。向量的维度通俗来说就是向量里面有多少个数。注意,第一个参数是词表的大小,如果你目前最多有8个词,通常填写10(多一个位置留给unk和pad),你后面万一进入与这8个词不同的词就映射到unk上,序列padding的部分就映射到pad上。

假如我们打算映射到8维(num_features或者embed_dim),那么,整个文本的形状变为100 x 128 x 8。接下来举个小例子解释一下:假设我们词表一共有10个词(算上unk和pad),文本里有2个句子,每个句子有4个词,我们想要把每个词映射到8维的向量。于是2,4,8对应于batch_size, seq_length, embed_dim(如果batch在第一维的话)。

另外,一般深度学习任务只改变num_features,所以讲维度一般是针对最后特征所在的维度。

开始编程:

所有需要的包的导入:

import torch

import torch.nn as nn

from torch.nn.parameter import Parameter

from torch.nn.init import xavier_uniform_

from torch.nn.init import constant_

from torch.nn.init import xavier_normal_

import torch.nn.functional as F

from typing import Optional, Tuple, Any

from typing import List, Optional, Tuple

import math

import warnings

X = torch.zeros((2,4),dtype=torch.long)

embed = nn.Embedding(10,8)

print(embed(X).shape)

位置编码

词嵌入之后紧接着就是位置编码,位置编码用以区分不同词以及同词不同特征之间的关系。代码中需要注意:X_只是初始化的矩阵,并不是输入进来的;完成位置编码之后会加一个dropout。另外,位置编码是最后加上去的,因此输入输出形状不变。

Tensor = torch.Tensor

def positional_encoding(X, num_features, dropout_p=0.1, max_len=512) -> Tensor:

    r'''

        给输入加入位置编码

    参数:

        - num_features: 输入进来的维度

        - dropout_p: dropout的概率,当其为非零时执行dropout

        - max_len: 句子的最大长度,默认512


    形状:

        - 输入: [batch_size, seq_length, num_features]

        - 输出: [batch_size, seq_length, num_features]

    例子:

        >>> X = torch.randn((2,4,10))

        >>> X = positional_encoding(X, 10)

        >>> print(X.shape)

        >>> torch.Size([2, 4, 10])

    '''

    dropout = nn.Dropout(dropout_p)

    P = torch.zeros((1,max_len,num_features))

    X_ = torch.arange(max_len,dtype=torch.float32).reshape(-1,1) / torch.pow(

        10000,

        torch.arange(0,num_features,2,dtype=torch.float32) /num_features)

    P[:,:,0::2] = torch.sin(X_)

    P[:,:,1::2] = torch.cos(X_)

    X = X + P[:,:X.shape[1],:].to(X.device)

    return dropout(X)

# 位置编码例子

X = torch.randn((2,4,10))

X = positional_encoding(X, 10)

print(X.shape)



2.1 使用PyTorch的MultiheadAttention来实现Attention的计算

torch.nn.MultiheadAttention(embed_dim, num_heads, dropout=0.0, bias=True, add_bias_kv=False, add_zero_attn=False, kdim=None, vdim=None)

参数说明如下:

embed_dim:最终输出的 K、Q、V 矩阵的维度,这个维度需要和词向量的维度一样

num_heads:设置多头注意力的数量。如果设置为 1,那么只使用一组注意力。如果设置为其他数值,那么 num_heads 的值需要能够被 embed_dim 整除

dropout:这个 dropout 加在 attention score 后面

forward(query, key, value, key_padding_mask=None, need_weights=True, attn_mask=None)

参数说明如下:

query:对应于 Key 矩阵,形状是 (L,N,E) 。其中 L 是输出序列长度,N 是 batch size,E 是词向量的维度

key:对应于 Key 矩阵,形状是 (S,N,E) 。其中 S 是输入序列长度,N 是 batch size,E 是词向量的维度

value:对应于 Value 矩阵,形状是 (S,N,E) 。其中 S 是输入序列长度,N 是 batch size,E 是词向量的维度

key_padding_mask:如果提供了这个参数,那么计算 attention score 时,忽略 Key 矩阵中某些 padding 元素,不参与计算 attention。形状是 (N,S)。其中 N 是 batch size,S 是输入序列长度。

如果 key_padding_mask 是 ByteTensor,那么非 0 元素对应的位置会被忽略

如果 key_padding_mask 是 BoolTensor,那么 True 对应的位置会被忽略

attn_mask:计算输出时,忽略某些位置。形状可以是 2D (L,S),或者 3D (N∗numheads,L,S)。其中 L 是输出序列长度,S 是输入序列长度,N 是 batch size。

如果 attn_mask 是 ByteTensor,那么非 0 元素对应的位置会被忽略

如果 attn_mask 是 BoolTensor,那么 True 对应的位置会被忽略

你可能感兴趣的:(Task02 学习Attention和Transformer)