热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【自然语言处理】聊聊注意力机制(AttentionMechanism)的发展

前言其实,关于注意力机制的文章真的有很多,而且写得相当精彩(毕竟过去这么多年了),这篇博文的主要目的在于以一个不同的角度(理论+代码)阐述注意力机制。浅谈首先这件事还要从序列到序列

前言

其实,关于注意力机制的文章真的有很多,而且写得相当精彩(毕竟过去这么多年了),这篇博文的主要目的在于以一个不同的角度(理论+代码)阐述注意力机制。

浅谈

首先这件事还要从序列到序列模型(Seq2seq Model)开始说起,最早的序列到序列模型是一个CNN+LSTM。
简单来说就是把CNN把编码端映射成一个固定向量,然后用LSTM一步步解码。
接着一个自然的想法是使用LSTM[1],因为LSTM的处理序列能力更强嘛。那么一种简单的做法是将编码端的最后一个隐层状态作为解码端的初始状态。
《【自然语言处理】聊聊注意力机制(Attention Mechanism)的发展》
那么很明显最后一个隐层的状态(固定长度的向量)随着句子长度正常携带的信息能力会明显不足。
为了解决这个问题,引入注意力机制,在解码端的每一步都得到一个创建一个 上 下 文 向 量 \color{red}{上下文向量} (Context Vector),通过上下文向量和解码端的输入生成下一个词。
而Transformer的自注意力机制(Self-Attention)就是将注意力机制从编码端到解码端抽取出来,使其可以捕获句子间的关系,实验证明了这种效果确实比LSTM好太多。
如果你只是单纯想了解下Attention,那么浅谈看看就够了,如果想深入,请往下继续看。(前方高能)

深入了解

Bahdanau Attention

第一个注意力机制,称为Bahdanau Attention[2]。
记编码器(Encoder)的隐含状态为 h h h,记解码器(Decoder)的隐含状态为 s s s

《【自然语言处理】聊聊注意力机制(Attention Mechanism)的发展》

p ( y i ∣ y 1 , . . . , y i − 1 , x ) = g ( y i − 1 , s i , c i ) p(y_i|y_1,…,y_{i-1},x)=g(y_{i-1},s_i,c_i) p(yiy1,...,yi1,x)=g(yi1,si,ci)

首先解码器当前词
y i y_i yi的输出根据上一个词
y i 1 y_{i_1} yi1,上一个解码器的隐含状态为
s i s_i si以及上下文向量
c i c_i ci得到。

s i = f ( s i − 1 , y i − 1 , c i ) s_i=f(s_{i-1},y_{i-1},c_i) si=f(si1,yi1,ci)

那么,解码器当前隐含层状态
s i s_i si则跟上一个隐含层状态
s i − 1 s_{i-1} si1,上一个输出
y i − 1 y_{i-1} yi1和当前的上下文向量
c i c_i ci

c i = ∑ j = 1 T x a i j h j c_i=\sum\limits_{j=1}^{T_x} {a_{ij}h_j} ci=j=1Txaijhj

当前的上下文向量
c i c_i ci是加权求和,注意到
T x T_x Tx是编码器层的步长,而
a i j a_{ij} aij表示这个编码器的隐含层构成上下文向量的一个权重。

a i j = e x p ( e i j ) ∑ k = 1 T x e x p ( e i k ) a_{ij}=\frac{exp(e_{ij})}{\sum\limits_{k=1}^{T_x}exp(e_{ik})} aij=k=1Txexp(eik)exp(eij)

那么
a i j a_{ij} aij则是通过
e i j e_{ij} eij的softmax得到。

以及
e i j = a ( s i − 1 , h j ) e_{ij}=a(s_{i-1},h_j) eij=a(si1,hj)

这里的
e i j e_{ij} eij可以意味着解码器的前一个隐层状态
s i − 1 s_{i-1} si1和编码器的某一个隐层状态
h j h_j hj的一个相似度,这里是用的一个前馈网络得到。

memory是encoder_outputs,也就是编码器的所有隐层状态,query。

来瞅瞅Tensorflow的代码。
BahdanauAttention代码

score = _bahdanau_score(
processed_query,
self._keys,
attention_v,
attention_g=attention_g,
attention_b=attention_b)
alignments = self._probability_fn(score, state)
next_state = alignments

这里的score就是 a j a_j aj a j a_j aj的大小是一个编码器层步长 T x T_x Tx的向量。

def _bahdanau_score(processed_query,
keys,
attention_v,
attention_g=None,
attention_b=None):
"""Implements Bahdanau-style (additive) scoring function.
This attention has two forms. The first is Bhandanau attention,
as described in:
Dzmitry Bahdanau, Kyunghyun Cho, Yoshua Bengio.
"Neural Machine Translation by Jointly Learning to Align and Translate."
ICLR 2015. https://arxiv.org/abs/1409.0473
The second is the normalized form. This form is inspired by the
weight normalization article:
Tim Salimans, Diederik P. Kingma.
"Weight Normalization: A Simple Reparameterization to Accelerate
Training of Deep Neural Networks."
https://arxiv.org/abs/1602.07868
To enable the second form, set please pass in attention_g and attention_b.
Args:
processed_query: Tensor, shape `[batch_size, num_units]` to compare to keys.
keys: Processed memory, shape `[batch_size, max_time, num_units]`.
attention_v: Tensor, shape `[num_units]`.
attention_g: Optional scalar tensor for normalization.
attention_b: Optional tensor with shape `[num_units]` for normalization.
Returns:
A `[batch_size, max_time]` tensor of unnormalized score values.
"""
# Reshape from [batch_size, ...] to [batch_size, 1, ...] for broadcasting.
processed_query = array_ops.expand_dims(processed_query, 1)
if attention_g is not None and attention_b is not None:
normed_v = attention_g * attention_v * math_ops.rsqrt(
math_ops.reduce_sum(math_ops.square(attention_v)))
return math_ops.reduce_sum(
normed_v * math_ops.tanh(keys + processed_query + attention_b), [2])
else:
return math_ops.reduce_sum(
attention_v * math_ops.tanh(keys + processed_query), [2])

而这个分数的计算则是通过processed_query(编码器所有隐层状态, T x T_x Tx个),keys(编码器当前的隐层状态,1个),论文提到是一个前馈网络,所以这里使用tanh激活函数。

Loung Attention

第二个注意力机制,是继Bahdanau Attention后的一个注意力机制叫Loung Attention[4]。
记编码器(Encoder)的隐含状态为 h t h_t ht,记解码器(Decoder)的隐含状态为 h s h_s hs。(请注意,跟Bahdanau Attention不一样了)

《【自然语言处理】聊聊注意力机制(Attention Mechanism)的发展》

首先说说他们的区别。

第一,Loung Attention提供了两种的注意力机制,一个是全局注意力机制(Global Attention),考虑全部的编码器的隐含状态;一个是局部注意力机制(Local Attention),只考虑局部窗口的隐含状态,这样可以节省计算量。

第二,全局注意力机制和Bahdanau Attention相似,但是还是有些区别,一个是Bahdanau Attention的编码器隐含层是由一个双向LSTM然后拼接而成,
h i = [ h → i ; h ← j ] h_i=[\overrightarrow h_i; \overleftarrow h_j] hi=[h i;h j],而Loung Attention仅仅是用了最顶层的隐含层。二个,两者计算解码器隐含层状态不同。Bahdanau Attention是
h t − 1 → a t → c t → h t h_{t-1} \to a_t \to c_t \to h_t ht1atctht,Loung Attention是
h t → a t → c t → h ~ t h_{t} \to a_t \to c_t \to \tilde h_t htatcth~t,简单来讲,Loung Attention像Seq2seq一样先计算出解码层的隐含状态
h t h_t ht,再结合上下文向量
c t c_t ct得出最终的解码器隐含状态(后文我把它称为解码器顶层隐含状态)
h ~ t \tilde h_t h~t

p ( y t ∣ y p(yty<t,x)=softmax(Wsh~t)
我们可以看到生成下一个词的主要是依靠这个当前的顶层解码器隐层状态 h ~ t \tilde h_t h~t
h ~ t = t a n h ( W c [ c t ; h t ] ) \tilde h_t = tanh(W_c[c_t;h_t]) h~t=tanh(Wc[ct;ht])
而这个当前的顶层解码器隐层状态则是根据当前上下文向量 c t c_t ct和解码器隐含层状态 h t h_t ht得到。
A t ( s ) = a l i g n ( h t , h s ) = e x p ( s c o r e ( h t , h ˉ s ) ) ∑ s ’ e x p ( s c o r e ( h t , h ˉ s ) \begin{array}{c} A_t(s) = align(h_t,h_s)\\ = \frac {exp(score(h_t,\bar h_s))}{\sum\nolimits_{s^’} {exp(score(h_t,\bar h_s)}} \end{array} At(s)=align(ht,hs)=sexp(score(ht,hˉs)exp(score(ht,hˉs))
其实 c t c_t ct的创建跟Bahdanau Attention类似,主要是计算 a i j a_{ij} aij的方式有些不同。并且Loung Attention提供了三种计算分数(score)的方式。

LuongAttention代码

with variable_scope.variable_scope(None, "luong_attention", [query]):
attention_g = None
if self._scale:
attention_g = variable_scope.get_variable(
"attention_g",
dtype=query.dtype,
initializer=init_ops.ones_initializer,
shape=())
score = _luong_score(query, self._keys, attention_g)
alignments = self._probability_fn(score, state)
next_state = alignments

def _luong_score(query, keys, scale):
"""Implements Luong-style (multiplicative) scoring function.
This attention has two forms. The first is standard Luong attention,
as described in:
Minh-Thang Luong, Hieu Pham, Christopher D. Manning.
"Effective Approaches to Attention-based Neural Machine Translation."
EMNLP 2015. https://arxiv.org/abs/1508.04025
The second is the scaled form inspired partly by the normalized form of
Bahdanau attention.
To enable the second form, call this function with `scale=True`.
Args:
query: Tensor, shape `[batch_size, num_units]` to compare to keys.
keys: Processed memory, shape `[batch_size, max_time, num_units]`.
scale: the optional tensor to scale the attention score.
Returns:
A `[batch_size, max_time]` tensor of unnormalized score values.
Raises:
ValueError: If `key` and `query` depths do not match.
"""
depth = query.get_shape()[-1]
key_units = keys.get_shape()[-1]
if depth != key_units:
raise ValueError(
"Incompatible or unknown inner dimensions between query and keys. "
"Query (%s) has units: %s. Keys (%s) have units: %s. "
"Perhaps you need to set num_units to the keys' dimension (%s)?" %
(query, depth, keys, key_units, key_units))
# Reshape from [batch_size, depth] to [batch_size, 1, depth]
# for matmul.
query = array_ops.expand_dims(query, 1)
# Inner product along the query units dimension.
# matmul shapes: query is [batch_size, 1, depth] and
# keys is [batch_size, max_time, depth].
# the inner product is asked to **transpose keys' inner shape** to get a
# batched matmul on:
# [batch_size, 1, depth] . [batch_size, depth, max_time]
# resulting in an output shape of:
# [batch_size, 1, max_time].
# we then squeeze out the center singleton dimension.
score = math_ops.matmul(query, keys, transpose_b=True)
score = array_ops.squeeze(score, [1])
if scale is not None:
score = scale * score
return score

主要看这句代码。

score = math_ops.matmul(query, keys, transpose_b=True)

这里的分数就是内积,也就是Attention分数计算方式之一。

Self-Attention

自注意力机制是Transformer[4]提出的。以上在代码里面query,key,value都是这篇文章提出的一些概念,就是这篇文章把注意力机制给抽取出来,把它变得更通用、一般化了。
可以注意每一个Transformer块(Block)都有一个Add&Norm,这里面用到了残差网络(Residual Network)和批量归一化(Batch Normalization)。批量归一化就是将输入 X X X进行归一化,使得输入数据能够服从同一分布。
一般的来讲key和value代表着之前编码器端的隐含状态,query指代解码器端的隐含状态。那么注意力机制就是用key跟query计算得到一个权重矩阵(之前的score)然后跟value进行加权求出一个上下文向量。

《【自然语言处理】聊聊注意力机制(Attention Mechanism)的发展》

A t t e n t i o n ( Q , K , V ) = s o f t m a x ( Q K T d k ) V Attention(Q,K,V)=softmax(\frac{QK^T}{\sqrt d_k})V Attention(Q,K,V)=softmax(d kQKT)V

主要看下Attention的计算,其实这里会发现这个跟之前的Attention计算方式非常相似,
Q K T QK^T QKT的结果就类似一个score,但是这里多了一个缩放因子
d k \sqrt d_k d k,文章提到是用来缩放的,避免score太大,会把概率分化接近成1和0。

这里的Transformer的计算不过多阐述,具体可以看
图解Transformer。

总结

目前好多论文似乎都要&#8221;注意力机制&#8221;一下,不过也间接地证明了注意力机制的有效性。其实目前的一些ELMO把词向量从静态走向动态,其中使用了加权得到的词向量的思想跟注意力机制非常相似,所以了解注意力机制的本质对后面的论文的帮助还是非常大的。

参考文献

【1】Sutskever I, Vinyals O, Le Q V. Sequence to sequence learning with neural networks[C]//Advances in neural information processing systems. 2014: 3104-3112.
【2】Bahdanau D, Cho K, Bengio Y. Neural machine translation by jointly learning to align and translate[J]. arXiv preprint arXiv:1409.0473, 2014.
【3】Luong M T, Pham H, Manning C D. Effective approaches to attention-based neural machine translation[J]. arXiv preprint arXiv:1508.04025, 2015.
【4】Vaswani A, Shazeer N, Parmar N, et al. Attention is all you need[C]//Advances in neural information processing systems. 2017: 5998-6008.


推荐阅读
  • 在稀疏直接法视觉里程计中,通过优化特征点并采用基于光度误差最小化的灰度图像线性插值技术,提高了定位精度。该方法通过对空间点的非齐次和齐次表示进行处理,利用RGB-D传感器获取的3D坐标信息,在两帧图像之间实现精确匹配,有效减少了光度误差,提升了系统的鲁棒性和稳定性。 ... [详细]
  • 独家解析:深度学习泛化理论的破解之道与应用前景
    本文深入探讨了深度学习泛化理论的关键问题,通过分析现有研究和实践经验,揭示了泛化性能背后的核心机制。文章详细解析了泛化能力的影响因素,并提出了改进模型泛化性能的有效策略。此外,还展望了这些理论在实际应用中的广阔前景,为未来的研究和开发提供了宝贵的参考。 ... [详细]
  • POJ 2482 星空中的星星:利用线段树与扫描线算法解决
    在《POJ 2482 星空中的星星》问题中,通过运用线段树和扫描线算法,可以高效地解决星星在窗口内的计数问题。该方法不仅能够快速处理大规模数据,还能确保时间复杂度的最优性,适用于各种复杂的星空模拟场景。 ... [详细]
  • 通过使用CIFAR-10数据集,本文详细介绍了如何快速掌握Mixup数据增强技术,并展示了该方法在图像分类任务中的显著效果。实验结果表明,Mixup能够有效提高模型的泛化能力和分类精度,为图像识别领域的研究提供了有价值的参考。 ... [详细]
  • 在《Python编程基础》课程中,我们将深入探讨Python中的循环结构。通过详细解析for循环和while循环的语法与应用场景,帮助初学者掌握循环控制语句的核心概念和实际应用技巧。此外,还将介绍如何利用循环结构解决复杂问题,提高编程效率和代码可读性。 ... [详细]
  • 从2019年AI顶级会议最佳论文,探索深度学习的理论根基与前沿进展 ... [详细]
  • 点互信息在自然语言处理中的应用与优化
    点互信息(Pointwise Mutual Information, PMI)是一种用于评估两个事件之间关联强度的统计量,在自然语言处理领域具有广泛应用。本文探讨了 PMI 在词共现分析、语义关系提取和情感分析等任务中的具体应用,并提出了几种优化方法,以提高其在大规模数据集上的计算效率和准确性。通过实验验证,这些优化策略显著提升了模型的性能。 ... [详细]
  • 本文详细探讨了OpenCV中人脸检测算法的实现原理与代码结构。通过分析核心函数和关键步骤,揭示了OpenCV如何高效地进行人脸检测。文章不仅提供了代码示例,还深入解释了算法背后的数学模型和优化技巧,为开发者提供了全面的理解和实用的参考。 ... [详细]
  • 浅层神经网络解析:本文详细探讨了两层神经网络(即一个输入层、一个隐藏层和一个输出层)的结构与工作原理。通过吴恩达教授的课程,读者将深入了解浅层神经网络的基本概念、参数初始化方法以及前向传播和反向传播的具体实现步骤。此外,文章还介绍了如何利用这些基础知识解决实际问题,并提供了丰富的实例和代码示例。 ... [详细]
  • 在Python编程中,掌握高级技巧对于提升代码效率和可读性至关重要。本文重点探讨了生成器和迭代器的应用,这两种工具不仅能够优化内存使用,还能简化复杂数据处理流程。生成器通过按需生成数据,避免了大量数据加载对内存的占用,而迭代器则提供了一种优雅的方式来遍历集合对象。此外,文章还深入解析了这些高级特性的实际应用场景,帮助读者更好地理解和运用这些技术。 ... [详细]
  • 2019年斯坦福大学CS224n课程笔记:深度学习在自然语言处理中的应用——Word2Vec与GloVe模型解析
    本文详细解析了2019年斯坦福大学CS224n课程中关于深度学习在自然语言处理(NLP)领域的应用,重点探讨了Word2Vec和GloVe两种词嵌入模型的原理与实现方法。通过具体案例分析,深入阐述了这两种模型在提升NLP任务性能方面的优势与应用场景。 ... [详细]
  • 表面缺陷检测数据集综述及GitHub开源项目推荐
    本文综述了表面缺陷检测领域的数据集,并推荐了多个GitHub上的开源项目。通过对现有文献和数据集的系统整理,为研究人员提供了全面的资源参考,有助于推动该领域的发展和技术进步。 ... [详细]
  • 图像拼接技术深入解析:基于OpenCV 3.4的Stitching模块源码分析(下篇)
    本文继续深入探讨图像拼接技术,特别是在OpenCV 3.4的Stitching模块中的源码实现。通过与VLFeat的SIFT实现进行对比,详细分析了OpenCV在图像特征提取、匹配及拼接过程中的关键算法和技术细节,为读者提供了全面的技术解析和实践指导。 ... [详细]
  • 本文介绍了如何利用Struts1框架构建一个简易的四则运算计算器。通过采用DispatchAction来处理不同类型的计算请求,并使用动态Form来优化开发流程,确保代码的简洁性和可维护性。同时,系统提供了用户友好的错误提示,以增强用户体验。 ... [详细]
  • 在第二课中,我们将深入探讨Scala的面向对象编程核心概念及其在Spark源码中的应用。首先,通过详细的实战案例,全面解析Scala中的类和对象。作为一门纯面向对象的语言,Scala的类设计和对象使用是理解其面向对象特性的关键。此外,我们还将介绍如何通过阅读Spark源码来进一步巩固对这些概念的理解。这不仅有助于提升编程技能,还能为后续的高级应用开发打下坚实的基础。 ... [详细]
author-avatar
紫逸石
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有