再探attention—self-attention原理详解

self-attention

self attention是论文《Attention is all you need》中提出的一种新的注意力机制。在了解了attention原理后,self-attention原理也就很好理解了。attention需要利用上一时刻的状态来计算权重,而self-attention与上一时刻没有任何关系,仅利用当前时间步的输入向量计算与每个时序向量的相关性,从而获得权重。这个性质决定了self-attention多个时间步可以并行推理,不受上一时间步的约束。

以下所有内容,如有侵权,请联系删除~

self-attention优缺点:

1.参数少,相比CNN与RNN,参数少,模型复杂度低(根据attention实现方式不同,复杂度不一)。

2.速度快,self-Attention 解决了 RNN 不能并行计算的问题。self-Attention机制每一步计算不依赖于上一步的计算结果,因此可以和CNN一样并行处理。

3.效果好,能够获取全局与局部之间的联系。RNN网络对长期依赖的捕捉会受到序列长度的限制。RNN处理较长的序列时,无法实现对长距离的记忆,这样就无法联系上下文获得有效的输出。selfattention机制利用了attention机制,计算每个序列与其他序列之间的关联,可以有效的获取上下文信息。

self attention的5个步骤:

  1. 根据输入获取Q,K,V。self-Attention的输入是时序向量[batch, time_steps, input_dim]。那么如何从输入的时序向量得到Q,K,V呢?输入的时序向量乘以3个矩阵便可以得到Q,K,V。这三个矩阵是可以训练的权重。假设需要的Q,K,V的维度是out_dim,则权重矩阵的shape为:(input_dim, out_dim)。需要注意的是这三个权重矩阵是权值共享的。这样就获得每个序列的Q,K,V。 Q,K,V的shape为:(batch, time_steps,out_dim)。

  2. 计算当前序列的注意力得分。每个序列的注意力向量计算方式都是一样的,以第一个序列为例:利用第一个序列的q与每一个序列的k相乘,每个序列上会计算出一个得分一共有time_steps个。这个得分用来衡量第一个序列应该在每个序列上投放多少的注意力。

    在批计算过程中,注意力矩阵shape为:[batch, time_steps, time_steps]

再探attention—self-attention原理详解_第1张图片

  1. 将权重除以8,这个8等于K的维度的开方,这是为了获得更稳定的梯度。

  2. 然后进行归一化,对当前序列的注意力向量取softmax。

  3. 将每个序列的注意力分数与每个序列的V相乘相加得到一个输出矩阵。

再探attention—self-attention原理详解_第2张图片

  1. 对seq个序列的注意力矩阵相加,获得当前序列的输出。

    在批计算时的shape为[batch,time_steps,out_dim]

  2. 转到2,继续下一个序列。

上述步骤可以用一个公式替代:
s o f t m a x ( Q ⋅ K T d k ) ⋅ V softmax({Q·K^T\over \sqrt{d_k}})·V softmax(dk QKT)V

self-attention的实现方式已了解,现在看一下代码,逻辑也很简单:

from keras import initializers
from keras import activations
from keras import backend as K
from keras.engine.topology import Layer
 
class MySelfAttention(Layer):
    
    def __init__(self,output_dim,kernel_initializer='glorot_uniform'**kwargs):
        self.output_dim=output_dim
        self.kernel_initializer = initializers.get(kernel_initializer)
        super(MySelfAttention,self).__init__(**kwargs)
        
    def build(self,input_shape):
        self.W=self.add_weight(name='W',
             shape=(3,input_shape[2],self.output_dim)# QKV
             initializer=self.kernel_initializer,
             trainable=True)
        self.built = True
        
    def call(self,x):
        q=K.dot(x,self.W[0])
        k=K.dot(x,self.W[1])
        v=K.dot(x,self.W[2])
        #print('q_shape:'+str(q.shape))
        e=K.batch_dot(q,K.permute_dimensions(k,[021]))#把k转置,并与q点乘
        e=e/(self.output_dim**0.5)
        e=K.softmax(e)
        o=K.batch_dot(e,v)
        return o
        
    def compute_output_shape(self,input_shape):
        return (input_shape[0],input_shape[1],self.output_dim)

参考:

https://jalammar.github.io/illustrated-transformer/

你可能感兴趣的:(时序深度学习,深度学习,自然语言处理,人工智能)