赞
踩
Transformer是一种仅使用attention机制、encoder-decoder架构的神经网络,最初应用于NLP领域的机器翻译,后逐渐在语音、CV、时间序列分析等多个领域成为主流深度模型。
Attention机制不像CNN/RNN一样对输入顺序敏感,Attention是顺序不敏感的。为了使Attention能够感受到顺序的变化,Transformer引入了Position Encoding。
思考两个问题
Position Encoding为什么是正余弦函数的形式?如何对位置进行编码?
最简单最能想到的方式:
因此位置编码需要满足以下条件:
满足以上三个条件的函数:有界周期函数,即三角函数。在不同维度使用不同周期的三角函数进行调控,周期最长为10000,可支持长序列输入,多个维度编码类似二进制编码,丰富了位置信息。
Position Encoding和Position Embedding的区别?
Transformers使用的是position encoding,而BERT使用的是position embedding,那么其本质区别是什么?
Attention机制可以描述为查表的过程,将各个时间步的输入映射为Query、Key、Value,在每个时间步计算当前时间步Query和所有时间步Key的相似度,根据相似度softmax之后的结果对所有时间步Value进行加权平均。
计算Attention方式:
Dot-product Attention,求Q和K的内积
Additive Attention:score = MLP(Q|K)
Transformer中Dot-product Attention还需乘以缩放因子1/sqrt(d_k),其目的是为了避免softmax函数落入梯度饱和区。
为什么不乘以缩放因子容易落入梯度饱和区?
Dot-product Attention公式如下,表示为不同q,k对内积的softmax归一化概率乘以对应的value,其中softmax归一化概率为注意力分数。
假设q,k分别为正态分布的随机变量,当q,k的维度增大时,其内积的方差也会增大,再经过softmax之后最大值概率趋于1,最小值概率趋于0,类似sigmoid。
代码验证:
随机生成10个维度为30的q和k(以下示例,1代表batch_size, 10代表n_steps,30代表hidden_dim),分别计算每个q,k对的注意力分数,发现在不乘以缩放因子时注意力分数非常集中,不利于梯度反向传播,而经过缩放因子后,注意力分布较为平滑,更容易获得有效的梯度。
Multi-head Attention将query、key、value映射到多个不同的子空间内,在多个子空间进行attention后,最后拼接起来。
映射参数
需要学习得到。
在 Transformer中,解码器的self-Attention和编码器中的不同,解码器采用了Masked Attention。Masked Attention保证了解码器只能注意到当前位置之前的信息,保证了自回归性。
编码器是对已知输入序列进行编码,因此没有采用masked attention,可以注意到当前位置前后各个位置的信息。
带mask的简单操作就是把qk计算的注意力分数置为负无穷,此后再进行softmax得到的注意力分数区域0,即不再关注到被mask的token。
Self-Attention输入是一个单一的嵌入序列,源序列;
Cross-Attention将两个相同维度的独立嵌入序列组合在一起,即源序列和目标序列,目标序列用作查询输入,源序列作为键和值输入。
Transformer解码器从完整的输入序列开始,但解码序列为空。cross attention将信息从输入序列传入解码器,以便它可以预测下一个输出序列标记。然后,解码器将预测值添加到输出序列中,并重复此自回归过程。
两层全连接层,独立应用在每个位置上,参数在每个位置共享。类似kernel大小为1的一维卷积。
残差块:跳跃连接,缓解梯度消失,防止网络退化
Layer Normalization:层归一化,使数据满足独立同分布,提高训练稳定性
class ScaledDotProductAttention(nn.Module):
"""Scaled dot-product attention"""
def __init__(self, temperature: float, attn_dropout: float = 0.1):
super().__init__()
self.temperature = temperature # 缩放因子
self.dropout = nn.Dropout(attn_dropout)
def forward(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
attn_mask: torch.Tensor = None,
) -> Tuple[torch.Tensor, torch.Tensor]:
attn = torch.matmul(q / self.temperature, k.transpose(2, 3)) # q:batch*n_head*T_step*F_dim k.transpose(2,3): batch*n_head*F_dim*T_step attn: batch*n_head*T_step*T_step
if attn_mask is not None:
attn = attn.masked_fill(attn_mask == 1, -1e9) # mask注意力分数填充为-∞
attn = self.dropout(F.softmax(attn, dim=-1))
output = torch.matmul(attn, v) # 注意力分数加权平均融合value
return output, attn
class MultiHeadAttention(nn.Module):
"""original Transformer multi-head attention"""
def __init__(
self,
n_head: int,
d_model: int,
d_k: int,
d_v: int,
attn_dropout: float,
):
super().__init__()
self.n_head = n_head
self.d_k = d_k
self.d_v = d_v
# 分别将q、k、v映射到不同的子空间, n_head:注意力头数,这里映射到空间大小是n_head*d_k,维度切分后即得到n_head个映射
self.w_qs = nn.Linear(d_model, n_head * d_k, bias=False)
self.w_ks = nn.Linear(d_model, n_head * d_k, bias=False)
self.w_vs = nn.Linear(d_model, n_head * d_v, bias=False)
# 上面Scaled dot-product,缩放因子为d_k**0.5
self.attention = ScaledDotProductAttention(d_k**0.5, attn_dropout)
# concat多头注意力结果并进行线性变换
self.fc = nn.Linear(n_head * d_v, d_model, bias=False)
def forward(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
attn_mask: torch.Tensor = None,
) -> Tuple[torch.Tensor, torch.Tensor]:
d_k, d_v, n_head = self.d_k, self.d_v, self.n_head
sz_b, len_q, len_k, len_v = q.size(0), q.size(1), k.size(1), v.size(1)
# Pass through the pre-attention projection: b x lq x (n*dv)
# Separate different heads: b x lq x n x dv
q = self.w_qs(q).view(sz_b, len_q, n_head, d_k)
k = self.w_ks(k).view(sz_b, len_k, n_head, d_k)
v = self.w_vs(v).view(sz_b, len_v, n_head, d_v)
# Transpose for attention dot product: b x n x lq x dv
q, k, v = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)
if attn_mask is not None:
# this mask is imputation mask, which is not generated from each batch, so needs broadcasting on batch dim
attn_mask = attn_mask.unsqueeze(0).unsqueeze(
1
) # For batch and head axis broadcasting.
v, attn_weights = self.attention(q, k, v, attn_mask)
# Transpose to move the head dimension back: b x lq x n x dv
# Combine the last two dimensions to concatenate all the heads together: b x lq x (n*dv)
v = v.transpose(1, 2).contiguous().view(sz_b, len_q, -1)
v = self.fc(v)
return v, attn_weights
Transformer编码器:任务P(X_i | X_0, X_1…X_i-1, X_i+1, X_i+2…X_T)。采用上下文还原被mask的token,适合表示学习,如BERT就采用Masked Language Model作为预训练任务之一,充分学习到词的表征。
Transformer解码器:任务:P(X_i | X_0, X_1…X_i-1),采用上文预测下文,自回归任务,适合问答、预测等任务。如GPT采用Auto Regression结构学习Language Model,在问答领域表现优秀。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。