Transformer中的核心概念III-Attention

发布于:2025-09-01 ⋅ 阅读:(18) ⋅ 点赞:(0)

Attention机制可以说是Transformer模型中最核心的概念了,正是Transformer模型采用了Attention机制使得Transformer模型具有比深度学习模型更加有力的信息提取能力。

1. 什么是Attention

Attention机制的产生渊源

注意力机制源自神经学中的Hebb学习规则,Hebb的理论认为在同一时间被激发的神经元间的联系会被强化。
在这里插入图片描述
注意力机制成为人工智能中的一个概念则比较复杂,目前来讲有3个不同版本。
版本1,2014年由Yoshua Bengio和Dizmitry Bahdanau提出
在这里插入图片描述
版本2:Andrej Karpathy在注意力机制论文发表的2年之前在与Dizmitry Bahdanau的邮件中提出了注意力机制
在这里插入图片描述
版本3:Jürgen Schmidhuber在1990-1991之间发表的相关论文中,提出了线性复杂度Transformer和注意力机制。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Attention机制的原理

Attention机制中的核心要素

人工智能中的Attention机制注意包括3个核心要素,查询(Query)、键(Key)、值(Value),此外还一个分数(Score)的概念是Attention机制中的一个必要概念。
• 查询(Query):用于表示模型当前需要关注的信息,相当于关键词,一般用张量形式表示
• 键(Key):用于表示输入到模型中序列的每个信息单元的标识符或标签,用于与Query进行比较,以决定哪些信息是相关的, 在机器翻译任务中,Key可能是源语言的每个单词或短语的特征张量
• 值(Value):Value通常包含输入序列的实际信息,当Query和Key匹配时,相应的Value值被用于计算输出
• 分数(Score): Score又称为注意力分数,用于表示Query和Key的匹配程度,Score越高,模型对当前信息单元的关注度越高
用一个比较形象的比喻:在图书馆查找想要的图书,Query是要查找的关键词,根据关键词(Query)在图书馆中图书的标题进行查找,这里图书的标题就是Key,当根据关键词(Query)查找到相应的图书标题(Key)之后,根据图书的标题(Key)提取对应图书的内容,而这里图书的内容就是Value。

Attention的核心原理

Attention机制的核心原理可以用下图表示:
在这里插入图片描述
根据查询(Query)在不同的键(Key)中相似性进行度量,不同的键(Key)与查询(Query)的相似性也不同,这里相似性度量的结果就是score,据对应的score值对相应的值(Value)根进行调整从而得到注意力机制对应的输出。关于Attention核心机制需要对以下几点进行说明:
(1) 查询(Query)、键(Key)、值(Value)是张量,不是向量,它们相互之间的运算属于张量的运算,这里的Query、Key和Value是可学习的,也就是可以通过训练过程动态调整,使得最后的结果满足某种最优;
(2) 注意力的第一个核心操作就是度量Key中包含多少Query,用专业的术语表达就是度量Key与Query的相似性;
score=similarity measure (Query, Key)
这里的相似性度量只是一个泛指,并不是具体的操作,数学当中有多个关于相似性度量的运算,这里可以根据实际的需求进行变换,不同的相似性度量运算也产生了不同类型的注意力机制,相似性度量的结果,在注意力机制中有了一个明确的名称,叫做注意力分数(score)。
(3) 得到注意力score之后需要进行softmax操作,softmax操作的作用是归一化,将注意力分数经过softmax归一化后的结果称为注意力权重。softmax操作实质上是在量化地衡量各个词的信息贡献度。
(4) 注意力的第二个核心操作就是将相应的Value与对应的softmax的结果作为attention的输出

Attention机制与卷积的对比

在这里插入图片描述
Attention机制本质作用是特征提取,而卷积也是深度学习中一种特征提取方法,这两者有一定相似之处,但是又有一定不同,下面将两者进行对比分析:
(1) Attention机制与卷积在本质上相类似。Attention机制中的Query相当于卷积操作中的卷积核,Key相当于CNN中的图片矩阵,Query与Key相似性度量的过程与CNN中卷积操作相对应。卷积核与图片中相应的区域进行卷积的过程实际上就是使用卷积核与图片中相应区域相似性进行度量,这与Attention机制中进行Query与Key的相似性度量在思路上是一致的。
(2) Attention机制与卷积虽然都是在进行相似性度量,但Attention机制在相似性度量实现的方式上却比卷积先进。CNN中的卷积核是以高斯核为基础,在不同的卷积操作中,只是高斯核σ数值的变化和卷积核尺寸大小的改变,执行卷积运算的运算法则是固定的。而Attention机制中的Query和Key的构成则是没有限制的,而且是可训练的,是一个动态优化的过程。Query和Key进行的相似性度量方式也是可以选择的,可以根据所要提取的信息选取不同的相似性度量计算方法。这两点上可以显示出Attention机制要比CNN中的卷积具有更大的灵活性。

Attention的变种

在Attention机制框架下,不断演化,产生了多种Attention机制,如下图所示。
在这里插入图片描述

2. Transformer中的self-Attention和MultiHead Attention

Attention机制是一种特征提取框架,在这当中有多种具体的实现方式,在Transformer模型中使用的是self-attention和Multi-Head Attention这2种方法,下面具体介绍这2种Attention机制。

self-Attention

self-Attention的基本原理可以用下图所示
在这里插入图片描述
从原理图中可以Q,K,V都是源自输入的X,这也就是为什么叫做self-attention的原因,用数学关系式可以表示为
在这里插入图片描述
WQ,WK,WV是权重矩阵,这3个矩阵是可学习的,也就是需要通过训练过程不断更新。得到Q,K,V之后然后再输入到注意力机制中进行计算。
在这里Q和K的相似性度量采用的是点积的方式进行度量的,度量的结果再经过缩放后,然后通过softmax进行归一化,归一化的结果再与V相乘,得到最后的输送,总结起来,在self-attention中attention机制具体的实现方式如下式所示
在这里插入图片描述

Multi-Head Attention

Transformer不是使用了单一的注意机制,而是self-Attention和Multi-Head Attention相耦合的注意力机制,要想彻底理解Transformer中的注意力机制,还需要理解Multi-Head Attention。Multi-Head Attention的原理如下图所示:
在这里插入图片描述
Multi-Head Attention注意力的一种扩展,列投影到多个不同的子空间中,并在每个子空间中独立地执行自注意力计算。这样做的目的是让模型能够从不同的表示子空间中捕捉信息,增强模型的表达能力。多头注意力的计算过程可以表示为:
在这里插入图片描述
其中,每个head的计算为:
在这里插入图片描述
更具体的就是将Query、Key和Value按照张量的方式进行拆分,这就叫做split,然后将分拆的Queryi、Keyi和Valuei分别按照具体的注意力计算方式进行,然后再将每个注意力的计算结果进行合并,这就叫做contact作为最后的计算结果。注意这里是合并,不是求和。总结Multi-Head Attention两个核心处理措施分别为splite和contact,split和contact的实质就是张量的维数变换,如下图所示:
在这里插入图片描述
在这个图中,split后的结果张量为3阶,但实际Multi-Head Attention中的split后的结果张量为4阶,因为4阶张量无法使用几何图形表示,所以这里采用的3阶进行说明。

Transformer中的注意力机制

Transformer中的注意力机制是self-Attention和Multi-Head Attention有机结合的结果,其完整的注意力实现方式如下图所示:
在这里插入图片描述
在Transformer中,以Multi-Head Attention作为注意力的主框架,而每个分头注意力的计算采用的是self-Attention。更具体的是将Q,K和V进行split后,每个对应的Qi和Ki按照QiKiT进行相似性度量,得到scorei,然后所有的scorei统一进行softmax,然后再分别与Vi进行相乘,最后将给自的结果进行contact,作为最后的结果。

3. Transformer中注意力实现的实例

为了更好的说明Transformer中如何实现注意力,这里继续使用这一系列文章中第一篇中的NLP实例,这一实例是来源于github。在这里只截取与注意力相关代码。

实例代码

class ScaledDotProductAttention(tf.keras.layers.Layer):  #(class-MultiHeadAttention)
    def __init__(self, d_h):
        super(ScaledDotProductAttention, self).__init__()
        self.d_h = d_h

    def call(self, query, key, value, mask=None):
        matmul_q_and_transposed_k = tf.matmul(query, key, transpose_b=True)
        scale = tf.sqrt(tf.cast(self.d_h, dtype=tf.float32))
        scaled_attention_score = matmul_q_and_transposed_k / scale
        if mask is not None:
            scaled_attention_score += (mask * -1e9)

        attention_weight = tf.nn.softmax(scaled_attention_score, axis=-1)

        return tf.matmul(attention_weight, value), attention_weight

class MultiHeadAttention(tf.keras.layers.Layer):  #(class-EncoderLayer,class-DecoderLayer)
    def __init__(self, attention_head_count, d_model):
        super(MultiHeadAttention, self).__init__()

        # model hyper parameter variables
        self.attention_head_count = attention_head_count
        self.d_model = d_model

        if d_model % attention_head_count != 0:
            raise ValueError(
                "d_model({}) % attention_head_count({}) is not zero.d_model must be multiple of attention_head_count.".format(
                    d_model, attention_head_count
                )
            )

        self.d_h = d_model // attention_head_count

        self.w_query = tf.keras.layers.Dense(d_model)
        self.w_key = tf.keras.layers.Dense(d_model)
        self.w_value = tf.keras.layers.Dense(d_model)

        self.scaled_dot_product = ScaledDotProductAttention(self.d_h)

        self.ff = tf.keras.layers.Dense(d_model)

    def call(self, query, key, value, mask=None):
        batch_size = tf.shape(query)[0]

        query = self.w_query(query)
        key = self.w_key(key)
        value = self.w_value(value)

        query = self.split_head(query, batch_size)
        key = self.split_head(key, batch_size)
        value = self.split_head(value, batch_size)

        output, attention = self.scaled_dot_product(query, key, value, mask)
        output = self.concat_head(output, batch_size)

        return self.ff(output), attention

    def split_head(self, tensor, batch_size):
        # inputs tensor: (batch_size, seq_len, d_model)
        return tf.transpose(
            tf.reshape(
                tensor,
                (batch_size, -1, self.attention_head_count, self.d_h)
                # tensor: (batch_size, seq_len_splited, attention_head_count, d_h)
            ),
            [0, 2, 1, 3]
            # tensor: (batch_size, attention_head_count, seq_len_splited, d_h)
        )

    def concat_head(self, tensor, batch_size):
        return tf.reshape(
            tf.transpose(tensor, [0, 2, 1, 3]),
            (batch_size, -1, self.attention_head_count * self.d_h)
        )

代码解释

(1) split操作的实现
在这里插入图片描述
(2) self-Attention的实现
在这里插入图片描述
(3) contact的实现
在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到