对于Transformer的Mask机制的再思考——Encoder部分

更新时间:2023-07-30 14:28:14 阅读: 评论:0

对于Transformer的Mask机制的再思考——Encoder部分
Mask机制
虽然Mask机制在NLP领域是⼀个⼗分常见的操作,但是过去并没有仔细思考它的意义。最近参加了阿⾥天池的⼀个关于医学影像报告异常检测的数据竞赛。本质上是⼀个关于⽂本的多标签分类任务。在这个任务中,我尝试使⽤Transformer的Encoder结构作为基础来构建分类模型。为了巩固以及加深理解,没有使⽤PyTorch⾃带的Transformer模型,⽽是选择⼿动搭建。
Encoder中的Mask
在Encoder部分,涉及到的mask主要指lf-attention过程中,在计算每个token的query与key的相似度时,需要考虑⼀个重要的问题就是padding。因为我们的每条语料数据的长度⼀般是不同的,因此为了保证输⼊模型的input的size完全⼀致,我们会在末尾添加padding部分来使得每个输⼊的长度完全⼀样。但是这部分内容实际上是没有意义的。因此在attention时,注意⼒不应该放在这部分,应该将这部分mask起来。也就是说我们需要将query与padding部分对应的key的相似度度量统⼀转化为⼀个很⼩的数,⽐如1e-9或1e-10。这样经过softmax之后,这部分的权重会接近于0。那么具体该怎么做呢?额,直接看代码吧。
⾸先是Encoder部分的最底层实现,MultiHeadAttention以及之后的全连接层。
假设我们的输⼊尺⼨为[B,L], B代表batch_size, L代表q_len,也就是序列长度,那么我们再经过lf-attention之后得到的输出,也就是下⾯的scaled_attn,尺⼨为[B,H,L,L],其中H代表Head个数,具体的转化过程见代码,我就不展开了。然后就到我们需要做mask的时候了,这个时候我们会在模型中传⼊⼀个尺⼨为[B,1,1,L]的mask,其中pad_idx对应位置被设置为0,也就是需要mask的位置,其余为1。借助PyTorch的broadcasting机制,我们可以顺利地实现对scaled_attn的mask任务,然后将经过mask之后的scaled_attn(相似度度量矩阵)去与value相乘,得到每个token最终的向量表⽰。因为本⽂重点在与解释mask 机制,对于后⾯的全连接以及residual+layer_norm 的操作就不展开细说了,⼤家可以参考下⾯的代码。
import numpy as np
import torch
as nn
functional as F
比比影院
class ScaleDotProductAttention(nn.Module):
def__init__(lf,scale,atten_dropout=0.1):
super(ScaleDotProductAttention,lf).__init__()
lf.dropout=nn.Dropout(atten_dropout)
lf.scale=scale
def forward(lf,q,k,v,mask=None):#shape=[B,H,L,D]
attn=torch.matmul(anspo(-2,-1))#这⾥q:[B,H,L,D] k:[B,H,D,L]
scaled_attn=attn/lf.scale #attn 的output:[B,H,L,L]
if mask is not None:#传⼊的mask:[B,1,1,L]
scaled_attn.masked_fill(mask==0,-1e9)
scaled_attn=lf.dropout(F.softmax(scaled_attn,dim=-1))
output=torch.matmul(scaled_attn,v)
return output,scaled_attn
class MultiHeadAttention(nn.Module):
def__init__(lf,n_head,dim_model,dim_k,dim_v,dropout=0.2):
super(MultiHeadAttention,lf).__init__()
lf.dim_model=dim_model
lf.n_head=n_head
lf.dim_k=dim_k #query 和 key的维度相同所以这⾥只定义⼀个
lf.dim_q=dim_k
lf.dim_v=dim_v
lf.w_q=nn.Linear(dim_model,n_head*dim_k,bias=Fal)
lf.w_k=nn.Linear(dim_model,n_head*dim_k,bias=Fal)
lf.w_v=nn.Linear(dim_model,n_head*dim_v,bias=Fal)
lf.fc=nn.Linear(n_head*dim_v,dim_model,bias=Fal)
lf.attention=ScaleDotProductAttention(scale=dim_k**0.5)
lf.dropout=nn.Dropout(dropout)
lf.layer_norm=nn.LayerNorm(dim_model,eps=1e-6)
def forward(lf,q,k,v,mask=None):
d_k,d_v,n_head=lf.dim_k,lf.dim_v,lf.n_head
batch_size,len_q,len_k,len_v=q.size(0),q.size(1),k.size(1),v.size(1)
residual=q
q=lf.w_q(q).view(batch_size,len_q,n_head,d_k)#将head单独取出作为⼀维
k=lf.w_k(k).view(batch_size,len_k,n_head,d_k)
v=lf.w_v(v).view(batch_size,len_v,n_head,d_v)
#在attention前将len_ 与 head维度互换
q,k,anspo(1,2),k.transpo(1,2),v.transpo(1,2)#shape=[B,H,L,D]
if mask is not None:#传⼊的mask:[B,1,L]
mask = mask.unsqueeze(1)# For head axis broadcasting--->mask:[B,1,1,L]
高阁
#attention
100米短跑技巧output,attn=lf.attention(q,k,v,mask=mask)
anspo(1,2).contiguous().view(batch_size,len_q,-1)#合并heads
output=lf.dropout(lf.fc(output))
#print(output.shape,q.shape)
output+=residual #+residual
output=lf.layer_norm(output)#layer normalization
return output
class PositionwiFeedForward(nn.Module):
'''two feed forward layers'''
def__init__(lf,dim_in,dim_hid,dropout=0.2):
super(PositionwiFeedForward,lf).__init__()
lf.w1=nn.Linear(dim_in,dim_hid)
lf.w2=nn.Linear(dim_hid,dim_in)#输出维度不变
lf.layer_norm=nn.LayerNorm(dim_in,eps=1e-6)
青年女歌手lf.dropout=nn.Dropout(dropout)
def forward(lf,x):
residual=x
x=lf.w2(lf.lu(lf.w1(x))))
x+=residual
return x
在完成来上述基础组件的搭建之后,我们就可以实现单个encoder_layer以及由任意多个encoder_layer搭建的完整Encoder了,下⾯是代码,为了看起来清晰,我将encoder_layer单独写在⼀个脚本上了。
as nn
import torch
from transformer_sublayers import ScaleDotProductAttention,MultiHeadAttention, PositionwiFeedForward
class EncoderLayer(nn.Module):
def__init__(lf,dim_model,dim_hid,n_head,dim_k,dim_v,dropout=0.2):
super(EncoderLayer,lf).__init__()
lf.slf_attn=MultiHeadAttention(n_head,dim_model,dim_k,dim_v)
lf.ffn=PositionwiFeedForward(dim_model,dim_hid,dropout=dropout)
朱利叶斯欧文
def forward(lf,enc_input,slf_attn_mask=None):
attn_output=lf.slf_attn(enc_input,enc_input,enc_input,mask=slf_attn_mask)#mask:Boolean构成的[B,1,L]
output=lf.ffn(attn_output)
return output
下⾯是完整Encoder(其中包括设计mask的函数定义)
import torch
as nn
import numpy as np
积极地英文
from transformer_layers import EncoderLayer
def get_pad_mask(q,pad_idx):
return(q!=pad_idx).unsqueeze(-2)
#定义位置信息
class PositionalEncoding(nn.Module):
def__init__(lf, dim_hid, n_position=200):
super(PositionalEncoding, lf).__init__()
#缓存在内存中,常量
def_get_sinusoid_encoding_table(lf, n_position, dim_hid):
''' Sinusoid position encoding table '''
毛豆做法def get_position_angle_vec(position):
return[position / np.power(10000,2*(hid_j //2)/ dim_hid)for hid_j in range(dim_hid)]
sinusoid_table = np.array([get_position_angle_vec(pos_i)for pos_i in range(n_position)])
sinusoid_table[:,0::2]= np.sin(sinusoid_table[:,0::2])# dim 2i
sinusoid_table[:,1::2]= np.cos(sinusoid_table[:,1::2])# dim 2i+1
return torch.FloatTensor(sinusoid_table).unsqueeze(0)
def forward(lf, x):
return x + lf.pos_table[:,:x.size(1)].clone().detach()
class Encoder(nn.Module):
def__init__(lf,vocab_size,dim_word_vec,n_layers,n_head,dim_k,dim_v,dim_model,dim_hid,pad_idx,dropout=0.2,n_position=200): super(Encoder,lf).__init__()
embedding_dim=dim_word_vec,
padding_idx=pad_idx)
lf.positionencode=PositionalEncoding(dim_hid=dim_word_vec,n_position=200)
lf.dropout=nn.Dropout(dropout)
免费网站流量统计lf.layer_stacks=nn.ModuleList([
EncoderLayer(dim_model=dim_model,dim_hid=dim_hid,n_head=n_head,dim_k=dim_k,dim_v=dim_v) for _ in range(n_layers)])
lf.layer_norm=nn.LayerNorm(dim_model,eps=1e-6)
lf.dim_model=dim_model
lf.pad_idx=pad_idx
def forward(lf,x):
token_bedding_layer(x)
token_position_embedd=lf.dropout(lf.positionencode(token_embedd))
encode_output=lf.layer_norm(token_position_embedd)#shape=[B,L,E]--->(batch_size,q_len,embed_dim)
mask=get_pad_mask(x,lf.pad_idx)#shape=[B,1,L]
for encode_layer in lf.layer_stacks:
encode_output=encode_layer(encode_output,slf_attn_mask=mask)
return encode_output

本文发布于:2023-07-30 14:28:14,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1102059.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:部分   基础   数据   模型   机制
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图