网易首页 > 网易号 > 正文 申请入驻

Attention is all you need 论文解析(附代码)

0
分享至

大数据文摘转载自数据派THU

作者:杨金珊

审校:陈之炎

“Attention is all you need”一文在注意力机制的使用方面取得了很大的进步,对Transformer模型做出了重大改进。

目前NLP任务中的最著名模型(例如GPT-2或BERT),均由几十个Transformer或它们的变体组成。

背景

减少顺序算力是扩展神经网络GPU、ByteNet和ConvS2S的基本目标,它们使用卷积神经网络作为基本构建块,并行计算所有输入和输出位置的隐含表示。在这些模型中,将来自两个任意输入或输出位置的信号关联起来,所需的操作数量随着位置距离的增加而增加,对于ConvS2S来说,二者是线性增长的;对于ByteNet来说,二者是对数增长的。这使得学习遥远位置之间的依赖关系变得更加困难。在Transformer中,将操作数量减少到一个恒定数值,这是以降低有效分辨率为代价的,因为需要对注意力权重位置做平均,多头注意力 (Multi-Head Attention)抵消了这一影响。

为什么需要transformer

在序列到序列的问题中,例如神经机器翻译,最初的建议是基于在编码器-解码器架构中使用循环神经网络(RNN)。这一架构在处理长序列时受到了很大的限制,当新元素被合并到序列中时,它们保留来自第一个元素的信息的能力就丧失了。在编码器中,每一步中的隐含状态都与输入句子中的某个单词相关联,通常是最邻近的那个单词。因此,如果解码器只访问解码器的最后一个隐含状态,它将丢失序列的第一个元素相关的信息。针对这一局限性,提出了注意力机制的概念。

与通常使用RNN时关注编码器的最后状态不同,在解码器的每一步中我们都关注编码器的所有状态,从而能够访问有关输入序列中所有元素的信息。这就是注意力所做的,它从整个序列中提取信息,即过去所有编码器状态的加权和,解码器为输出的每个元素赋予输入的某个元素更大的权重或重要性。从每一步中正确的输入元素中学习,以预测下一个输出元素。

但是这种方法仍然有一个重要的限制,每个序列必须一次处理一个元素。编码器和解码器都必须等到t-1步骤完成后才能处理第t-1步骤。因此,在处理庞大的语料库时,计算效率非常低。

什么是Transformer

Transformer是一种避免递归的模型架构,它完全依赖于注意力机制来绘制输入和输出之间的全局依赖关系。Transformer允许显著的并行化……Transformer是第一个完全依靠自注意力来计算输入和输出的表示,而不使用序列对齐的RNN或卷积的传导模型。

图1 Transformer 架构

从图1可以观察到,左边是一个编码器模型,右边是一个解码器模型。两者都包含一个重复N次的“一个注意力和一个前馈网络”的核心块。但为此,首先需要深入探讨一个核心概念:自注意力机制。

Self-Attention基本操作

Self-attention是一个序列到序列的操作:一个向量序列进去,一个向量序列出来。我们称它们为输入向量, ,…,和相应的输出向量, ,…,。这些向量的维数都是k。要产生输出向量,Self-attention操作只需对所有输入向量取加权平均值,最简单的选择是点积。在我们的模型的Self-attention机制中,我们需要引入三个元素:查询、值和键(Queries, Values and Keys)。


class SelfAttention(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),"Embed size needs to be div by heads"

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)
def forward(self,values,keys,query,mask):
N=query.shape[0]
value_len,key_len,query_len=values.shape[1],keys.shape[1],query.shape[1]

#split embedding into self.heads pieces
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])
#queries shape: (N,query_len, heads, heads_dim)
#keys shape: (N,key_len, heads, heads_dim)
#energy shape: (N,heads,query_len,key_len)
if mask is not None:
energy=energy.masked_fill(mask==0,float("-1e20"))#close it ,0
attention=torch.softmax(energy/(self.embed_size**(1/2)),dim=3)#softmax
out=torch.einsum("nhql,nlhd->nqhd",[attention,values]).reshape(N,query_len,self.heads*self.head_dim)
#attention shape: (N,heads, query_len,key_len)
#values shape: (N,value_len,heads,head_dim)#key_len=value_len=l
#after einsum(N,query_len,heads,head_dim) then flatten last two dim

out=self.fc_out(out)
return out

Queries,Values和Keys

在自注意力机制中,通常输入向量以三种不同的方式使用:查询、键和值。在每个角色中,它将与其他向量进行比较,以获得自己的输出(Query),获得第j个输出(Key),并在权重建立后计算每个输出向量(Value)。为了得到这些,我们需要三个维数为k * k的权重矩阵,并为每个计算三个线性变换:

图2 查询、值和键(Queries, Values and Keys)三元素

通常称这三个矩阵为K、Q和V,这三个可学习权值层应用于相同的编码输入。因此,由于这三个矩阵都来自相同的输入,可以应用输入向量本身的注意力机制,即“Self-attention”。

TheScaledDot-ProductAttention(带缩放的点积注意力)

输入由维的“查询”和“键”以及维的“值”值组成。我们用所有“键”计算“查询”的点积,每个“键”除以的平方根,应用一个softmax函数来获得值的权重。

使用Q, K和V矩阵来计算注意力分数。分数衡量的是对输入序列的其他位置或单词的关注程度。也就是说,查询向量与要评分的单词的键向量的点积。对于位置1,我们计算和的点积,然后是、2, 、等等,…

接下来应用“缩放”因子来获得更稳定的梯度。softmax函数在大的值下无法正常工作,会导致梯度消失和减慢学习速度[1]。在“softmax”之后,我们乘以“值”矩阵,保留想要关注的单词的值,并最小化或删除无关单词的值(它在V矩阵中的值应该非常小)。

这些操作的公式为:

Multi-head Attention(多头注意力)

在前面的描述中,注意力分数一次集中在整个句子上,即使两个句子包含相同的单词,但顺序不同,也将产生相同的结果。相反,如果想关注单词的不同部分,”self-attention”的辨别能力则比较大,通过组合几个自注意力头,将单词向量分成固定数量(h,头的数量)的块,然后使用Q, K和V子矩阵将自注意力应用到相应的块。

图3 多头注意力机制

由于下一层(前馈层)只需要一个矩阵,每个单词的一个向量,所以“在计算每个头部的点积之后,需要连接输出矩阵,并将它们乘以一个附加的权重矩阵Wo”[2]。最后输出的矩阵从所有的注意力头部获取信息。

PositionalEncoding(位置编码)

前文已经简单地提到,由于网络和self-attention机制是排列不变的,句子中单词的顺序是该模型中需要解决的问题。如果我们打乱输入句子中的单词,会得到相同的解。需要创建单词在句子中位置的表示,并将其添加到单词嵌入(embedding)中。

为此,我们在编码器和解码器栈底部的输入嵌入中添加了“位置编码”。位置编码与嵌入具有相同的维数,因此两者可以求和,位置编码有多种选择。

应用一个函数将句子中的位置映射为实值向量之后,网络将学习如何使用这些信息。另一种方法是使用位置嵌入,类似于单词嵌入,用向量对每个已知位置进行编码。“它需要训练循环中所有被接受的位置的句子,但位置编码允许模型外推到比训练中遇到的序列长度更长的序列”,[1]。

TransformerBlock(Transformer代码块)


class TransformerBlock(nn.Module):
def __init__(self, embed_size,heads,dropout,forward_expansion):
super(TransformerBlock,self).__init__()
self.attention=SelfAttention(embed_size,heads)
self.norm1=nn.LayerNorm(embed_size)
self.norm2=nn.LayerNorm(embed_size)

self.feed_forward=nn.Sequential(
nn.Linear(embed_size, forward_expansion*embed_size),
nn.ReLU(),
nn.Linear(forward_expansion*embed_size,embed_size)
)
self.dropout=nn.Dropout(dropout)
def forward(self,values,keys,query,mask):
attention=self.attention(values,keys,query,mask)
x=self.dropout(self.norm1(attention+query))
forward=self.feed_forward(x)
out=self.dropout(self.norm2(forward+x))
return out

Theencoder(编码器)


  • 位置编码:将位置编码添加到输入嵌入(将输入单词被转换为嵌入向量)。

    N=6个相同的层,包含两个子层:一个多头自注意力机制,和一个全连接的前馈网络(两个线性转换与一个ReLU激活)。它按位置应用于输入,这意味着相同的神经网络会应用于属于句子序列的每一个“标记”向量。


  • 每个子层(注意和FC网络)周围都有一个残余连接,将该层的输出与其输入相加,然后进行归一化。

  • 在每个残余连接之前,应用正则化:“对每个子层的输出应用dropout,然后将其添加到子层输入并正则化。

图4 编码器结构


class TransformerBlock(nn.Module):
def __init__(self, embed_size,heads,dropout,forward_expansion):
super(TransformerBlock,self).__init__()
self.attention=SelfAttention(embed_size,heads)
self.norm1=nn.LayerNorm(embed_size)
self.norm2=nn.LayerNorm(embed_size)

self.feed_forward=nn.Sequential(
nn.Linear(embed_size, forward_expansion*embed_size),
nn.ReLU(),
nn.Linear(forward_expansion*embed_size,embed_size)
)
self.dropout=nn.Dropout(dropout)
def forward(self,values,keys,query,mask):
attention=self.attention(values,keys,query,mask)
x=self.dropout(self.norm1(attention+query))
forward=self.feed_forward(x)
out=self.dropout(self.norm2(forward+x))
return out

DecoderBlock(解码器代码块)


  • 位置编码:编码器的编码相类似。

  • N=6个相同的层,包含3个子层。第一,屏蔽多头注意力或屏蔽因果注意力,以防止位置注意到后续位置。禁用点积注意力模块的软最大层,对应的值设置为−∞。第二个组件或“编码器-解码器注意力”对解码器的输出执行多头注意力,“键”和“值”向量来自编码器的输出,但“查询”来自前面的解码器层,使得解码器中的每个位置都能覆盖输入序列中的所有位置。,最后是完连接的网络。

  • 每个子层周围的残差连接和层归一化,类似于编码器。

  • 然后重复在编码器中执行的相同残差dropout。



class DecoderBlock(nn.Module):
def __init__(self, embed_size,heads,dropout,forward_expansion,device):
super(DecoderBlock,self).__init__()
self.attention=SelfAttention(embed_size,heads)
self.norm=nn.LayerNorm(embed_size)
self.transformer_block=TransformerBlock(
embed_size, heads, dropout, forward_expansion
self.dropout=nn.Dropout(dropout)
def forward(self,x,value,key,src_mask,trg_mask):
#source mask and target mask
attention=self.attention(x,x,x,trg_mask)#trg_mask is the mask mult-headed attention the first one in decoder block
query=self.dropout(self.norm(attention+x))
out=self.transformer_block(value,key,query,src_mask)
return out

在N个堆叠的解码器的最后,线性层,一个全连接的网络,将堆叠的输出转换为一个更大的向量,logits。

图5 解码器结构

Joining all the pieces: the Transformer(全部拼接起来构成Transformer)

定义并创建了编码器、解码器和linear-softmax最后一层等部件之后,便可以将这些部件连接起来,形成Transformer模型。

值得一提的是,创建了3个掩码,包括:

编码器掩码:它是一个填充掩码,从注意力计算中丢弃填充标记。

解码器掩码1:该掩码是填充掩码和前向掩码的结合,它将帮助因果注意力丢弃“未来”的标记,我们取填充掩码和前向掩码之间的最大值。

解码器掩码2:为填充掩码,应用于编码器-解码器注意力层。


class Transformer(nn.Module):
def __init__(
self,
src_vocab_size,
trg_vocab_size,
src_pad_idx,
trg_pad_idx,
embed_size=256,
num_layers=6,
forward_expansion=4,
heads=8,
dropout=0,
device="cuda",
max_length=100):
super(Transformer,self).__init__()
self.encoder=Encoder(
src_vocab_size,
embed_size,
num_layers,
heads,
device,
forward_expansion,
dropout,
max_length
self.decoder=Decoder(
trg_vocab_size,
embed_size,
num_layers,
heads,
forward_expansion,
dropout,
device,
max_length
self.src_pad_idx=src_pad_idx
self.trg_pad_idx=trg_pad_idx
self.device=device
def make_src_mask(self,src):
src_mask=(src!= self.src_pad_idx).unsqueeze(1).unsqueeze(2)
#(N,1,1,src_len)
return src_mask.to(self.device)
def make_trg_mask(self,trg):
N,trg_len=trg.shape
trg_mask=torch.tril(torch.ones((trg_len,trg_len))).expand(
N,1,trg_len,trg_len
return trg_mask.to(self.device)
def forward(self,src,trg):
src_mask=self.make_src_mask(src)
trg_mask=self.make_trg_mask(trg)
enc_src=self.encoder(src,src_mask)
out=self.decoder(trg, enc_src,src_mask, trg_mask)
return out

参考文献:

[1] Peter Bloem, “Transformers from scratch” blog post, 2019.

[2] Jay Alammar, “The Ilustrated Transformer” blog post, 2018.

编辑:王菁

校对:林亦霖

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
医生发现:每天早起后先排便的人,用不了半年,身体或迎来5改变

医生发现:每天早起后先排便的人,用不了半年,身体或迎来5改变

任医生聊健康
2026-06-08 20:00:48
康熙去世国库剩2716万两,雍正去世剩3453万,乾隆去世时剩多少?

康熙去世国库剩2716万两,雍正去世剩3453万,乾隆去世时剩多少?

云居历史
2026-06-27 17:18:51
江苏河道惊现2米蟒蛇尸体!背后藏饲养、放生两大关键问题

江苏河道惊现2米蟒蛇尸体!背后藏饲养、放生两大关键问题

匹夫来搞笑
2026-06-28 09:57:52
烂番茄91%,等了两年,这部王牌一回归就炸了

烂番茄91%,等了两年,这部王牌一回归就炸了

天天美剧吧
2026-06-27 15:02:13
三国启动CPTPP入约谈判,对中国入约是阻力还是新转机?

三国启动CPTPP入约谈判,对中国入约是阻力还是新转机?

靓仔情感
2026-06-27 15:50:30
韩国队止步小组赛,无缘11亿韩元晋级奖金

韩国队止步小组赛,无缘11亿韩元晋级奖金

懂球帝
2026-06-28 10:06:07
吃“春药”后,是什么感觉?医生说的2个案例,告诉你真实感受

吃“春药”后,是什么感觉?医生说的2个案例,告诉你真实感受

医学科普汇
2026-06-23 17:16:20
夫妻亲热时,这4个地方是“禁区”,再爱也不能做,小心搞出人命

夫妻亲热时,这4个地方是“禁区”,再爱也不能做,小心搞出人命

奇妙的本草
2026-06-26 12:50:26
内马尔左腿“变形”照引700万围观:伤痕令人揪心,淘汰赛或复出

内马尔左腿“变形”照引700万围观:伤痕令人揪心,淘汰赛或复出

星耀国际足坛
2026-06-27 20:46:46
2026高考成绩公布后,张桂梅式教育被批,再次证明:父母放任不管的代价,远比想象中更残酷......

2026高考成绩公布后,张桂梅式教育被批,再次证明:父母放任不管的代价,远比想象中更残酷......

二胡的岁月如歌
2026-06-27 18:18:29
男篮世青赛一夜三大惨案!日本29分惨败:中国队苦主爆冷欧洲劲旅

男篮世青赛一夜三大惨案!日本29分惨败:中国队苦主爆冷欧洲劲旅

篮球快餐车
2026-06-28 02:20:36
联合国最新报告:全球可卡因产量创新高,吸毒人数10年增加40%

联合国最新报告:全球可卡因产量创新高,吸毒人数10年增加40%

知识圈
2026-06-27 17:42:50
一华裔女子“代表全球华人”向以色列道歉,谁给的权利?网友炸锅

一华裔女子“代表全球华人”向以色列道歉,谁给的权利?网友炸锅

锅锅爱历史
2026-06-28 14:28:30
特朗普:“伊朗将不复存在”,巴铁刚打完电话,中方援助在路上了

特朗普:“伊朗将不复存在”,巴铁刚打完电话,中方援助在路上了

启迪你思维
2026-06-28 10:46:23
女演员千万别整容,看42岁王佳佳和40岁江疏影同框,就知道了

女演员千万别整容,看42岁王佳佳和40岁江疏影同框,就知道了

芬霏剧时光
2026-06-26 11:31:34
钱在集体挪窝

钱在集体挪窝

智远同学
2026-06-28 12:02:13
官方:与奥地利比赛在阿根廷国内收视率高达91.5%

官方:与奥地利比赛在阿根廷国内收视率高达91.5%

懂球帝
2026-06-28 03:09:05
明日五月十五,牢记:5样不上桌,福气不进门,家人吃出好福气

明日五月十五,牢记:5样不上桌,福气不进门,家人吃出好福气

小茉莉美食记
2026-06-28 13:30:08
为什么阿根廷门将带着伤病,在小组末轮也不休息?

为什么阿根廷门将带着伤病,在小组末轮也不休息?

星耀国际足坛
2026-06-27 20:57:50
不回短信?追梦曝光证据戳穿伦德博格谎言:整蛊菜鸟戏码要升温了

不回短信?追梦曝光证据戳穿伦德博格谎言:整蛊菜鸟戏码要升温了

追球者
2026-06-28 08:16:37
2026-06-28 15:43:00
大数据文摘 incentive-icons
大数据文摘
专注大数据,每日有分享!
6872文章数 94550关注度
往期回顾 全部

科技要闻

DeepSeek最新论文:如何让大模型跑得更快

头条要闻

一派出所被指集体去KTV招异性陪侍 涉事者都被保留公职

头条要闻

一派出所被指集体去KTV招异性陪侍 涉事者都被保留公职

体育要闻

韩国可算确定被淘汰了

娱乐要闻

曾沛慈拿下《乘风2026》年度总冠军

财经要闻

两只股票撑起的韩国股市,半年熔断 33 次

汽车要闻

蔚来ES大五座体验 全场景行李舱让你带着生活出发

态度原创

亲子
艺术
数码
健康
公开课

亲子要闻

怎么可以这样传话呢?

艺术要闻

15幅 乔治·莫兰迪的静物花卉特辑

数码要闻

欧洲热浪已致上百人死亡!中国空调爆单:二手价一度超新品价

“无糖汤圆”是否隐藏着健康陷阱?

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版