NLP学习笔记09:注意力机制——从 Self-Attention 到 Transformer

张开发
2026/4/19 3:16:21 15 分钟阅读

分享文章

NLP学习笔记09:注意力机制——从 Self-Attention 到 Transformer
NLP学习笔记09注意力机制——从 Self-Attention 到 Transformer作者Ye Shun日期2026-04-18一、前言注意力机制Attention Mechanism是现代深度学习尤其是自然语言处理中的核心技术之一。它的灵感来自人类的认知过程当我们阅读一句话时并不会平均地处理每一个词而是会把更多注意力放在当前最相关的部分。例如在阅读句子猫坐在垫子上因为它很舒服。理解“它”指代什么时人不会机械地平均看待所有词而会自动把注意力集中到与“它”最相关的候选对象上。注意力机制希望让模型也具备这种“动态聚焦”的能力。它的核心思想可以概括为一句话根据输入中不同部分对当前任务的重要性动态分配不同权重。这套思想已经成为 Transformer、BERT、GPT、T5 等模型的基础。理解注意力机制几乎就是理解现代 NLP 的关键起点。这篇笔记将围绕以下几个问题展开注意力机制到底在解决什么问题Query、Key、Value 分别是什么意思自注意力和多头注意力是怎么工作的注意力机制为什么能推动 Transformer 崛起实践中如何实现和可视化注意力二、为什么需要注意力机制在注意力机制出现之前处理序列数据的主力模型主要是 RNN、LSTM 和 GRU。它们虽然能处理上下文但存在一些明显局限。1. 长距离依赖难题在长序列中前后信息相距很远时RNN 系列模型往往难以稳定捕捉这种依赖。即便 LSTM 和 GRU 已经缓解了问题也并没有彻底解决。2. 顺序计算限制RNN 在时间步之间存在强依赖因此很难像普通矩阵运算那样充分并行。训练长序列时效率容易受到影响。3. 信息压缩瓶颈在早期 Seq2Seq 模型中编码器通常需要把整个源句压缩成一个固定长度向量再交给解码器。这个单一向量很容易丢失细节尤其是在长句子上。4. 注意力给出的改进思路注意力机制的关键改进是不要求把所有信息压缩成一个固定向量在每一步都能动态查看输入中的不同位置根据当前任务重点重新分配权重也就是说模型在做决策时不再只依赖“一个总摘要”而是可以“回头查看原文中最相关的部分”。三、注意力机制的基本概念注意力机制最经典的数学形式是Attention(Q, K, V) softmax(QK^T / √d_k) V这是缩放点积注意力Scaled Dot-Product Attention的表达式。1. Q、K、V 分别是什么这三个量可以理解为QQuery当前要查询什么KKey每个位置提供的“索引”或“匹配依据”VValue每个位置真正携带的信息可以把它类比成数据库检索Query 是检索请求Key 是检索键Value 是对应的数据内容模型会先用Q和所有K做匹配算出“当前应该关注谁”再用这些权重对所有V加权求和得到最终输出。2. 为什么要除以√d_k如果向量维度很大QK^T的点积值容易变得很大导致softmax之后分布过于尖锐训练不稳定。所以引入缩放项√d_k让数值范围更平衡梯度更稳定。3. softmax 的作用softmax会把匹配分数转换成一组和为 1 的权重表示模型对不同位置的关注比例。这就意味着权重越大说明模型越关注那个位置权重越小说明那个位置对当前计算贡献较少四、自注意力机制Self-Attention自注意力Self-Attention是注意力机制中最关键的一种形式。它的特点是序列中的每个位置都可以和序列中的所有其他位置建立联系。也就是说在自注意力中Query 来自输入序列本身Key 来自输入序列本身Value 也来自输入序列本身1. 工作流程对于一个输入序列中的某个位置自注意力通常按以下步骤工作计算当前词和所有词之间的相关性分数对分数做 softmax得到注意力权重用这些权重对所有 Value 做加权求和得到当前词新的上下文表示2. 一个简化示例importtorchimporttorch.nn.functionalasFdefself_attention(query,key,value):scorestorch.matmul(query,key.transpose(-2,-1))/(query.size(-1)**0.5)weightsF.softmax(scores,dim-1)returntorch.matmul(weights,value)3. 自注意力的优势全局上下文感知每个位置都能直接访问整个序列中的所有位置而不像 RNN 那样必须逐步传递信息。更适合并行计算自注意力可以把一个序列中所有位置的相关性计算写成矩阵乘法因此更适合现代 GPU 并行计算。需要特别说明的是它“更容易并行”但不代表它在序列长度上的复杂度更低标准自注意力在序列长度为n时时间和内存开销通常是O(n^2)。这是它和 RNN 很不一样、也很重要的一个点。更强的长距离建模能力两个相距很远的词之间也可以通过一次注意力计算直接建立联系而不需要跨越很多时间步传递。4. 自注意力的一个限制自注意力本身并不天然编码位置信息。换句话说如果不额外加入位置编码它并不知道“哪个词在前哪个词在后”。所以在 Transformer 中自注意力通常要配合位置编码Positional Encoding一起使用。五、多头注意力Multi-Head Attention单头注意力虽然已经很强但模型在一次注意力计算中可能只学到某一种关系模式。多头注意力Multi-Head Attention就是为了解决这个问题。它的核心思想是把注意力机制并行做很多次让不同的头去关注不同类型的关系。1. 多头注意力的结构多头注意力通常包括多组独立的Q、K、V线性变换多个并行注意力头拼接多个头的输出再通过一个线性层映射回原维度2. 为什么多头更强不同注意力头可能学习到不同模式例如一个头关注主谓关系一个头关注长距离依赖一个头关注局部词组搭配一个头关注指代消解这会显著增强模型的表达能力。3. 多头注意力示例importtorchimporttorch.nnasnnimporttorch.nn.functionalasFclassMultiHeadAttention(nn.Module):def__init__(self,d_model,num_heads):super().__init__()self.d_modeld_model self.num_headsnum_heads self.d_kd_model//num_heads self.W_qnn.Linear(d_model,d_model)self.W_knn.Linear(d_model,d_model)self.W_vnn.Linear(d_model,d_model)self.W_onn.Linear(d_model,d_model)defforward(self,query,key,value):batch_sizequery.size(0)Qself.W_q(query).view(batch_size,-1,self.num_heads,self.d_k).transpose(1,2)Kself.W_k(key).view(batch_size,-1,self.num_heads,self.d_k).transpose(1,2)Vself.W_v(value).view(batch_size,-1,self.num_heads,self.d_k).transpose(1,2)scorestorch.matmul(Q,K.transpose(-2,-1))/(self.d_k**0.5)weightsF.softmax(scores,dim-1)outputtorch.matmul(weights,V)outputoutput.transpose(1,2).contiguous().view(batch_size,-1,self.d_model)returnself.W_o(output)六、注意力机制在 NLP 中的应用注意力机制已经成为现代 NLP 系统的核心组件尤其在 Transformer 架构中几乎无处不在。1. 机器翻译在经典的 Seq2Seq with Attention 中解码器在生成每个目标词时不再只依赖固定长度的编码向量而是动态关注源句中最相关的部分。这显著改善了长句翻译质量。2. 文本摘要摘要模型需要识别原文中的关键信息。注意力机制可以帮助模型聚焦核心句子或核心片段提高摘要生成质量。3. 问答系统问答任务中经常需要计算“问题”和“文档”之间的对应关系。交叉注意力Cross-Attention可以帮助模型找到最相关的证据片段。4. 语言模型GPT 系列模型使用掩码自注意力Masked Self-Attention保证每个位置只能看到它前面的内容从而实现自回归生成。5. 表示学习模型BERT 使用双向自注意力可以同时利用左右上下文因此在文本理解任务中表现非常强。七、BERT 中的注意力BERT 是注意力机制应用得最典型的模型之一。它本质上是由多层 Transformer Encoder 堆叠而成。1. BERT 的关键特点使用双向自注意力每层都有多头注意力通过多层堆叠不断增强上下文表示2. 如何获取注意力权重在 Hugging Face 中如果想拿到 BERT 的注意力权重一般要显式设置output_attentionsTruefromtransformersimportBertModel,BertTokenizer tokenizerBertTokenizer.from_pretrained(bert-base-uncased)modelBertModel.from_pretrained(bert-base-uncased)inputstokenizer(Hello, my dog is cute,return_tensorspt)outputsmodel(**inputs,output_attentionsTrue)attentionoutputs.attentions这样outputs.attentions中才会包含各层、各头的注意力权重。八、注意力机制的常见变体随着序列越来越长、任务越来越复杂研究者提出了很多注意力机制的变体。1. 缩放点积注意力这是 Transformer 中最经典的版本使用点积计算相似度用√d_k缩放计算效率高2. 加法注意力Additive Attention这是更早期的一种注意力形式常见于 Bahdanau Attention。它通过一个小型前馈网络来计算 Query 和 Key 的兼容性。相比点积注意力计算更灵活在低维场景下有时更稳定但效率通常不如点积注意力3. 局部注意力Local Attention局部注意力只关注输入的一部分而不是整段序列。这样可以显著减少计算成本。4. 稀疏注意力Sparse Attention稀疏注意力不会让每个位置都和所有位置建立连接而是只计算部分位置之间的注意力。例如 Longformer 中的滑动窗口注意力。这类方法的目标都是一样的在尽量保留建模能力的同时降低标准自注意力的二次复杂度开销。九、实践练习建议练习1实现基础注意力机制可以先写一个最简单的 attention 模块输入一组隐藏状态输出上下文向量和注意力权重importtorchimporttorch.nnasnnimporttorch.nn.functionalasFclassSimpleAttention(nn.Module):def__init__(self,hidden_size):super().__init__()self.attentionnn.Linear(hidden_size,1)defforward(self,encoder_outputs):attention_scoresself.attention(encoder_outputs).squeeze(2)attention_weightsF.softmax(attention_scores,dim1)context_vectortorch.bmm(attention_weights.unsqueeze(1),encoder_outputs)returncontext_vector.squeeze(1),attention_weights这个练习很适合帮助理解注意力分数是怎么来的权重是如何归一化的上下文向量是如何加权求和得到的练习2可视化注意力权重如果把注意力权重画成热力图会更直观地看到模型到底在关注什么。importmatplotlib.pyplotaspltimportseabornassnsdefplot_attention(attention_weights,source_tokens,target_tokens):plt.figure(figsize(10,8))sns.heatmap(attention_weights,xticklabelssource_tokens,yticklabelstarget_tokens,cmapYlGnBu)plt.xlabel(Source Tokens)plt.ylabel(Target Tokens)plt.title(Attention Weights Visualization)plt.show()这个练习对理解机器翻译和 Transformer 非常有帮助。十、学习建议如果你想真正把注意力机制学透我建议按下面的顺序来先理解最基本的加权求和思想再理解 Query、Key、Value 的角色分工然后理解自注意力和多头注意力接着理解注意力如何进入 Transformer最后再去看 BERT、GPT、T5 等大模型这样更容易把“公式、结构和应用”串起来。十一、总结注意力机制的本质是让模型在处理输入时不必平均看待所有信息而是能够根据当前任务动态聚焦最相关的部分。从方法角度看注意力机制解决了固定向量瓶颈问题自注意力让序列中任意位置都能直接交互多头注意力让模型可以并行学习多种关系模式从应用角度看它推动了机器翻译、问答、摘要、语言模型等任务的发展它也是 Transformer、BERT、GPT 等现代模型的核心基础理解注意力机制之后再去看 Transformer就会更容易明白为什么现代 NLP 会从 RNN 时代走向自注意力时代。

更多文章