深度学习:Attention机制:自注意力机制(Self-Attention)详解_第1页
深度学习:Attention机制:自注意力机制(Self-Attention)详解_第2页
深度学习:Attention机制:自注意力机制(Self-Attention)详解_第3页
深度学习:Attention机制:自注意力机制(Self-Attention)详解_第4页
深度学习:Attention机制:自注意力机制(Self-Attention)详解_第5页
已阅读5页,还剩22页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

深度学习:Attention机制:自注意力机制(Self-Attention)详解1引言1.1注意力机制的重要性在深度学习领域,注意力机制(AttentionMechanism)的引入极大地提升了模型处理序列数据和理解复杂输入的能力。传统的序列模型,如循环神经网络(RNN)和长短期记忆网络(LSTM),在处理长序列时面临梯度消失或梯度爆炸的问题,导致模型难以学习到序列中长距离的依赖关系。注意力机制通过允许模型在处理序列时关注输入序列中的关键部分,有效地解决了这一问题,提高了模型的性能和效率。1.1.1自注意力机制的起源与应用自注意力机制(Self-Attention)最初在2017年被Vaswani等人在论文《AttentionisAllYouNeed》中提出,作为Transformer模型的核心组成部分。与传统的注意力机制不同,自注意力机制允许模型中的每个位置直接关注序列中的所有位置,而不仅仅是前一个或几个位置。这种机制不仅加速了训练过程,还增强了模型对输入序列中长距离依赖关系的捕捉能力。自注意力机制的应用广泛,包括但不限于:-自然语言处理(NLP):在机器翻译、文本分类、情感分析等任务中,自注意力机制帮助模型理解句子结构和语义。-计算机视觉(CV):在图像识别和生成任务中,自注意力机制可以捕捉图像中不同区域之间的关系。-语音识别:自注意力机制在处理语音信号时,能够识别和理解语音中的重要特征。1.2自注意力机制详解1.2.1原理自注意力机制基于查询(Query)、键(Key)和值(Value)的概念。在Transformer模型中,输入序列的每个位置都会生成对应的查询、键和值向量。查询向量用于寻找输入序列中与当前位置相关联的键向量,而键向量则用于衡量其他位置与当前位置的相关性。值向量包含了输入序列中每个位置的信息,当查询与键匹配时,模型会根据匹配程度加权组合值向量,生成注意力加权的表示。具体步骤查询、键、值的生成:对于输入序列中的每个位置,通过线性变换生成查询、键和值向量。计算注意力权重:使用查询向量和键向量计算注意力权重,通常通过点积操作实现。加权求和:根据计算出的注意力权重,对值向量进行加权求和,生成注意力加权的表示。输出:将注意力加权的表示与输入序列的原始表示结合,作为模型的输出。1.2.2代码示例以下是一个使用PyTorch实现的自注意力机制的简单示例:importtorch

importtorch.nnasnn

classSelfAttention(nn.Module):

def__init__(self,embed_size,heads):

super(SelfAttention,self).__init__()

self.embed_size=embed_size

self.heads=heads

self.head_dim=embed_size//heads

assert(self.head_dim*heads==embed_size),"Embedsizeneedstobedivisiblebyheads"

self.values=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.keys=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.queries=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.fc_out=nn.Linear(heads*self.head_dim,embed_size)

defforward(self,values,keys,query,mask):

N=query.shape[0]

value_len,key_len,query_len=values.shape[1],keys.shape[1],query.shape[1]

#Splittheembeddingintoself.headsdifferentpieces

values=values.reshape(N,value_len,self.heads,self.head_dim)

keys=keys.reshape(N,key_len,self.heads,self.head_dim)

queries=query.reshape(N,query_len,self.heads,self.head_dim)

energy=torch.einsum("nqhd,nkhd->nhqk",[queries,keys])

#queriesshape:(N,query_len,heads,heads_dim),

#keysshape:(N,key_len,heads,heads_dim)

#energy:(N,heads,query_len,key_len)

ifmaskisnotNone:

energy=energy.masked_fill(mask==0,float("-1e20"))

attention=torch.softmax(energy/(self.embed_size**(1/2)),dim=3)

out=torch.einsum("nhql,nlhd->nqhd",[attention,values]).reshape(

N,query_len,self.heads*self.head_dim

)

#valuesshape:(N,value_len,heads,heads_dim)

#outaftereinsum:(N,query_len,heads,head_dim),then

#outshape:(N,query_len,heads*head_dim)

out=self.fc_out(out)

returnout代码解释初始化:定义了查询、键、值的线性变换层,以及一个输出层用于将多头注意力的结果合并回原始的嵌入维度。前向传播:首先将输入的嵌入向量分割成多个头,每个头的维度为embed_size//heads。然后计算查询向量和键向量之间的点积,得到能量矩阵。如果存在mask,则应用mask以屏蔽不需要关注的部分。最后,使用softmax函数计算注意力权重,并对值向量进行加权求和,生成注意力加权的表示。1.2.3结论自注意力机制通过允许模型关注输入序列中的关键部分,显著提高了模型处理长序列数据的能力。在深度学习的多个领域,自注意力机制已经成为构建高效、强大模型的关键技术之一。通过上述代码示例,我们可以更深入地理解自注意力机制的工作原理,并在实际项目中应用这一机制。2自注意力机制基础2.1自注意力机制的概念自注意力机制(Self-Attention)是深度学习中的一种机制,主要用于处理序列数据,如自然语言文本。它允许模型在处理序列中的每个元素时,考虑整个序列的信息,而不仅仅是其直接的前后文。自注意力机制的核心思想是让序列中的每个位置的元素能够“关注”到序列中的其他元素,从而捕捉到更长距离的依赖关系。2.1.1例子假设我们有一个英文句子“Johnreadsabookintheclassroom”,我们想要模型理解“reads”这个动作是由“John”执行的,而不是“book”或“classroom”。在自注意力机制中,每个词都会计算与其他词之间的注意力权重,这些权重会帮助模型更好地理解词与词之间的关系。2.2自注意力与传统注意力的区别传统注意力机制通常用于序列到序列(Seq2Seq)模型中,如机器翻译。在这种机制下,解码器在生成每个输出词时,会根据当前的解码状态,从编码器的输出中选择一部分信息作为参考。然而,这种机制在处理长序列时效率较低,因为每次解码都需要遍历整个编码器的输出。自注意力机制则不同,它在处理序列中的每个元素时,都会同时考虑序列中所有其他元素的信息,而不仅仅是其直接的前后文。这种机制不仅能够捕捉到更长距离的依赖关系,而且在处理长序列时,计算效率也更高。2.2.1代码示例下面是一个使用PyTorch实现的自注意力机制的简单示例:importtorch

importtorch.nnasnn

classSelfAttention(nn.Module):

def__init__(self,embed_size,heads):

super(SelfAttention,self).__init__()

self.embed_size=embed_size

self.heads=heads

self.head_dim=embed_size//heads

assert(self.head_dim*heads==embed_size),"Embedsizeneedstobedivisiblebyheads"

self.values=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.keys=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.queries=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.fc_out=nn.Linear(heads*self.head_dim,embed_size)

defforward(self,values,keys,query,mask):

N=query.shape[0]

value_len,key_len,query_len=values.shape[1],keys.shape[1],query.shape[1]

#Splittheembeddingintoself.headsdifferentpieces

values=values.reshape(N,value_len,self.heads,self.head_dim)

keys=keys.reshape(N,key_len,self.heads,self.head_dim)

queries=query.reshape(N,query_len,self.heads,self.head_dim)

values=self.values(values)

keys=self.keys(keys)

queries=self.queries(queries)

energy=torch.einsum("nqhd,nkhd->nhqk",[queries,keys])

#queriesshape:(N,query_len,heads,heads_dim),

#keysshape:(N,key_len,heads,heads_dim)

#energy:(N,heads,query_len,key_len)

ifmaskisnotNone:

energy=energy.masked_fill(mask==0,float("-1e20"))

attention=torch.softmax(energy/(self.embed_size**(1/2)),dim=3)

out=torch.einsum("nhql,nlhd->nqhd",[attention,values]).reshape(

N,query_len,self.heads*self.head_dim

)

out=self.fc_out(out)

returnout在这个例子中,我们定义了一个SelfAttention类,它包含了一个前向传播函数forward。这个函数接收values、keys、query和mask作为输入,然后计算自注意力权重,并使用这些权重对values进行加权求和,得到输出。2.3自注意力机制的关键组件自注意力机制的关键组件包括:查询(Query):用于寻找信息的向量。键(Key):用于与查询进行匹配,确定注意力权重的向量。值(Value):根据注意力权重进行加权求和的向量。注意力权重(AttentionWeights):查询与键之间的匹配程度,通常通过点积或相似度计算得到。多头注意力(Multi-HeadAttention):将自注意力机制分成多个头,每个头独立计算注意力,然后将结果拼接起来,通过一个全连接层输出。这样可以捕捉到不同类型的依赖关系。2.3.1例子在自注意力机制中,查询、键和值通常是从输入序列的嵌入向量中线性变换得到的。例如,对于一个词嵌入向量x,我们可以使用三个不同的线性变换W^Q、W^K和W^V来得到查询Q、键K和值V:Q=x@W^Q

K=x@W^K

V=x@W^V然后,我们可以通过点积计算查询与键之间的注意力权重:attention_weights=softmax(Q@K.T/sqrt(d_k))其中d_k是键向量的维度。最后,我们使用注意力权重对值向量进行加权求和,得到输出:output=attention_weights@V2.3.2多头注意力示例多头注意力机制可以进一步增强自注意力机制的能力,通过并行计算多个注意力头,然后将结果拼接起来。下面是一个使用PyTorch实现的多头注意力机制的示例:classMultiHeadAttention(nn.Module):

def__init__(self,embed_size,heads):

super(MultiHeadAttention,self).__init__()

self.heads=heads

self.head_dim=embed_size//heads

assert(self.head_dim*heads==embed_size),"Embedsizeneedstobedivisiblebyheads"

self.values=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.keys=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.queries=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.fc_out=nn.Linear(heads*self.head_dim,embed_size)

defforward(self,values,keys,query,mask):

N=query.shape[0]

value_len,key_len,query_len=values.shape[1],keys.shape[1],query.shape[1]

#Splittheembeddingintoself.headsdifferentpieces

values=values.reshape(N,value_len,self.heads,self.head_dim)

keys=keys.reshape(N,key_len,self.heads,self.head_dim)

queries=query.reshape(N,query_len,self.heads,self.head_dim)

values=self.values(values)

keys=self.keys(keys)

queries=self.queries(queries)

energy=torch.einsum("nqhd,nkhd->nhqk",[queries,keys])

#queriesshape:(N,query_len,heads,heads_dim),

#keysshape:(N,key_len,heads,heads_dim)

#energy:(N,heads,query_len,key_len)

ifmaskisnotNone:

energy=energy.masked_fill(mask==0,float("-1e20"))

attention=torch.softmax(energy/(self.head_dim**(1/2)),dim=3)

out=torch.einsum("nhql,nlhd->nqhd",[attention,values]).reshape(

N,query_len,self.heads*self.head_dim

)

out=self.fc_out(out)

returnout在这个例子中,我们定义了一个MultiHeadAttention类,它包含了一个前向传播函数forward。这个函数接收values、keys、query和mask作为输入,然后计算多头自注意力权重,并使用这些权重对values进行加权求和,得到输出。每个头独立计算注意力,然后将结果拼接起来,通过一个全连接层输出。通过上述示例和解释,我们可以看到自注意力机制如何在深度学习模型中工作,以及它如何通过多头注意力机制增强模型捕捉不同类型依赖关系的能力。3自注意力机制的数学基础3.1向量和矩阵的复习在深度学习中,向量和矩阵是构建自注意力机制的基础。向量可以视为一个一维数组,而矩阵则是一个二维数组。在自注意力机制中,我们经常需要对向量和矩阵进行操作,如点积、矩阵乘法和转置等。3.1.1点积点积是两个向量的线性组合,其结果是一个标量。如果两个向量分别为a=a1,a3.1.2矩阵乘法矩阵乘法是两个矩阵相乘的操作,其结果是一个新的矩阵。如果矩阵A的尺寸为m×n,矩阵B的尺寸为n×p,那么它们的乘积C=AB的尺寸为mc3.1.3转置矩阵的转置是将矩阵的行和列互换的操作。如果矩阵A的尺寸为m×n,那么它的转置AT的尺寸为3.2自注意力的计算过程自注意力机制允许模型在处理序列数据时,关注序列中的不同部分。这一机制通过计算查询(Query)、键(Key)和值(Value)之间的注意力权重来实现。3.2.1计算注意力权重假设我们有查询向量q,键向量k,值向量v,其中k和v是序列中每个元素的表示。自注意力机制首先计算查询向量和所有键向量之间的点积,然后通过softmax函数将这些点积转换为注意力权重。3.2.2缩放点积注意力缩放点积注意力是自注意力机制的一种变体,它在计算点积之前,先将点积结果除以键向量的维度的平方根,以避免点积结果过大导致softmax函数饱和。3.3缩放点积注意力缩放点积注意力(ScaledDot-ProductAttention)是自注意力机制的核心部分。它通过计算查询向量和键向量之间的点积,然后除以键向量维度的平方根,最后通过softmax函数将点积结果转换为注意力权重。3.3.1代码示例importtorch

importtorch.nn.functionalasF

defscaled_dot_product_attention(query,key,value,mask=None):

"""

计算缩放点积注意力

:paramquery:查询向量,尺寸为(batch_size,num_heads,seq_len,d_k)

:paramkey:键向量,尺寸为(batch_size,num_heads,seq_len,d_k)

:paramvalue:值向量,尺寸为(batch_size,num_heads,seq_len,d_v)

:parammask:掩码矩阵,尺寸为(batch_size,num_heads,seq_len,seq_len)

:return:注意力加权的值向量,尺寸为(batch_size,num_heads,seq_len,d_v)

"""

d_k=query.size(-1)

scores=torch.matmul(query,key.transpose(-2,-1))/torch.sqrt(torch.tensor(d_k,dtype=torch.float))

ifmaskisnotNone:

scores=scores.masked_fill(mask==0,-1e9)

attention_weights=F.softmax(scores,dim=-1)

returntorch.matmul(attention_weights,value)3.3.2数据样例假设我们有一个序列,其中包含三个元素,每个元素的键向量和值向量如下:键向量:k值向量:v查询向量:q3.3.3示例描述在这个例子中,我们使用缩放点积注意力计算查询向量和键向量之间的注意力权重,然后使用这些权重对值向量进行加权求和。query=torch.tensor([[1,1]],dtype=torch.float)

key=torch.tensor([[1,0],[0,1],[1,1]],dtype=torch.float)

value=torch.tensor([[10,0],[0,10],[5,5]],dtype=torch.float)

#计算缩放点积注意力

output=scaled_dot_product_attention(query.unsqueeze(0).unsqueeze(0),key.unsqueeze(0).unsqueeze(0),value.unsqueeze(0).unsqueeze(0))

print(output)输出结果将是一个加权求和后的值向量,表示模型在处理序列数据时,对序列中不同部分的注意力程度。通过上述的数学基础复习和缩放点积注意力的代码示例,我们深入了解了自注意力机制的计算过程。自注意力机制通过计算查询、键和值之间的注意力权重,使模型能够关注序列中的不同部分,从而提高模型在处理序列数据时的性能。4自注意力在序列建模中的应用4.1自注意力在自然语言处理中的应用4.1.1原理与内容自注意力机制(Self-Attention)在自然语言处理(NLP)中的应用主要体现在它能够处理序列数据,如文本,而无需依赖于递归或卷积操作。自注意力机制通过计算序列中每个位置的词与所有其他位置的词之间的关系,从而捕捉到全局的上下文信息。这种机制特别适合处理长距离依赖问题,因为它的计算复杂度与序列长度线性相关,而不是递归或卷积的二次或更高次复杂度。示例:Transformer模型中的自注意力在Transformer模型中,自注意力机制是其核心组成部分。下面是一个简化版的自注意力机制的实现代码,使用了PyTorch库。importtorch

importtorch.nnasnn

classSelfAttention(nn.Module):

def__init__(self,embed_size,heads):

super(SelfAttention,self).__init__()

self.embed_size=embed_size

self.heads=heads

self.head_dim=embed_size//heads

assert(self.head_dim*heads==embed_size),"Embedsizeneedstobedivisiblebyheads"

self.values=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.keys=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.queries=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.fc_out=nn.Linear(heads*self.head_dim,embed_size)

defforward(self,values,keys,query,mask):

N=query.shape[0]

value_len,key_len,query_len=values.shape[1],keys.shape[1],query.shape[1]

#Splittheembeddingintoself.headsdifferentpieces

values=values.reshape(N,value_len,self.heads,self.head_dim)

keys=keys.reshape(N,key_len,self.heads,self.head_dim)

queries=query.reshape(N,query_len,self.heads,self.head_dim)

values=self.values(values)

keys=self.keys(keys)

queries=self.queries(queries)

energy=torch.einsum("nqhd,nkhd->nhqk",[queries,keys])

#queriesshape:(N,query_len,heads,heads_dim),

#keysshape:(N,key_len,heads,heads_dim)

#energy:(N,heads,query_len,key_len)

ifmaskisnotNone:

energy=energy.masked_fill(mask==0,float("-1e20"))

attention=torch.softmax(energy/(self.embed_size**(1/2)),dim=3)

#attentionshape:(N,heads,query_len,key_len)

out=torch.einsum("nhql,nlhd->nqhd",[attention,values]).reshape(

N,query_len,self.heads*self.head_dim)

#valuesshape:(N,value_len,heads,heads_dim)

#outaftereinsum:(N,query_len,heads,head_dim),then

#outshape:(N,query_len,heads*head_dim)

out=self.fc_out(out)

#outshape:(N,query_len,embed_size)

returnout4.1.2解释上述代码定义了一个自注意力层,它接受values、keys和query作为输入,这些输入通常是从词嵌入层得到的。mask参数用于处理序列中不同长度的问题,确保在计算注意力时不会考虑填充的词。在forward函数中,首先将输入的嵌入向量分割成多个头(heads),每个头的维度为head_dim。然后,通过线性变换得到values、keys和queries。接下来,使用torch.einsum计算queries和keys之间的点积,得到能量矩阵,这反映了序列中词之间的相关性。如果提供了mask,则会将不相关的词的能量值设为一个非常小的数,确保在softmax操作中这些词的注意力权重接近于零。最后,将注意力权重与values相乘,得到加权后的values,然后将这些加权值重新组合并通过一个线性层,得到最终的输出。4.2自注意力在计算机视觉中的应用4.2.1原理与内容在计算机视觉中,自注意力机制被用于处理图像或视频等数据,以捕捉图像中不同部分之间的关系。与NLP中的自注意力不同,计算机视觉中的自注意力通常是在特征图上进行的,这些特征图可以是卷积神经网络的输出。自注意力机制能够帮助模型关注图像中的重要区域,而忽略不相关的背景信息。示例:Squeeze-and-ExcitationNetworks中的自注意力Squeeze-and-ExcitationNetworks(SENet)是一种在卷积神经网络中使用自注意力机制的模型。下面是一个SENet中自注意力模块的简化实现。importtorch

importtorch.nnasnn

classSELayer(nn.Module):

def__init__(self,channel,reduction=16):

super(SELayer,self).__init__()

self.avg_pool=nn.AdaptiveAvgPool2d(1)

self.fc=nn.Sequential(

nn.Linear(channel,channel//reduction,bias=False),

nn.ReLU(inplace=True),

nn.Linear(channel//reduction,channel,bias=False),

nn.Sigmoid()

)

defforward(self,x):

b,c,_,_=x.size()

y=self.avg_pool(x).view(b,c)

y=self.fc(y).view(b,c,1,1)

returnx*y.expand_as(x)4.2.2解释SELayer首先使用全局平均池化层(globalaveragepooling)对输入特征图进行压缩,得到一个通道维度的向量。然后,通过两个全连接层进行特征的“挤压”和“激励”,其中第一个全连接层的输出维度是输入维度的reduction倍,这有助于减少计算量。ReLU激活函数用于引入非线性,而Sigmoid函数则用于生成每个通道的权重,这些权重在0到1之间。最后,将生成的权重与原始输入特征图相乘,得到加权后的特征图,这有助于模型关注重要的特征通道。4.3自注意力在语音识别中的应用4.3.1原理与内容在语音识别中,自注意力机制被用于处理音频信号的特征表示,以捕捉语音中的长距离依赖关系。与NLP和计算机视觉不同,语音信号通常具有更高的时间分辨率,因此自注意力机制在处理这种高分辨率数据时特别有用。示例:Conformer模型中的自注意力Conformer是一种结合了卷积和自注意力机制的语音识别模型。下面是一个简化版的Conformer中自注意力模块的实现。importtorch

importtorch.nnasnn

classConformerAttention(nn.Module):

def__init__(self,d_model,num_heads,dropout=0.1):

super(ConformerAttention,self).__init__()

self.self_attn=nn.MultiheadAttention(d_model,num_heads,dropout=dropout)

self.conv_module=nn.Sequential(

nn.Conv1d(d_model,d_model*2,kernel_size=31,padding=15,groups=2),

nn.GLU(dim=1),

nn.Conv1d(d_model,d_model,kernel_size=15,padding=7,groups=2),

nn.SiLU(),

nn.LayerNorm(d_model),

nn.Dropout(dropout)

)

defforward(self,x,mask=None):

x=self.self_attn(x,x,x,attn_mask=mask)[0]

x=x.transpose(0,1)

x=self.conv_module(x)

x=x.transpose(0,1)

returnx4.3.2解释ConformerAttention模块结合了多头自注意力和卷积模块。self_attn使用PyTorch的MultiheadAttention层来计算自注意力,这与NLP中的自注意力机制类似。然后,通过一个卷积模块来捕捉局部特征,该模块使用了门控线性单元(GLU)和SiLU激活函数,以引入非线性和局部依赖性。最后,通过LayerNorm和Dropout层来规范化和防止过拟合,得到最终的输出。通过上述示例,我们可以看到自注意力机制在不同领域的应用,以及它是如何被集成到更复杂的模型中的。自注意力机制通过计算序列或特征图中元素之间的关系,帮助模型更好地理解输入数据的结构,从而在各种任务中提高性能。5Transformer模型中的自注意力5.1Transformer模型概述Transformer模型是深度学习领域中用于处理序列数据的一种创新架构,由Vaswani等人在2017年的论文《AttentionisAllYouNeed》中提出。与传统的循环神经网络(RNN)和长短期记忆网络(LSTM)不同,Transformer模型完全依赖于自注意力机制(Self-Attention)来处理输入序列,这使得模型在并行计算上更加高效,同时也能够捕捉到序列中的长距离依赖关系。5.1.1关键特性自注意力机制:允许模型在处理序列时,关注序列中的所有位置,而不仅仅是前一个位置。位置编码:由于模型没有循环结构,因此需要位置编码来提供序列中元素的顺序信息。多头注意力:通过并行使用多个注意力头,模型可以同时关注不同位置的多个信息,增强了模型的表达能力。5.2编码器中的自注意力层编码器是Transformer模型的一部分,负责将输入序列转换为中间表示。编码器由多个相同的层堆叠而成,每个层包含两个子层:自注意力层和前馈神经网络层。自注意力层允许模型在编码阶段关注输入序列中的所有单词,以捕捉它们之间的关系。5.2.1自注意力机制原理自注意力机制通过计算查询(Query)、键(Key)和值(Value)的点积来实现。查询、键和值都是从输入序列的嵌入向量中线性变换得到的。具体步骤如下:线性变换:输入序列的每个嵌入向量通过三个不同的线性变换,分别得到查询、键和值向量。点积计算:查询向量与所有键向量进行点积计算,得到的分数经过softmax函数归一化,生成注意力权重。加权求和:将注意力权重与所有值向量相乘,然后求和,得到加权后的表示向量。输出变换:加权后的表示向量再经过一个线性变换,得到最终的输出向量。5.2.2代码示例importtorch

importtorch.nnasnn

classSelfAttention(nn.Module):

def__init__(self,embed_size,heads):

super(SelfAttention,self).__init__()

self.embed_size=embed_size

self.heads=heads

self.head_dim=embed_size//heads

assert(self.head_dim*heads==embed_size),"Embedsizeneedstobedivisiblebyheads"

self.values=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.keys=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.queries=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.fc_out=nn.Linear(heads*self.head_dim,embed_size)

defforward(self,values,keys,query,mask):

N=query.shape[0]

value_len,key_len,query_len=values.shape[1],keys.shape[1],query.shape[1]

#Splitembeddingintoself.headspieces

values=values.reshape(N,value_len,self.heads,self.head_dim)

keys=keys.reshape(N,key_len,self.heads,self.head_dim)

queries=query.reshape(N,query_len,self.heads,self.head_dim)

energy=torch.einsum("nqhd,nkhd->nhqk",[queries,keys])

#queriesshape:(N,query_len,heads,heads_dim)

#keysshape:(N,key_len,heads,heads_dim)

#energy:(N,heads,query_len,key_len)

ifmaskisnotNone:

energy=energy.masked_fill(mask==0,float("-1e20"))

attention=torch.softmax(energy/(self.embed_size**(1/2)),dim=3)

out=torch.einsum("nhql,nlhd->nqhd",[attention,values]).reshape(

N,query_len,self.heads*self.head_dim

)

#valuesshape:(N,value_len,heads,heads_dim)

#outaftersplit:(N,query_len,heads,head_dim)

#outshape:(N,query_len,embed_size)

out=self.fc_out(out)

returnout5.3解码器中的自注意力层解码器也是由多个相同的层堆叠而成,每个层包含三个子层:自注意力层、编码器-解码器注意力层和前馈神经网络层。解码器中的自注意力层与编码器中的自注意力层类似,但增加了掩码(Mask)机制,以确保在生成序列时,模型只能关注到当前时刻及之前的输入。5.3.1掩码自注意力机制在解码器中,为了生成序列的下一个词,模型不能提前“看到”序列中未生成的部分。因此,掩码自注意力机制通过在注意力权重矩阵中设置掩码,将未来位置的权重设为负无穷,经过softmax函数后,这些位置的权重将为0,从而确保模型只能关注到当前时刻及之前的输入。5.3.2代码示例classMaskedSelfAttention(nn.Module):

def__init__(self,embed_size,heads):

super(MaskedSelfAttention,self).__init__()

self.self_attention=SelfAttention(embed_size,heads)

defforward(self,values,keys,query,mask):

#Createamaskforfuturetokens

future_mask=torch.tril(torch.ones(query_len,query_len)).to(device)

#Applythemask

masked_attention=self.self_attention(values,keys,query,future_mask*mask)

returnmasked_attention5.4多头自注意力机制多头自注意力机制是Transformer模型中的一个重要组成部分,它允许模型在不同的表示子空间中并行地执行自注意力计算。通过并行使用多个注意力头,模型可以同时关注不同位置的多个信息,增强了模型的表达能力。5.4.1多头自注意力原理在多头自注意力中,输入序列的嵌入向量被分成多个头,每个头独立地进行自注意力计算。计算完成后,所有头的输出被拼接在一起,然后通过一个线性层进行变换,得到最终的输出向量。5.4.2代码示例classMultiHeadAttention(nn.Module):

def__init__(self,embed_size,heads):

super(MultiHeadAttention,self).__init__()

self.heads=heads

self.head_dim=embed_size//heads

assert(self.head_dim*heads==embed_size),"Embedsizeneedstobedivisiblebyheads"

self.values=nn.Linear(embed_size,embed_size)

self.keys=nn.Linear(embed_size,embed_size)

self.queries=nn.Linear(embed_size,embed_size)

self.fc_out=nn.Linear(embed_size,embed_size)

defforward(self,values,keys,query,mask):

N=query.shape[0]

value_len,key_len,query_len=values.shape[1],keys.shape[1],query.shape[1]

values=self.values(values)

keys=self.keys(keys)

queries=self.queries(query)

values=values.reshape(N,value_len,self.heads,self.head_dim)

keys=keys.reshape(N,key_len,self.heads,self.head_dim)

queries=queries.reshape(N,query_len,self.heads,self.head_dim)

energy=torch.einsum("nqhd,nkhd->nhqk",[queries,keys])

ifmaskisnotNone:

energy=energy.masked_fill(mask==0,float("-1e20"))

attention=torch.softmax(energy/(self.embed_size**(1/2)),dim=3)

out=torch.einsum("nhql,nlhd->nqhd",[attention,values]).reshape(

N,query_len,self.heads*self.head_dim

)

out=self.fc_out(out)

returnout通过上述代码示例和原理介绍,我们可以看到Transformer模型如何利用自注意力机制和多头注意力机制来处理序列数据,捕捉长距离依赖关系,同时保持计算的高效性。6自注意力机制的优化与变体6.1自注意力机制的局限性自注意力机制在处理序列数据时展现出了强大的能力,但并非没有局限性。主要的局限包括:计算复杂度:自注意力机制的计算复杂度为O(n^2),其中n是序列长度。对于非常长的序列,这可能导致计算效率低下。局部信息处理:虽然自注意力机制能够处理长距离依赖,但在某些任务中,局部信息的处理仍然非常重要,自注意力可能在处理局部信息时不如卷积神经网络(CNN)有效。并行化限制:在训练过程中,自注意力机制的计算可以并行化,但在生成序列时,如在自然语言生成任务中,自注意力机制需要逐个生成词,这限制了其并行化能力。参数量:自注意力机制的参数量相对较大,特别是在多头注意力和深层模型中,这可能导致过拟合问题。6.2优化自注意力的策略为了解决上述局限,研究者们提出了多种优化策略:6.2.1局部自注意力局部自注意力机制通过限制注意力计算的范围,只考虑序列中的一部分元素,从而减少计算复杂度。例如,在处理文本时,可以只考虑每个词的前后几个词,而不是整个序列。6.2.2稀疏注意力稀疏注意力机制通过在注意力矩阵中引入稀疏性,减少不必要的计算。这通常通过设计特定的稀疏模式来实现,如blocksparseattention,其中注意力只在特定的block之间计算。6.2.3可变形自注意力可变形自注意力机制通过引入可学习的偏移量,使得模型能够关注到序列中更灵活的位置,从而增强模型的局部信息处理能力。6.2.4自注意力的并行化在生成序列时,通过预测整个序列的输出,而不是逐个生成,可以实现自注意力机制的并行化。例如,Transformer-XL和Reformer等模型通过引入循环机制或局部敏感哈希(LSH)来实现这一点。6.2.5参数效率优化通过使用低秩近似、因子分解等技术,可以减少自注意力机制的参数量,从而降低过拟合的风险。例如,Linformer模型通过将注意力矩阵投影到低维空间来实现这一点。6.3自注意力机制的变体自注意力机制的多种变体已经被提出,以适应不同的任务和场景:6.3.1多头注意力(Multi-HeadAttention)多头注意力机制通过将自注意力机制分解为多个“头”,每个头独立计算注意力,然后将结果合并,从而增强模型的表达能力。这允许模型在不同的注意力空间中捕捉到不同的信息。#多头注意力的实现示例

importtorch

importtorch.nnasnn

classMultiHeadAttention(nn.Module):

def__init__(self,embed_dim,num_heads):

super(MultiHeadAttention,self).__init__()

self.embed_dim=embed_dim

self.num_heads=num_heads

self.head_dim=embed_dim//num_heads

assertself.head_dim*num_heads==self.embed_dim,"Embeddingdimensionmustbedivisiblebynumberofheads"

self.q_linear=nn.Linear(embed_dim,embed_dim)

self.k_linear=nn.Linear(embed_dim,embed_dim)

self.v_linear=nn.Linear(embed_dim,embed_dim)

self.out_linear=nn.Linear(embed_dim,embed_dim)

defforward(self,query,key,value,mask=None):

batch_size=query.size(0)

#线性变换

query=self.q_linear(query)

key=self.k_linear(key)

value=self.v_linear(value)

#分割成多个头

query=query.view(batch_size,-1,self.num_heads,self.head_dim).transpose(1,2)

key=key.view(batch_size,-1,self.num_heads,self.head_dim).transpose(1,2)

value=value.view(batch_size,-1,self.num_heads,self.head_dim).transpose(1,2)

#计算注意力

scores=torch.matmul(query,key.transpose(-2,-1))/math.sqrt(self.head_dim)

ifmaskisnotNone:

scores=scores.masked_fill(mask==0,-1e9)

attention=torch.softmax(scores,dim=-1)

#加权求和

out=torch.matmul(attention,value)

out=out.transpose(1,2).contiguous().view(batch_size,-1,self.embed_dim)

#输出线性变换

out=self.out_linear(out)

returnout6.3.2轴注意力(AxialAttention)轴注意力机制通过将序列分解为多个轴,然后在每个轴上独立计算注意力,从而减少计算复杂度。这在处理高维数据,如图像和视频时特别有效。6.3.3因果注意力(CausalAttention)因果注意力机制在生成序列时使用,它通过屏蔽未来的信息,确保模型在生成当前元素时只考虑过去的信息。这是在自然语言生成和语音合成等任务中常用的策略。6.3.4相对位置编码(RelativePositionalEncoding)相对位置编码通过引入相对位置信息,增强自注意力机制对位置信息的敏感度。这在处理具有明确位置依赖性的任务时特别有用,如自然语言理解和机器翻译。6.3.5自注意力与卷积的结合一些模型,如ConvolutionalSelf-Attention,通过结合自注意力和卷积操作,既保留了自注意力的全局依赖捕捉能力,又增强了局部信息处理能力。通过这些优化策略和变体,自注意力机制在深度学习中的应用变得更加广泛和高效。7自注意力机制的实现与案例分析7.1自注意力机制的PyTorch实现在深度学习中,自注意力机制(Self-Attention)是一种允许模型在处理序列数据时关注输入序列中不同部分的重要性的技术。它在自然语言处理、图像识别等多个领域都有广泛应用。下面,我们将使用PyTorch框架来实现一个基本的自注意力机制。7.1.1实现代码importtorch

importtorch.nnasnn

importtorch.nn.functionalasF

classSelfAttention(nn.Module):

def__init__(self,embed_size,heads):

super(SelfAttention,self).__init__()

self.embed_size=embed_size

self.heads=heads

self.head_dim=embed_size//heads

assert(self.head_dim*heads==embed_size),"Embedsizeneedstobedivisiblebyheads"

self.values=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.keys=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.queries=nn.Linear(self.head_dim,self.head_dim,bias=False)

self.fc_out=nn.Linear(heads*self.head_dim,embed_size)

defforward(self,values,keys,query,mask):

N=query.shape[0]

value_len,key_len,query_len=values.shape[1],keys.shape[1],query.shape[1]

#Splittheembeddingintoself.headsdifferentpieces

values=values.reshape(N,value_len,self.heads,self.head_dim)

keys=keys.reshape(N,key_len,self.heads,self.head_dim)

queries=query.reshape(N,query_len,self.heads,self.head_dim)

values=self.values(values)

keys=self.keys(keys)

queries=self.queries(queries)

energy=torch.einsum("nqhd,nkhd->nhqk",[queries,keys])

#queriesshape:(N,query_len,heads,heads_dim),

#keysshape:(N,key_len,heads,heads_dim)

#energy:(N,heads,query_len,key_len)

ifmaskisnotNone:

energy=energy.masked_fill(mask==0,float("-1e20"))

attention=torch.softmax(energy/(self.embed_size**(1/2)),dim=3)

out=torch.einsum("nhql,nlhd->nqhd",[attention,values]).reshape(

N,query_len,self.heads*self.head_dim

)

out=self.fc_out(out)

returnout7.1.2代码解释初始化:我们定义了SelfAttention类,它包含embed_size和heads两个参数,分别表示嵌入向量的维度和注意力头的数量。head_dim是每个注意力头的维度,确保embed_size可以被heads整除。前向传播:在forward函数中,我们首先将输入的values、keys和query张量按照heads分割,然后通过线性层转换它们。接下来,我们使用torch.einsum计算查询和键之间的点积,得到能量矩阵。如果提供了mask,我们将其应用到能量矩阵上,以屏蔽不需要关注的部分。最后,我们计算注意力权重,将其与values相乘,然后通过另一个线性层得到输出。7.2自注意力在文本分类中的案例自注意力机制在文本分类任务中可以显著提高模型的性能。下面是一个使用自注意力机制进行文本分类的简单示例。7.2.1数据样例假设我们有以下文本数据:texts=["我喜欢这个电影","这个电影很糟糕","这家餐厅的食物很好吃"]

labels=[1,0,1]#1表示正面评价,0表示负面评价7.2.2模型实现importtorchtext

#Tokenizeandbuildvocabulary

tokenizer=lambdax:x.split()

TEXT=torchtext.data.Field(sequential=True,tokenize=tokenizer,lower=True)

LABEL=torchtext.data.LabelField(dtype=torch.float)

#Loaddata

train_data,test_data=torchtext.datasets.TextClassification.splits(TEXT,LABEL,texts,labels)

#Buildvocabulary

TEXT.build_vocab(train_data,max_size=10000)

LABEL.build_vocab(train_data)

#Definemodel

classTextClassifier(nn.Module):

def__init__(self,vocab_size,embed_size,heads,num_classes):

super(TextClassifier,self).__init__()

self.embedding=nn.Embedding(vocab_size,embed_size)

self.self_attention=SelfAttention(embed_size,heads)

self.fc=nn.Linear(embed_size,num_clas

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论