LLM模型开发教程(九)注意力机制解读(一文拿下)
注意力机制
什么是注意力机制
-
原理:在处理某个词时,模型动态计算它与输入中所有词的相关性(注意力权重),加权融合信息。
-
案例:句子 “The animal didn’t cross the street because it was too tired.”
- 当处理代词 “it” 时,模型通过注意力机制发现:
- “it” 与 “animal” 的关联权重高
- 与 “street” 的权重低
- 于是正确理解 “it” 指的是 “animal”,而非 “street”
- 当处理代词 “it” 时,模型通过注意力机制发现:
-
对比 RNN:RNN 需从头一步步传递“animal”的信息到“it”,中间经过多个词,信息易衰减;而注意力机制一步直达关键位置。
核心点:
注意力 = 动态聚焦 + 直接连接,让模型精准抓取上下文中最相关的信息。
注意力机制的定义
动态分配权重,聚焦最相关输入
✅ 核心要素提取
| 要素 | 说明 |
|---|---|
| 动态 | 权重不固定,随任务和上下文变化 |
| 分配权重 | 每个输入被赋予一个“重要性分数” |
| 聚焦相关部分 | 输出只关注真正相关的输入信息 |
🌟 类比理解
- 像人阅读时:看到“它”会自动回看前面的主语
- 像查字典时:想找“猫”,就只盯着“cat”那一页
💡 小白口诀:
“哪里重要看哪里,重点内容才输出。”
注意力机制的本质

一句话理解:
- 训练期间模型拿到训练的文本后,把文本根据内置的模型字典映射成token_id集合
- 拿到token_id集合进行embedding空间向量化,在训练期间embedding根据反向传播得到的loss方向不断的在调整token_id的空间坐标。根据预测方向(本质还是loss方向)来调整词性,词义之间的在空间坐标上的距离,在投入生产的前一刻产出一份相对稳定的embedding参照表。
- 即在模型应用过程中本质上是把token_id去embedding参照表找到空间坐标.
- 这样在transformer阶段,初始化时通过找到的向量数据自带的空间坐标的信息来计算出每个词的Q,K,V以此通过初始化注意力机制的算法算法,全量并行计算所有词汇,所有的词汇互相之间会用自己的Q来参与并行矩阵运算(即交换其他词汇的K,V),并以此来进行权重调整,最终来锚定输入的真实语义
为什么要学习注意力机制?
- 基石地位:注意力机制已成为现代深度学习的核心支柱,尤其在自然语言处理(NLP)领域
- 主流模型依赖:GPT 系列、Gemini、LLaMA、BERT 等前沿大模型均基于注意力架构构建
- 理解 AI 前沿的关键:不掌握注意力机制,就无法深入理解当前最先进的 AI 技术原理与能力
- 本质作用:让模型学会“聚焦”——在处理文本时动态关注最相关的信息,而非机械地顺序处理
一句话总结:注意力机制是通往现代 AI 的“钥匙”,它是实现上下文理解、长距离依赖建模和高效信息提取的核心技术。
注意力机制解决了什么问题?
-
打破串行处理限制
- 传统模型(如 RNN)按顺序逐字处理,难以高效捕捉长距离依赖
- 注意力机制允许模型并行处理所有输入,直接建立任意位置之间的关联
-
模拟人类认知方式
- 人类阅读时不会平均关注每个词,而是根据上下文动态聚焦关键信息
- 注意力机制模仿这一过程:在不同任务中,动态调整对哪些词“更关注”
-
实现灵活的信息权重分配
- 每个词在当前语境中的重要性被量化为“注意力权重”
- 例如:在句子 “猫坐在椅子上” 中,“猫” 和 “椅子” 可能比 “坐” 更受关注
核心价值:
从“机械式顺序处理” → “智能式重点聚焦”,让模型真正具备理解上下文的能力。
历史背景
2017年以前:序列数据处理的主流模型是 RNN
- 背景:在 2017 年之前,处理自然语言文本、时间序列信号、音频等序列数据的主要方法是 循环神经网络(RNN)
- 代表变体:
- LSTM(Long Short-Term Memory)长短期记忆
→ 解决传统 RNN 的梯度消失问题,能更好记住长期依赖信息 - GRU(Gated Recurrent Unit)门循环单元
→ 结构更简洁,性能接近 LSTM,训练更快
- LSTM(Long Short-Term Memory)长短期记忆
特点总结:
- 按顺序逐个处理输入(串行处理)
- 每一步只看当前输入和上一步状态
- 能捕捉时间/位置上的依赖关系
- 但存在训练慢、长距离依赖建模困难等问题
历史意义:RNN 及其改进版(LSTM/GRU)曾是 NLP 领域的“黄金标准”,直到 2017 年 Transformer 出现并引入注意力机制,才被逐步取代。
RNN/LSTM 的核心思想精炼解析
-
模拟人类阅读过程:
像人一样,逐个处理序列中的元素(如词语),按顺序从左到右进行。 -
维护“记忆”机制:
- 每一步都保留一个“隐藏状态”(Hidden State),相当于模型的“短期记忆”
- 这个状态会根据当前输入和上一时刻的状态进行更新
- 更新后的状态传递给下一步,用于处理下一个词
类比理解:
就像你读一句话时,每看到一个词都会结合前面的内容来理解当前词的意思——这个“上下文理解”的过程,就是 RNN/LSTM 的核心逻辑。
关键优势:
能捕捉序列中的时间依赖关系,适合处理语言、语音等具有顺序性的数据。
局限性:
- 训练速度慢(必须串行处理)
- 难以建模非常长距离的依赖(梯度消失问题)
→ 这些问题促使了注意力机制和 Transformer 的诞生。
RNN/LSTM 的两大挑战详解
- 难以捕捉长距离依赖关系
-
问题描述:
在长文本中,后文的词可能指代前文很早出现的概念(如“它”指代开头的名词)。
RNN 必须通过层层传递隐藏状态来保留信息,但每一步都可能丢失或稀释关键信息。 -
类比理解:
就像“传话游戏”——一句话从第一个人传到最后一个人,中间经过多人转述,内容容易失真甚至完全改变。
→ 长序列中,原始信息在传递过程中逐渐“消失”(梯度消失)或“爆炸”(梯度爆炸),导致模型无法准确理解远距离上下文。 -
后果:
模型难以理解复杂的语义结构,如跨句指代、逻辑推理等。
- 并行计算性差
-
问题描述:
RNN 是顺序处理的:必须先完成第 t 步,才能开始第 t+1 步。
所有步骤只能串行执行,无法同时处理多个时间步。 -
影响:
- 训练速度慢,尤其对长文本效率极低
- 不利于利用现代 GPU 的大规模并行计算能力
- 成为模型扩展和实时应用的瓶颈
总结:
-RNN/LSTM 虽然能建模序列依赖,但在长距离信息传递和计算效率上存在根本性缺陷。
- 这正是为什么注意力机制和 Transformer 架构能够取代它们——它们解决了这两个核心痛点。
注意力与自注意力的区别
| 维度 | Attention | Self-Attention |
|---|---|---|
| Q 来源 | 一个序列 | 同一序列 |
| K / V 来源 | 另一个序列 | 同一序列 |
| 是否跨序列 | 可以 | 不可以 |
| 主要作用 | 跨信息源对齐 | 序列内部建模 |
| Transformer 中的位置 | Encoder–Decoder | Encoder / Decoder 内部 |
注意力机制中核心三要素Q,K,V
计算注意力分数的计算过程
将注意力分数转为权重过程
输出向量计算过程
辅助大白话理解
- QKV的数据来源一致,元数据都是token的向量空间坐标。经过三个不同的可学习的权重矩阵(线性变化)运算所得,各自有功能侧重。
- Q:的重点是查询。
- K:用来被Q匹配计算,
- V用来承载所有token的信息。
- 但存储的都是token的向量空间坐标进过线性变换后的结果,这一点要记住!
- Q与K中的每一项计算得到权重,拿到权重与V中的每一项进行加权求和,然后更新token。
- 此处举个例子,为了直观,这里把投影后的 Q/K/V 简化成与原 embedding 数值一致:“我爱中国”翻译成英文。那么分词形式可能是:我/爱/中国,分词之后拿到了分词的token_id列表,比如[666,777,888],分别是我,爱,中国token_id。token_id经过向量化之后。“我”也就是token_id666用空间坐标[1123,-12,-0.123]表示。"爱"也就是777用空间坐标[-0.1233,-912,-0.123]表示。“中国”也就是888用空间坐标[0.123,0.2341,123]表示。那么在注意力机制中表示"我"的Q是[1123,-12,-0.123],而K中放的是[[1123,-12,-0.123],[-0.1233,-912,-0.123],[0.123,0.2341,123]],而V中也是{[1123,-12,-0.123],[-0.1233,-912,-0.123],[0.123,0.2341,123]},此时Q需要与K中的每一项去进行相似度计算从而得到每个token的权重a。然后拿着权重与所有token的V中的的信息进行加权求和,以此来更新token向量表示。即核心流程是QK计算拿权重->权重与V计算->生成新的token向量表示
自注意力的计算公式

注意力分数的作用
- 总结:Q与K的乘积
- 用于计算 Query 与每个 Key 的匹配程度
- 分数越高 → 相关性越强 → 对应 Value 被赋予更高权重
- 最终输出 = 各 Value 按分数加权求和
✅ 简单说:
注意力分数 = “有多相关” → 决定“该不该重点用”
自注意力机制的工作流
-
投影:
用三个可学习矩阵 $ W_q, W_k, W_v $ ,将输入 $ a^i $ 转为 Q、K、V 向量 -
计算分数:
$ Q \cdot K^T $ → 得到每个输入之间的相关性得分 -
归一化:
对得分执行 softmax → 得到注意力权重 $ \alpha $ (和为 1) -
加权输出:
$ \alpha \cdot V $ → 加权求和,得到最终表示
✅ 核心公式链:
输入 → QKV 投影 → 注意力分数 → softmax → 权重 → 加权 V → 输出
自注意力的矩阵化操作
一、 输入矩阵化

n:token 数量
d_model 维度,一般维度都很大比如512维
二、Q,K,V的产生
Q:
K:
V:

Q、K、V的结果:

三、注意力分数计算

四、最终结果,权重与V加权求和计算

CNN、FC、注意力机制的区别
| 特征 | CNN | FC | 注意力机制 |
|---|---|---|---|
| 处理方式 | 局部区域,共享卷积核 | 全局连接,固定权重 | 动态权重,关注相关输入部分 |
| 权重特点 | 共享,位置无关 | 固定,不随输入变化 | 随 Q-K 交互动态变化 |
| 关注点 | 空间局部特征 | 整体输入,无空间感知 | 内容相关性(如“它”指代谁) |
| 优点 | 图像处理强 | 特征整合好 | 支持长序列、并行计算、灵活聚焦 |
| 典型应用 | 计算机视觉 | 分类任务 | NLP、翻译、文本理解等 |
✅ 关键一句话:
CNN 看局部,FC 看整体,注意力看内容关系。
实现一个注意力机制
"""
自注意力机制(Self-Attention)实现
【是什么】
自注意力机制是Transformer的核心,让模型能够关注序列中不同位置的信息。
核心思想:每个词通过查询(Query)、键(Key)、值(Value)机制,计算与其他所有词的相关性。
【解决什么问题】
1. 传统RNN无法并行计算,注意力机制可以并行处理所有位置
2. 长距离依赖:直接计算任意两个位置的关系,不受距离限制
3. 上下文理解:每个词都能"看到"整个序列的信息
【应用场景】
- GPT、BERT等大语言模型的核心组件
- 机器翻译、文本生成、问答系统
- 图像识别、语音处理等序列任务
【核心公式】
Attention(Q, K, V) = softmax(QK^T / √dk) × V
"""
import torch
import torch.nn.functional as F
# ==================== 第一步:文本预处理 ====================
# 目标:将文本转换为数字ID,方便模型处理
sentence = "this is my cat"
# 分词:将句子拆分成单词列表
tokens = sentence.replace(",", "").split()
print("分词结果:", tokens)
# 排序:为构建词汇表做准备(按字母顺序排序,保证一致性)
sort_token = sorted(tokens)
print("排序后:", sort_token)
# 构建词汇表:为每个词分配唯一ID(0, 1, 2, 3...)
# 这是最简单的tokenizer,实际应用中用BPE、WordPiece等更复杂的方法
word_dict = {}
for i, word in enumerate(sort_token):
word_dict[word] = i
print("词汇表:", word_dict)
# 文本转ID:将原始句子中的词映射为对应的ID
token_ids = []
for token in tokens:
token_ids.append(word_dict[token])
print("词ID序列:", token_ids)
# ==================== 第二步:词嵌入(Word Embedding) ====================
# 目标:将离散的词ID转换为连续的向量表示
# 转为张量:PyTorch需要张量格式
token_ids = torch.tensor(token_ids)
print("token_ids张量:", token_ids)
# 创建嵌入层:将词ID映射为向量
# 参数:vocab_size=词汇表大小, embedding_dim=向量维度
# 这里4个词,每个词用16维向量表示
torch.manual_seed(123) # 固定随机种子,保证结果可复现
embed_layer = torch.nn.Embedding(len(word_dict), 16)
embeddings = embed_layer(token_ids).detach() # detach():不计算梯度,仅用于演示
print("词嵌入向量形状:", embeddings.shape) # (序列长度, 嵌入维度) = (4, 16)
print("词嵌入:", embeddings)
# ==================== 第三步:创建注意力机制的权重矩阵 ====================
# 目标:定义Q、K、V的线性变换矩阵
# 定义输出维度:Q、K、V的维度可以不同,但通常K和Q相同
# 实际应用中:GPT-2用768维,这里用小值便于理解
dq, dk, dv = 24, 24, 28 # Q维度、K维度、V维度
# 获取输入维度:嵌入向量的维度
d = embeddings.shape[1] # d = 16
# 创建权重矩阵:用于将嵌入向量转换为Q、K、V
# W_q: (d, dq) = (16, 24) - 将16维嵌入转为24维Query
# W_k: (d, dk) = (16, 24) - 将16维嵌入转为24维Key
# W_v: (d, dv) = (16, 28) - 将16维嵌入转为28维Value
W_q = torch.nn.Parameter(torch.rand(d, dq))
W_k = torch.nn.Parameter(torch.rand(d, dk))
W_v = torch.nn.Parameter(torch.rand(d, dv))
print("W_q形状:", W_q.shape)
print("W_k形状:", W_k.shape)
print("W_v形状:", W_v.shape)
# ==================== 第四步:计算Q、K、V ====================
# 目标:通过线性变换得到Query、Key、Value矩阵
# Q (Query):查询矩阵,表示"我想找什么"
# K (Key):键矩阵,表示"我有什么"
# V (Value):值矩阵,表示"我的内容是什么"
# 矩阵乘法:embeddings × W = (4, 16) × (16, 24) = (4, 24)
Q = embeddings.matmul(W_q)
print("Q形状:", Q.shape) # (序列长度, dq) = (4, 24)
K = embeddings.matmul(W_k)
print("K形状:", K.shape) # (序列长度, dk) = (4, 24)
V = embeddings.matmul(W_v)
print("V形状:", V.shape) # (序列长度, dv) = (4, 28)
# ==================== 第五步:计算注意力分数 ====================
# 目标:计算每个词与其他所有词的相关性
# 点积注意力:Q × K^T 计算相似度
# Q × K^T: (4, 24) × (24, 4) = (4, 4)
# 结果矩阵scores[i][j]表示第i个词对第j个词的注意力分数
scores = Q.matmul(K.T)
print("注意力分数矩阵形状:", scores.shape) # (4, 4)
print("注意力分数:", scores)
# 缩放:除以√dk,防止点积值过大导致softmax梯度消失
# 这是缩放点积注意力(Scaled Dot-Product Attention)的关键步骤
scores = scores / (dk**0.5)
print("缩放后的分数:", scores)
# ==================== 第六步:Softmax归一化 ====================
# 目标:将注意力分数转换为概率分布(权重)
# Softmax:将分数转换为0-1之间的概率,且每行和为1
# dim=1:对每一行进行softmax,表示每个词对所有词的注意力权重
weights = F.softmax(scores, dim=1)
print("注意力权重形状:", weights.shape) # (4, 4)
print("注意力权重:", weights)
# 权重矩阵含义:weights[i][j] = 第i个词对第j个词的关注程度
# ==================== 第七步:加权求和得到输出 ====================
# 目标:根据注意力权重,对Value进行加权求和
# 加权求和:weights × V = (4, 4) × (4, 28) = (4, 28)
# 每个词的输出 = 所有词的Value的加权平均,权重由注意力分数决定
output = weights.matmul(V)
print("最终输出形状:", output.shape) # (序列长度, dv) = (4, 28)
print("最终输出:", output)
"""
【总结】
1. 输入:词序列 → 词嵌入向量
2. 变换:嵌入向量 → Q、K、V
3. 计算:Q×K^T → 注意力分数 → Softmax → 权重
4. 输出:权重 × V → 注意力输出
【关键理解】
- 注意力机制让每个词都能"关注"到序列中其他所有词
- 权重矩阵告诉我们哪些词之间的关系更重要
- 输出是考虑了全局上下文的词表示
【参数说明】
- d: 嵌入维度(输入维度)
- dq, dk, dv: Q、K、V的输出维度
- 序列长度: 输入词的数量
【用法】
在实际模型中,这些权重矩阵(W_q, W_k, W_v)是可学习的参数,
通过反向传播自动优化,让模型学会关注哪些词之间的关系。
"""
更多推荐

所有评论(0)