注意力机制实现技巧

本文详细介绍了注意力机制,包括注意力评分函数、多头注意力机制(如加性注意力和缩放点积注意力)、自注意力机制的实现技巧,以及与卷积、全连接和池化层的区别。重点讲解了如何在实际代码中高效地实现多头自注意力机制。
摘要由CSDN通过智能技术生成

提示:主要是结合李沐老师在B站的课以及《动手深度学习》做的一些笔记


一、注意力机制

参考视频参考代码
不随意线索(非自主性提示)
非自主性提示是基于环境中物体的突出性和易见性。假设目前环境中有5个物品:一份报纸、一篇研究报告、一杯咖啡、一个笔记本和一本书,所有纸制品都是黑白印刷的,但是咖啡杯是红色的。很自然咖啡杯相较于其他物品来说会更加引起人们的注意,如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/47a8408d8b9741e4ab2870028c243698.png
随意线索(自主性提示)
当我们想读书时,面对同样5个物体,这时我们的注意力便会放在书上。如下图所示
在这里插入图片描述
我的理解
以上述这个例子来说,不随意线索就是没有主观意识,主要受环境等客观因素影响;而随意线索是本身意识和认知产生,主要受主观因素影响。
注意力机制

  • 卷积、全连接、池化层都只考虑不随意线索
  • 注意力机制则显示的考虑随意线索
    • 随意线索被称之为查询(query)
    • 每个输入是一个值(value)和不随意线索(key)的对
    • 通过注意力池化层来有偏向性的选择某些输入

非参注意力池化层
给定数据 ( x i , y i ) , i = 1 , . . . , n (x_i, y_i),i = 1,...,n (xi,yi)i=1,...,n,平均池化是最简单的方案: f ( x ) = 1 n ∑ i y i f(x)=\frac{1}{n}\sum_iy_i f(x)=n1iyi注意 ( x i , y i ) (x_i, y_i) (xi,yi)是键值对, x x x是query(随意线索)。
更好的方案Nadaraya-Watson核回归 f ( x ) = ∑ i = 1 n K ( x − x i ) ∑ j = 1 n K ( x − x j ) y i f(x)=\sum_{i=1}^n\frac{K(x-x_i)}{\sum_{j=1}^nK(x-x_j)}y_i f(x)=i=1nj=1nK(xxj)K(xxi)yi注意 x x x是query, x j x_j xj是key, y i y_i yi是value。 K ( x ) K(x) K(x)是一个核函数,其基本思路是若 x x x x i x_i xi距离较近,则 K ( x − x i ) K(x-x_i) K(xxi)越大。
若使用高斯核 K ( u ) = 1 2 π e x p ( − u 2 2 ) K(u)=\frac{1}{\sqrt{2π}}exp(-\frac{u^2}{2}) K(u)=2π 1exp(2u2),那么有 f ( x ) = ∑ i = 1 n e x p ( − 1 2 ( x − x i ) 2 ) ∑ j = 1 n e x p ( − 1 2 ( x − x j ) 2 ) y i = ∑ i = 1 n s o f t m a x ( − 1 2 ( x − x i ) 2 ) y i f(x)=\sum_{i=1}^n\frac{exp(-\frac{1}{2}{(x-x_i)^2})}{\sum_{j=1}^nexp(-\frac{1}{2}{(x-x_j)^2})}y_i\\=\sum_{i=1}^nsoftmax(-\frac{1}{2}{(x-x_i)}^2)y_i f(x)=i=1nj=1nexp(21(xxj)2)exp(21(xxi)2)yi=i=1nsoftmax(21(xxi)2)yi参数化的注意力机制
在之前基础上引入可以学习的 w w w f ( x ) = ∑ i = 1 n s o f t m a x ( − 1 2 ( ( x − x i ) w ) 2 ) y i f(x)=\sum_{i=1}^nsoftmax(-\frac{1}{2}{((x-x_i)w)^2})y_i f(x)=i=1nsoftmax(21((xxi)w)2)yi

1.1 注意力评分函数

在这里插入图片描述
上图理解
本质就是经过注意力评分函数 a a a和softmax层就得到了一个注意力权重,查询和键相似度越高,注意力权重就越大,然后将注意力权重与对应键的值相乘再相加就得到了最终输出。实质就是加权平均值。
扩展到高维度
假设query q ∈ R q \bm{q}∈R^q qRq m m m对key-value ( k 1 , v 1 ) , . . . , (\bm{k}_1,\bm{v}_1),..., (k1,v1),..., 这里 k i ∈ R k , v i ∈ R v \bm{k}_i∈R^k,\bm{v}_i∈R^v kiRkviRv
注意力池化层 f ( q , ( k 1 , v 1 ) , . . . , ( k m , v m ) ) = ∑ i = 1 m α ( q , k i ) v i ∈ R v f(\bm{q},(\bm{k}_1,\bm{v}_1),...,(\bm{k}_m,\bm{v}_m))=\sum_{i=1}^mα(\bm{q},\bm{k}_i)\bm{v}_i∈R^v f(q,(k1,v1),...,(km,vm))=i=1mα(q,ki)viRv α ( q , k i ) = s o f t m a x ( a ( q , k i ) ) = e x p ( a ( q , k i ) ) ∑ j = 1 m e x p ( a ( q , k j ) ) ∈ R α(\bm{q},\bm{k}_i)=softmax(a(\bm{q},\bm{k}_i))=\frac{exp(a(\bm{q},\bm{k}_i))}{\sum_{j=1}^mexp(a(\bm{q},\bm{k}_j))}∈R α(q,ki)=softmax(a(q,ki))=j=1mexp(a(q,kj))exp(a(q,ki))R其中 a ( q , k i ) a(\bm{q},\bm{k}_i) a(q,ki)是注意力分数,为常量。
Additive Attention(加性注意力)

  • 可学参数: W k ∈ R h × k , W q ∈ R h × q , W v ∈ R h \bm{W_k∈R^{h×k}},\bm{W_q∈R^{h×q}},\bm{W_v}∈R^h WkRh×k,WqRh×q,WvRh a ( k , q ) = W v T t a n h ( W k k + W q q ) a(\bm{k},\bm{q})=\bm{W_v}^Ttanh(\bm{W}_k\bm{k}+\bm{W}_q\bm{q}) a(k,q)=WvTtanh(Wkk+Wqq)
  • 等价于将key和query合并起来后放入到一个隐藏层大小为h,输出大小为1的单隐藏层MLP。
    Scale Dot-Product Attention(缩放点积注意力)
  • 如果query和key都是同样的长度 q , k i ∈ R d \bm{q},\bm{k}_i∈R^d q,kiRd,那么可以 a ( q , k i ) = < q , k i > / d a(\bm{q},\bm{k}_i)=<\bm{q},\bm{k}_i>/\sqrt{d} a(q,ki)=<q,ki>/d 除以 d \sqrt{d} d 是减小对向量长度的敏感程度
  • 向量化版本
    • Q ∈ R n × d , K ∈ R m × d , V ∈ R m × v \bm{Q}∈R^{n×d},\bm{K}∈R^{m×d},\bm{V}∈R^{m×v} QRn×dKRm×dVRm×v
    • 注意力分数: a ( Q , K ) = Q K T / d ∈ R n × m a(\bm{Q},\bm{K})=\bm{QK}^T/\sqrt{d}∈R^{n×m} a(Q,K)=QKT/d Rn×m
    • 注意力池化: f = s o f t m a x ( a ( Q , K ) ) V ∈ R n × v f=softmax(a(\bm{Q,K}))\bm{V}∈R^{n×v} f=softmax(a(Q,K))VRn×v

1.2 多头注意力机制

基本思想
使用独立学习得到的h组不同的线性投影来变换查询、键和值。然后,将h组变换后的查询、键和值并行地送到注意力汇聚中。最后,将这h个注意力汇聚的输出拼接在一起,并且通过另一个可以学习的线性投影进行变换,以产生最终输出。
在这里插入图片描述
其公式解析如下:
给定查询 q ∈ R d q q∈R^{d_q} qRdq 键 k ∈ R d k 键k∈R^{d_k} kRdk和值 v ∈ R d v v∈R^{d_v} vRdv,每个注意力头 h i ( i = 1 , . . . , h ) h_i(i=1,...,h) hii=1,...,h的计算方法为: h i = f ( W i ( q ) q , W i ( k ) k , W i ( v ) v ) ∈ R p v h_i = f(W^{(q)}_iq, W^{(k)}_ik, W^{(v)}_iv)∈R^{p_v} hi=f(Wi(q)q,Wi(k)k,Wi(v)v)Rpv其中可以学习的参数包括 W i ( q ) ∈ R p q × d q W^{(q)}_i∈R^{p_q×d_q} Wi(q)Rpq×dq W i ( k ) ∈ R p k × d k W^{(k)}_i∈R^{p_k×d_k} Wi(k)Rpk×dk W i ( v ) ∈ R p v × d v W^{(v)}_i∈R^{p_v×d_v} Wi(v)Rpv×dv,以及代表注意力汇聚的函数f。f可以是加性注意力和缩放点积注意力。多头注意力的输出需要经过另一个线性转换,它对应着 h h h个头连结后的结果,因此其可学习参数是 W o ∈ R p o × h p v W_o∈R^{p_o×hp_v} WoRpo×hpv W o [ h 1 . . . . . . h h ] ∈ R p o W_o \begin{bmatrix} h_1 \\ ...\\...\\ h_h \\ \end{bmatrix} ∈R^{p_o} Wo h1......hh Rpo这里每一个 W i ( q ) 、 W i ( k ) 、 W i ( v ) W^{(q)}_i、W^{(k)}_i、W^{(v)}_i Wi(q)Wi(k)Wi(v)代表一组不同的全连接层(有多少个注意力头,就有多少组全连接层),用来对同一组 q 、 k 、 v q、k、v qkv做线性变换。
提示
沐神代码初看起来是一个单头注意力,实则是将 h h h组全连接层的参数拼接起来了。

1.3 多头自注意力机制代码实现技巧讲解

自注意力机制就是查询 q q q、键 k k k、值 v v v相同。现在我们假设我们有两句话,即batch_size大小为2,每句话长度为10,所以当前输入 x x x的shape就为(2, 10)。现在假设我们有注意力头为8个。一般在NLP来说,在输入到模型中时,我们会把每一个字映射为一个稠密向量,我们假设当前映射维度为96,所以经过“embbeding层”后,我们的输入 x x x维度变为了(2, 10, 96)(假设我们这里已经考虑了位置编码),按照我们的上述要求,所以有:

q 1 = k 1 = v 1 = q 2 = k 1 = v 2 = . . . = q 8 = k 8 = v 8 = x q_1=k_1=v_1=q_2=k_1=v_2=...=q_8=k_8=v_8=x q1=k1=v1=q2=k1=v2=...=q8=k8=v8=x
还有线性24个不同的线性变化层,如下:
l i n e a r q 1 、 l i n e a r k 1 、 l i n e a r v 1 、 . . . 、 l i n e a r q 8 、 l i n e a r k 8 、 l i n e a r v 8 linear_{q_1}、linear_{k_1}、linear_{v_1}、...、linear_{q_8}、linear_{k_8}、linear_{v_8} linearq1lineark1linearv1...linearq8lineark8linearv8
q 1 q_1 q1先通过 l i n e a r q 1 linear_{q_1} linearq1 k 1 k_1 k1通过 l i n e a r k 1 linear_{k_1} lineark1 v 1 v_1 v1通过 l i n e a r v 1 linear_{v_1} linearv1进行线性变化后,再采用注意力机制进行计算。这样的实现方法就很复杂。
在实际实现中,我们一般怎么实现的呢???
注意96*8=768,所以首先在 x x x经过embbeding层后我们是直接将输入 x x x的shape转化为(2,10,768),看懂了吗,就是经过这个embbeding层后,我们已经将8个头的的输入 x x x拼接在一起了,可能有部分人会说,这样这8个头输入不就不相等了吗,其实这个不要紧,就算你相等,最后还不是经过线性层变化再用注意力机制求解的嘛,所以这是第一个技巧
第二个技巧
因为我们有 q 、 k 、 v q、k、v qkv,目前的话我们只得到了第一个,所以我们将输入 x x x经过如下变换就可以得到 q 、 k 、 v q、k、v qkv

import torch.nn as nn
c_attn = nn.Linear(768, 3*768)
q, k, v = c_attn(x).split(768, dim=2)  # q、k、v的shape都为(2, 10, 768)

经过上述步骤后就得到了 q 、 k 、 v q、k、v qkv。然后做一个维度变换后,就可以使用注意力机制了,代码如下:

k = k.view(2, 10, 8, 768 // 8).transpose(1, 2) # (2, 8, 10, 96)
q = q.view(2, 10, 8, 768 // 8).transpose(1, 2) # (2, 8, 10, 96)
v = v.view(2, 10, 8, 768 // 8).transpose(1, 2) # (2, 8, 10, 96)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值