目录
摘要
导言
背景
模型架构
编码器解码器堆叠
注意力层
Scaled Dot-Product Attentions
Multi-Head Attention
transformer 中的注意力机制三种使用姿势
Position-wise Feed-Forward Networks
Embeddings and Softmax
Positional Encoding
Why Self-Attention
实验
Training Data and Batching
Hardware and Schedule
Optimizer
Regularization
Results
结论
附
batch norm VS layer norm
attention
摘要 主流序列转录模型(sequence transduction models)是基于复杂的RNN和CNN模型,其包含编码器-解码器(encoder-decoder)架构。性能最好的模型里通常也会在编码器-解码器之间使用注意力机制。文章提出了一个简单的新模型Transformer--仅仅依赖于注意力机制,不使用RNN和CNN。在两个机器翻译任务上展示了特别好的性能--更好的并行度,训练时间更短。在WMT 2014 Englishto-German翻译任务上达到了28.4的BLUE score,比目前最好的结果提升了2个BLUE。在WMT 2014 English-to-French翻译任务中,做了一个单模型,效果优于其他的模型,在8个GPU上训练了3.5天达到了41.8的BLUE,其训练时间相对于文献中其他模型的训练时间来说很少。Transformer架构能够很好的泛化到其他任务。
导言 【NLP|Transformer - Attention Is All You Need - 跟李沐学AI】在RNN里面是把序列从左到右一步一步的往前计算。假设序列是一个句子,他就是一个词一个词的往前看,对t个词会计算一个输出(隐藏状态)
文章图片
,其是由前一个时刻的隐藏状态
文章图片
和当前
文章图片
时刻的输入决定的。它是一个时序即一步一步的计算过程,难以并行。如果序列比较长的话,很早期的那些时序信息在后面的时候可能会丢掉,若不想丢掉,会需要比较大的
文章图片
,但是每一个时间步的
文章图片
都得存下来,会导致内存开销比较大。
已经有相关工作将attention应用到了编码器-解码器里面了,主要是用于如何将编码器的东西有效的传给解码器,即和RNN是一起使用的。
背景 卷积网络替换循环神经网络来减少时序的计算。但是卷积神经网络难以建模长序列,因为卷积计算每次去看一个比较小的窗口(例如一个3*3的像素块),若两个像素隔得比较远,得用很多层卷积,一层一层堆上去才能把隔的较远的像素融合起来。但是使用Transformer里面的注意力机制,每次能看到所有的像素,一层就能看到整个序列。但是卷积可以有多输出通道,每一个输出通道可以认为是识别不一样的模式,想要获得类似多输出通道的效果,提出了多头的注意力机制(Multi-Head Attention)。
自注意力机制(self-attention)将单个序列的不同位置联系起来以计算序列的表示,之前的有些工作已经使用了自注意力机制。
基于循环attention机制的端到端的memory networks,而不是序列对齐的循环,在简单语言中的问答和语言建模任务中表现良好。
Transformer是第一个仅依赖于自注意力机制来做的编码器-解码器框架。
模型架构 编码器将序列的符号表示
文章图片
映射成相同长度的序列的矩阵表示
文章图片
。对于句子来说,
文章图片
可以看作是句子中第t个词在词表中的下标,
文章图片
代表
文章图片
的向量表示,n表示序列的长度。给定编码器的输出
文章图片
,解码器会生成长为m的序列
文章图片
,这里的n可以不等于m。解码器的词是一个一个生成的(自回归,例如生成
文章图片
,可以拿到编码器输出
文章图片
和t时刻前的解码器的输出
文章图片
,过去时刻的输出会作为当前时刻的输入),这一点与编码器不同,编码器一次很可能能看完整个句子。
Transformer使用了一个编码器解码器的架构,将self-attention、point-wise、fully connecte layers堆叠在一起的。
文章图片
编码器的输入:例如中译英,就是中文的句子。解码器的输入:解码器在做预测的时候是没有输入的,实际上解码器之前时刻的一些输出作为输入。(shifted right一个一个往右移)。
编码器解码器堆叠 编码器:由
文章图片
个完全一样的层(layers)堆叠起来。每一个层里面有两个子层(sub-layers)。第一个子层是多头注意力子层(multi-head self-attention mechanism),第二个子层是一个全连接层(就是一个MLP)。每一个子层都用了一个残差连接,再接上layer normalization。每个子层的输出用式子可表示为
文章图片
。因为残差网络需要输入x和子层的输出
文章图片
的维度是一样的,否则得做投影,简单起见,将embedding层和所有子层的输出维度统一为
文章图片
。
解码器:解码器也是由6个完全一样的层堆叠起来的。解码器有3个子层。第一个子层是masked multi-head self-attention, 因为解码器做的是一个自回归(当前输出的输入集是上面一些时刻的输出),即预测的时候我们不应该看到之后那些时刻的输出(保证训练和预测的行为的一致性),但是在注意力机制中每次都能看到完整的输入,在解码器训练的时候,在预测t时刻的输出不应该看到t时刻以后的那些输入,可以通过带掩码的注意力机制实现。第二个子层是multi-headattention, 其中k,v来自编码器的输出,q来自解码器的前一子层的输出,这边不需要掩码,因为在翻译任务中,我们是能拿到完整的源语言的句子的,上一子层的mask是针对目标语言的,我们翻译的时候都是逐字逐词从前往后翻的。第三个子层同编码器第二个子层一样是一个全连接层。每一个子层都用了一个残差连接,再接上layer normalization。
注意力层 注意力函数是将一个query和一些key-value对映射成输出的一个函数,query,keys,values 和output都是一些向量,output是values的加权和(所以输出的维度和value的维度是一样的),每一个value的权重是其对应的 key 和 query 的相似度( compatibility function ,不同的注意力机制有不同的算法)算出的。
Scaled Dot-Product Attentions
queries 和 keys维度相同都是
文章图片
, values 的维度是
文章图片
, 将query和所有的keys 做内积作为相似度,得到的结果再除以
文章图片
,在用一个softmax来得到最后的权重(attention scores)。
实现时,将queries拼成一个矩阵
文章图片
,keys和values也一起组合成矩阵 K 和 V 。 则输出矩阵为:
文章图片
有两种常见的注意力机制,一种是加性注意力机制(additive attention,用一个隐藏层的全连接层实现,可以处理query和key不等长的情况),另一种是点积注意力机制(dot-product(multiplicative) attention,这边在传统点积的基础上除以了
文章图片
),这两种都差不多,但是点积实现比较简单,且比较高效,两次矩阵乘法就能搞定。当
文章图片
的值比较小的时候,这两个机制的性能相近,当
文章图片
比较大时,加性注意力机制比不带缩放的点积注意力机制性能好。
解释:
我们怀疑,对于很大的值,点积绝对值大幅度增长,套上softmax函数最大的那个值会更加接近1,剩下的值更加接近0,值更加向两端靠拢,计算梯度的时候会发现梯度比较小,因为softmax最后的结果是希望预测的值置信的靠近1,不置信的地方尽量接近0,这样就可以说收敛的差不多了,这时候梯度就比较小,就跑不动。
假设q和k的分量是均值为0、方差为1的独立随机变量,它们的点积
文章图片
是均值为0,方差为
文章图片
文章图片
文章图片
则
文章图片
个相加就是均值0, 方差
文章图片
,这个是时候若除以
文章图片
,则就又会变为均值0方差1。
transformer里面
文章图片
一般比较大,通常是512,为了抵消这种影响,我们通过除以
文章图片
将点积幅度缩小。
mask 主要是为了避免在第t时刻看到之后时间的东西,假设 query 和 key 是等长的,长度为 n ,且在时间上是能对应起来的,那么第t时间的 queries
文章图片
,那么在计算的时候应该只去看 keys 的
文章图片
,而不应该去看
文章图片
和它之后的东西,因为在当前时刻还没有
文章图片
,但是注意力机制会看到所有,
文章图片
会和 keys 里面所有的 k 做运算(即会和
文章图片
计算),其实是可以全部计算的,只要保证在计算权重的时候不要用到后面这些东西,所以就有图2中的 mask ,对于
文章图片
与
文章图片
及之后的那些计算值替换成一个非常大的负数,例如
文章图片
,这么大的负数在经过softmax后就会变成0,所以softmax之后
文章图片
对应的那些权重都会变成0,那么起作用的就剩下
文章图片
,相当于在计算output的时候只用了 values 的
文章图片
,后面的那些没有看,所以mask的作用是训练的时候让第t个时刻的 query 只看对应的前面那一些的key-values 的 pair 对,使预测的时候能跟训练的时候一一对应上的。
文章图片
Multi-Head Attention
与其做一个单个的注意力函数,不如将整个 queries , keys , values 投影到一个低维的空间投影h 次,再做 h 次的注意力函数,将每一个函数的输出并在一起再投影回来得到我们最终的输出,见图2。 Scaled Dot-Product Attentio 里面没有可学的参数,注意力函数就是内积,有时候为了识别不一样的模式,希望可以有一些不一样的计算像素的方法,若用加性注意力机制还是有权重可以学的,Multi-Head Attention 先投影到一个低维空间,这个投影的 W 是可以学的,给 h 次机会希望可以学到不一样的方法,使得在投影进去的那个度量空间能匹配不同模式需要的一些相似函数,最后再投影回来(有点像卷积里面多输出通道的感觉)。
文章图片
其中,
文章图片
投影矩阵的维度
文章图片
我们采用
文章图片
个并行的注意力层,或者说heads,则各个维度是
文章图片
。由于每个head的维数减少,总计算成本与全维单头部注意力的计算成本相似。虽然我们看到式(3)有许多小矩阵的乘法,实际上也可以通过一次的矩阵乘法来实现。这边贴一个哈佛大学的pytorch版本的实现, The Annotated Transformer,先做一个不改变维度的投影,Q 、 K 、 V 是三个不同的投影矩阵,且投影矩阵的形状均是是
文章图片
,通过一个输入输出均是
文章图片
的线性层实现,将得到的矩阵变成我们要的形状,先 reshape 将最后一维切成
文章图片
,再 transpose 因为我们希望得到的最后两维是序列长度和 embedding 维度
文章图片
。
def clones(module, N):
"Produce N identical layers."
return nn.ModuleList([copy.deepcopy(module) for _ in range(N)])linears = clones(nn.Linear(d_model, d_model), 4)
query, key, value = https://www.it610.com/article/[
lin(x).view(nbatches, -1, self.h, self.d_k).transpose(1, 2)
for lin, x in zip(linears, (query, key, value))
]
transformer 中的注意力机制三种使用姿势
编码器中的注意力机制,设batch_size=1,句子长度n, embedding 维度
文章图片
,则它的输入是形状为
文章图片
的向量( n 个长度为
文章图片
的向量),注意力层的三个输入( queries , keys , values)是一根线过去的然后 copy 三次,即 queries , keys , values 是同一个东西,这就是自注意力机制,n 个queries ,每一个 query 会得到一个输出( values 的加权和,权重是query 和对应的 keys的相似度),则有 n 个输出,输出的最后一维的长度和 values 最后一维的长度一样也是
文章图片
, 即输入输出的形状一样。由于多头会学习出 h 个不同的距离空间。
解码器中的注意力机制,和编码器类似,可能就是长度变成 m ,embedding 维度也是
文章图片
。有区别的是编码器有 mask ,当算 t 时刻的 query 对应的输出,不应该看到 t 时刻之后的东西,t 时刻之后的权重要设置为0(赋一个大负数,这样过softmax后就是0啦,即只有 t 时刻前的起作用 (不包括 t 时刻))。
编码器解码器之间的注意力机制,这边不再是自注意力了, keys 和 values 来自编码器最后一层的输出(形状是
文章图片
),queries 是来自解码器前一层的输出(形状是
文章图片
)。意味着对解码器的每一个 query 要算一个输出(来自编码器输出的 values 的加权和),可以理解为将编码器的输出根据解码器的输出拎出来。
Position-wise Feed-Forward Networks
他就是一个 MLP ,这边有一个 Position-wise , position 输入序列有很多个词,一个词就是一个点 position ,把一个 MLP 对每一个词作用一次(对每一个词作用的是同样一个 MLP ),就是 MLP 作用在最后一个维度(每一个词共享同一个权重参数矩阵),它的另一种描述方式是两个kernel大小为1的卷积。
文章图片
有两个线性层,第一个线性层的输出维度是
文章图片
,将维度扩大了4倍,最后有一个残差链接,为了保证维度不变,所以第二层将维度投影回
文章图片
。
解释,关于transformer怎么有效的使用序列信息。
考虑简单情况,无残差连接,也没有 layerNorm ,然后 attention 是一个单头的。在 transformer 的 MLP 虽然画了多个棕色的框,但是其实是一个它们的权重是一样的,每个 MLP 对你每一个输入的点做运算,会得到一个输出。输入和输出的大小都是一样的, attention 的作用就是把整个序列里面的信息抓取出来做一次汇聚 aggregation , 所以序列中我们感兴趣的信息就已经抓取出来了,以至于在做投影( MLP )映射成我们更想要的语意空间的时候,因为已经涵盖了我们要的序列信息,所以每个 MLP 只要在对每个点独立做就行。因为序列信息已经被汇聚完成,所以 MLP 是可以分开做的。这就是transformer 如何抽取序列信息然后把这些信息加工成我们最后要的语意空间的向量的过程。
RNN,棕色的 MLP 也是每个时间步共享权重矩阵。如绿色的线表示把上一时刻的输出(作为历史信息)放回来和该时刻的输入一起并进去(就完成了信息的传递),得到当前时刻的输出。
RNN 和 Transformer 一样都是用一个 MLP 来做语意空间的转换,不同的是传递序列信息的方式,RNN是把上一时刻的信息输出传入到下一时刻做输入, Transformer 是通过一个 attention 层去全局的抓取整个序列里面的信息,然后再用 MLP 做语义的转换。关注带你都是如何有效的使用序列信息。
文章图片
Embeddings and Softmax
对于任何一个词学习长为
文章图片
的向量表示它,编码器、解码器以及 softmax 前的线性均需要 embedding ,这三者是一样的权重,权重乘上了
文章图片
。
解释:为啥要乘上
文章图片
,为了后面加上位置编码的时候,能在同一个 scale 上不被淹没,参照 transformer encoder输入单词,对单词进行embedding的时候,为什么乘根号d?? - 知乎 @王四喜 和 @Quokka 。
因为是3个 embedding 层共享权重的,其中有 softmax 前的线性层,通常是要用 xavier 等方法初始化的,也即编码器、解码器的 embedding 亦是如此。xavier 初始化的
文章图片
,这边的
文章图片
,即
文章图片
,维度比较大的时候学的权重值就会变小,但是之后要加位置编码,所以想把
文章图片
,这样相加的时候就在同一个 scale 上。
Positional Encoding
attention 是没有时序信息的,输出是 values 的加权和,权重是 query 和 keys 的相似度,和序列信息无关,不会关注 key-value 对在序列的什么位置,换句话说,给定一句话,将词打乱 attention 的结果是一样的,尽管词在序列中的顺序发生了变化,但是加权和不会变,这是不合理的,词序打乱可能会影响语义,所以需要通过别的方式加入时序信息。 RNN 是上一时刻的输出作为下一时刻的输入来传递历史信息,其本就是带时序的。在输入中天机时序信息,例如一个词在位置 i ,将位置
文章图片
加入到输入里面。
文章图片
思路:在计算机中假设用32位的整数表示数字,就是说用32个 bit ,每个 bit 上有不同的值来表示
文章图片
? ,可以认为一个数字使用一个长为32的向量表示的。现在,词会被表示成一个维度为
文章图片
? 的向量,同样这里我们用
文章图片
? 的向量表示数字,具体的值是通过周期不一样的 sin 和 cos 函数来算出来的。
其中pos 是位置,i 是维度。 也就是说,位置编码的每个维度对应于一个正弦曲线。 这些波长的范围是
文章图片
,准确来说范围是
文章图片
。 我们选择这个函数是因为我们假设它允许模型很容易通过相对位置来学习到相对位置信息,因为对任意确定的偏移k,
文章图片
?可以表示为
文章图片
?的线性函数。
还使用可训练的位置embeddings进行了试验,发现这两个版本产生几乎相同的结果(参见表 3 的 E 行)。 选择正弦曲线,因为它可以允许模型推断比训练期间遇到的更长的序列。
下图中,
文章图片
,这边序列长度取了100,在 embedding 维度 上切
文章图片
时的位置编码,固定住i,即最后一维,只看其值随位置的变化则是一个个不同周期的正弦(偶)或余弦函数(奇),波长随着 i 的增大而增大。
plt.figure(figsize=(15, 5))
# 词嵌入维度512, 置0比率为0
pe = PositionalEncoding(512, 0.)
# 向pe中传入一个全零初始化的x,相当于展示pe, [bs, seq_len, d_model]
y = pe(torch.zeros(1, 100, 512))
for dim in range(70, 74):
plt.plot(np.arange(100), y[0, :, dim].data.numpy())plt.legend(['dim {}'.format(p) for p in range(70, 74)])
plt.show()
文章图片
Why Self-Attention
文章图片
?
表1比较了四种不一样的层,比较了三个方面,复杂度,计算的顺序性(这个越少越好,表示下一步计算必须等前面多少步计算完成),最大的长度(表示信息从一个数据点到另一个数据点要走多远,越短越好)。 self-attention 复杂度,例如 queries 和 keys 长度是 n,它们的列数都是d,所以是
文章图片
,顺序性,因其就是几个矩阵乘法,矩阵乘法并行度较高,所以是
文章图片
的,最大的长度,一个 query 可以和所有的 keys 做运算,输出是所有 values 的加权和,所以任何一个 query 和任何一个很远的 key-value 对只需要一次就可以。循环层,复杂度,序列长度是 n ,维度是d,可以理解为过 n 次 dense layer ,就是
文章图片
,对比 self-attention 若是 n 大则循环层复杂度低, d 大则 attention 复杂度低,现在看来 n 和 d 在差不多的数据层面所以这俩差不多复杂度,但是由于循环层需要一个一个做运算,下一步计算必须等前面步计算完成,所以并行化上比较亏,信息从第一个数据点到最后一个数据点要走 n 步,对长度比较长的序列不够友好,因为信息会走丢。卷积, kernel 是 k , d 是输入输出的通道数, k 通常不会很大,一般就是3或5等类似,卷积的并行度比较高,卷积的一个点每一次是有一个长为 k 的窗口来看的,所以 k 距离以内的信息一次就能传递,但是超出 k ,需要一层一层的叠上去。self-attention( restricted ), query 只跟最近的 r 个邻居做运算,但是相对原先的自注意力机制信息从一个数据点到另一个数据点可能需要走更多的步数。attention 主要关心的是对于特别长的信息能把整个信息整合的更好,所以self-attention( restricted )不常用,还是原始的自注意力机制用的更广一点。
实际上, attention 对模型的假设更少,想要达到和 RNN 、 CNN 相同的效果则需要更大的模型和更多的数据,所以现在基于 transformer 的模型都是特别大特别贵。
实验 Training Data and Batching 在标准的WMT 2014 English-German数据集上进行了训练,其中包含约450万个句子对。 这些句子使用 byte-pair encoding (BPE,把词根提出来,可以使整个字典比较小)进行编码,源语句和目标语句共享大约37000个 tokens 的词汇表 ( 编码器 和 解码器的 embedding 就可以公用,模型会比较简单)。 对于英语-法语翻译,我们使用更大的WMT2014 English-French 数据集,它包含3600万个句子,并将tokens分成32000个word-piece词汇表[38]。 序列长度相近的句子一起进行批处理。 每个训练批次的句子对包含大约25000个源tokens和25000个目标tokens。
Hardware and Schedule 我们在一台具有8个NVIDIA P100 GPU的机器上训练我们的模型。 使用本文描述的超参数的base models,每个 batch 耗时约0.4秒。 我们的base models共训练了10万步,花费12小时。 如表3底部描述的大模型, 每个 batch 耗时约1.0秒,大模型训练了30万步(3.5天)。
Optimizer 使用Adam优化器,其中
文章图片
? ( 这边
文章图片
取的比较小,常用的是0.999)及
文章图片
?。 根据以下公式在训练过程中改变学习率:
文章图片
这对应于在第一次warmup_steps 步骤中线性地增加学习速率,并且随后将其与步骤数的平方根倒数成比例地减小。 我们使用warmup_steps = 4000。学的向量越长的时候,学习率要低一点。先由一个较小的值慢慢爬到一个高的值,爬到之后按照步数的0.5次方衰减。这边几乎没啥可调的超惨,本来 adam 对学习率不怎么敏感。
下面直观感受一下,学习率的变化情况,蓝色的
文章图片
,其余的
文章图片
,从蓝色线和橙色线可以看出,大的
文章图片
学习率要低一点。红色的
文章图片
,其余的
文章图片
,
文章图片
越小,学习率一开始涨的越快,但是达到一个临界点后他们的学习率一样,毕竟过了
文章图片
后,学习率就由第一部分决定。
文章图片
Regularization 训练期间我们采用三种正则化:
Residual Dropout 将 dorpout 应用到每个子层的输出,在进入残差连接和 layerNorm 之前。 此外,在编码器和解码器中,将dorpout应用到embeddings和位置编码求和。 对于 base model ,我们使用
文章图片
?丢弃率,把10%的元素置成0,剩下的元素乘上1.1(
文章图片
)。可以看到对每一个带权重的层在输出上都使用了 dorpout 。
Label Smoothing 在训练过程中,我们使用的label smoothing的值为
文章图片
? 。 这损害了perplexity,因为模型学得更加不确定,但提高了准确性和BLEU得分。
Results
文章图片
?
文章图片
结论 文中提出的Transformer,是第一个完全基于注意力机制的序列转录模型--将编码器-解码器架构常用的循环层替换成了multi-headed self-attention。在机器翻译的任务上,Transformer的训练要比比循环层卷积层快很多。在WMT 2014 English-to-German 和 WMT 2014 English-to-French翻译任务上,达到了最好的效果。在 WMT 2014 English-to-German 任务上,效果优于所有的模型。很看好基于注意力机制模型的前景,并打算运用到其他任务上。我们计划将Transformer扩展到涉及输入和输出模式的文本以外的任务上,并研究local, restricted注意机制,以有效处理图像、音频和视频等。generation less sequential 也是一个研究方向。
附 batch norm VS layer norm 考虑二位输入的情况,每一行是样本,每一列是特征,batch norm就是每次在一个batch内将每一个列(每一个特征)变成均值0方差1。训练阶段在小批量里面的均值方差;算出全局的均值方差存起来在预测的时候用。还会学一个
文章图片
?出来,即可以把这个向量通过学习变为方差为某个值均值为某个值的东西。
同考虑二维输入,layer norm在很多时候和batch norm是一样的,不过layer norm是对每个样本做了normalization,即把每一行变为均值0方差1的向量,可以认为是把矩阵转置一下,做一个batch norm,再转置回去。
但是transformer里面的输入是三维的,输入的是一个序列的样本,每一个样本里面有很多个元素,每个元素有自己的embedding,形状是
文章图片
?,这时候若用batch norm,每次取一个特征,把他的序列的元素以及batch全部搞出来
文章图片
?展平成一个向量
文章图片
?,将其变为均值0方差1。对于layer norm,就是每次取一个样本,把它的embedding和batch全搞出来
文章图片
?展平成一个向量
文章图片
?,将其变为均值0方差1。对于这种变长的序列通常 layer norm 用的比较多,因为序列,每个样本的长度
文章图片
?可能会发生变化,见图蓝色batch norm,黄色layernorm,主要就是两种切法的不同,会带来计算均值方差的不同,若样本长度变化比较大,每次算一个batch里面的均值方差抖动相对较大,还有一点,预测的时候我们会记下全局的均值方差,若预测的样本比较长,我们在训练的时候没见到伸出去那么长的之前算出的均值方差不那么好用。layernorm是每一个样本自己算均值方差,也不需要存下全局均值方差,毕竟是针对样本的,反正无论长短都是在各自样本里面算的,相对来说稳定一些。
文章图片
?
attention
文章图片
?
Q = torch.tensor([[[0.1, 0.5, 0.1, 0.01], [0.6, 0.2, 0.1, 0.02], [0.01, 0.02, -0.01, -0.01]]])
K = torch.tensor([[[0.1, 0.4, 0.05, 0.05], [0.5, -0.1, 0.08, 0.05]]])
V = torch.tensor([[[0.15, 0.38, 0.06, 0.06, 0.05], [0.55, -0.12, 0.08, 0.06, 0.06]]])
att = torch.matmul(Q, K.transpose(-2, -1))
att_score = att.softmax(dim=-1)
out = torch.matmul(att_score, V)
print("Q: {}, K: {}, V: {}, att_score: {}, out: {}".format(
Q.size(), tuple(K.size()), tuple(V.size()), tuple(att_score.size()), tuple(out.size())
))
print("att:")
print(att)
print("att_score:")
print(att_score)
print('out:')
print(out)
文章图片
?
当用内积做相似度是,Q和K最后一维要相同,output是values的加权和(所以输出和values 的最后一维相同),output倒数第二维是Q倒数第二维的维度,因为本来一个query,就是通过该query和所有的keys来算得相似度向量,该向量的元素个数是keys的个数,这些元素又是对应的values的权重,那么一个query会得到一个加权和,可以理解为该序列values在给定query下的表示。
推荐阅读
- nlp|NLP之文本分类(一)---文本分类描述
- #|新闻主题分类任务——torchtext 库进行文本分类
- 自然语言处理|自然语言处理(七)(AG_NEWS新闻分类任务(TORCHTEXT))
- KBQA|使用句法依存分析实现KBQA中的约束挂载
- TensorFlow|Tensorflow 神经网络训练加速
- 神经网络|Datawhale-李宏毅深度学习/神经网络训练方法
- 深度学习|【深度学习】吴恩达深度学习-Course1神经网络与深度学习-第三周浅层神经网络作业
- 深度学习|神经网络和深度学习-第2周 logistic-regression-as-a-neural-network
- 深度学习|神经网络和深度学习(二)-神经网络的编程基础