惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tenable Blog
MyScale Blog
MyScale Blog
罗磊的独立博客
Hugging Face - Blog
Hugging Face - Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
爱范儿
爱范儿
博客园 - 司徒正美
D
Darknet – Hacking Tools, Hacker News & Cyber Security
量子位
N
News | PayPal Newsroom
S
Secure Thoughts
酷 壳 – CoolShell
酷 壳 – CoolShell
L
LINUX DO - 热门话题
有赞技术团队
有赞技术团队
V
Visual Studio Blog
T
Tailwind CSS Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Project Zero
Project Zero
B
Blog RSS Feed
J
Java Code Geeks
Google Online Security Blog
Google Online Security Blog
Last Week in AI
Last Week in AI
Cyberwarzone
Cyberwarzone
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
小众软件
小众软件
博客园 - 【当耐特】
Latest news
Latest news
T
Threat Research - Cisco Blogs
aimingoo的专栏
aimingoo的专栏
博客园_首页
博客园 - 三生石上(FineUI控件)
Engineering at Meta
Engineering at Meta
D
Docker
Forbes - Security
Forbes - Security
Help Net Security
Help Net Security
Apple Machine Learning Research
Apple Machine Learning Research
P
Proofpoint News Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Simon Willison's Weblog
Simon Willison's Weblog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
V2EX - 技术
V2EX - 技术
N
Netflix TechBlog - Medium
The Last Watchdog
The Last Watchdog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
T
Threatpost
Cloudbric
Cloudbric
T
The Exploit Database - CXSecurity.com
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 叶小钗
Webroot Blog
Webroot Blog

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
transformer系列:#3 深度解析多头注意力
AI-Frontiers · 2026-05-06 · via 博客园_首页


原文:https://mp.weixin.qq.com/s/Cg-BR4oSAvvoZsvQGTfmCQ

欢迎关注公zh: Al-Frontiers

transformer往期文章推荐

收藏! transformer学习资源汇总

transformer进阶之路:#1 整体概述

transformer进阶之路:#2 工作原理详解

这是 Transformer 系列文章的第三篇,沿用前期「自上而下」的方式讨论 Transformer。前两篇里,我们了解了 Transformer 是什么、架构,工作机制。

本篇文章我们将深入探索 「多头注意力」——— Transformer 的大脑。

Transformer 中如何使用注意力

正如我们在第二篇 transformer进阶之路:#2 工作原理详解所讨论的,Transformer 在三个地方用到了注意力:

  1. 编码器中的自注意力:输入序列关注自己

  2. 解码器中的自注意力:目标序列关注自己

  3. 解码器中的编码器-解码器注意力:目标序列关注输入序列

1 Transformer 中如何使用注意力

注意力输入的参数:Query、Key 和 Value

注意力层接收三个参数形式的输入,分别叫做 Query(查询)Key(键)Value(值)

这三个参数的结构很相似:序列里的每个词都由一个向量来表示。

编码器自注意力

输入序列首先被送入输入嵌入和位置编码,该模块为输入序列中的每个词生成一个编码表示,该表示捕捉了每个词的含义和位置。然后,该编码表示被送入第一个编码器中的自注意力机制,该机制接收查询(Query)、键(Key)和值(Value)三个参数。自注意力机制随后也为输入序列中的每个词生成一个编码表示,但这次编码表示中加入了每个词的注意力分数。随着编码结果依次经过堆栈中的所有编码器,每个自注意力模块都会将自身的注意力分数添加到每个词的编码表示中。

2 编码器自注意力

解码器自注意力

在解码器栈中,目标序列首先被送入输出嵌入和位置编码,该过程会为目标序列中的每个词生成一个编码表示,该表示捕捉了每个词的含义和位置。

然后,该编码表示被送入第一个解码器中的自注意力机制,该机制会接收查询(Query)、键(Key)和值(Value)这三个参数。自注意力机制随后也会为目标序列中的每个词生成一个编码表示,该表示还包含了每个词的注意力得分。

经过层归一化之后,这个表示被传给第一个解码器中「编码器-解码器注意力」的 Query 参数。

编码器-解码器注意力

与此同时,编码器堆栈最后一个编码器的输出,被传给编码器-解码器注意力的 Value 和 Key 参数。

这样一来,编码器-解码器注意力同时拿到了两样东西:一个是目标序列的表示(来自解码器自注意力),另一个是输入序列的表示(来自编码器堆栈)。所以,它生成的表示里,既包含了目标序列每个词的注意力分数,也融入了输入序列注意力分数带来的影响。

当这个表示流过解码器堆栈里的所有解码器时,每一个自注意力层和每一个编码器-解码器注意力层,也都会把自己的注意力分数继续加进每个词的表示里。

多个注意力头

在 Transformer 里,注意力模块会把自己的计算并行地重复多次。每一次重复,就叫做一个「注意力头」。注意力模块会把它的 Query、Key 和 Value 参数按 N 份切开,然后把每一份独立地传给一个单独的头。所有这些相似的注意力计算结果,最后会被组合在一起,生成一个最终的注意力分数。这就是「多头注意力」的由来——它给了 Transformer 更强大的能力,去为每个词编码多重关系细微差别
3 多个注意力头

为了准确理解数据在内部是怎么处理的,我们还是以训练一个翻译任务为例,一步步走过注意力模块的工作流程。我们只用一个训练样本:输入序列是英文 "You are welcome",目标序列是西班牙语 "De nada"。

注意力的超参数

有三个超参数决定了数据的维度:

  • 嵌入尺寸:嵌入向量的宽度(我们的例子里用了宽度 6)。这个维度会贯穿整个 Transformer 模型,所以有时候也会被叫成别的名字,比如模型尺寸等等。

  • 查询尺寸(等于 Key 和 Value 的尺寸):三个线性层用来生成 Query、Key 和 Value 矩阵时,各自使用的权重的尺寸(我们的例子里,查询尺寸用了 3)。

  • 注意力头的数量(我们的例子里用了 2 个头)。

除此之外,还有个批量大小,它给了我们一个表示样本数量的维度。

输入层

输入嵌入和位置编码层,产出一个形状为 (样本数, 序列长度, 嵌入尺寸) 的矩阵。这个矩阵被喂给堆栈里第一个编码器的 Query、Key 和 Value。
4 输入层

为了让图示更简单,我们会丢掉批量这个维度,只关注剩下的维度。

4-1 输入层

线性层

Query、Key 和 Value 各自有一个独立的线性层,每个层有自己的权重。输入数据穿过这些线性层,就产生了 Q、K 和 V 矩阵。

5 线性层

把数据拆分到各个注意力头

现在,数据要被拆分到多个注意力头上,这样每个头才能独立处理。

但有个关键点需要理解:这仅仅是逻辑上的拆分。Query、Key 和 Value 并不是真的被切成了一个个独立的物理矩阵——每个头一个。相反,Query、Key 和 Value 各自仍然是一个单独的数据矩阵,只不过矩阵里为每个头划出了逻辑上独立的分区。同样,也不是每个头都有自己独立的线性层。实际上,所有注意力头共享同一个线性层,只不过它们在自己的那一块逻辑数据分区上操作。

线性层的权重也按头做了逻辑分区

这种逻辑拆分,是通过把输入数据线性层的权重,都均匀地划分到各个注意力头上实现的。我们可以通过设定下面的查询尺寸来达到这一点:

查询尺寸 = 嵌入尺寸 / 头的数量

6 线性层的权重也按头做了逻辑分区

在我们的例子里,查询尺寸 = 6 / 2 = 3。尽管线性层的权重(和输入数据)是一个单独的矩阵,但我们可以把它想象成:每个头的独立层权重,被堆叠在了一起。

6.1 线性层的权重也按头做了逻辑分区

因此,所有头的计算可以通过一次矩阵运算完成,而不需要 N 次独立的运算。这让计算更高效,也让模型保持简洁,因为需要的线性层更少了,但同时仍然保留了独立注意力头的强大能力。

重塑 Q、K 和 V 矩阵

线性层输出的 Q、K 和 V 矩阵会被重塑,以显式地包含一个「头」的维度。现在,每个切片就对应每个头的矩阵。

这个矩阵会被再次重塑——这次是交换「头」和「序列」这两个维度。尽管图示里没有画出批量维度,但 Q 现在的维度变成了 (批量, 头, 序列, 查询尺寸)。

7 重塑 Q、K 和 V 矩阵

下面的图示展示了我们的示例 Q 矩阵,从线性层出来后,被拆分的完整过程。

最后一个步骤只是为了可视化——虽然 Q 矩阵仍然是一个单独的矩阵,但我们可以把它想象成每个头有一个逻辑上独立的 Q 矩阵。

7.1 重塑 Q、K 和 V 矩阵

现在,我们准备好计算注意力分数了。

为每个头计算注意力分数

现在我们有了 Q、K 和 V 这三个矩阵,并且它们已经被拆分到了各个头上。下面就用它们来计算注意力分数。

我们会只使用最后两个维度(序列和查询尺寸)来展示单个头的计算,同时跳过前两个维度(批量和头)。本质上,我们可以想象这些计算会为每个头、批量里的每个样本重复进行——尽管很明显,它们实际上是作为一次矩阵运算发生的,而不是一个循环。

第一步,是做一个 Q 和 K 之间的矩阵乘法。

8 计算每个头部的注意力得分

然后,一个掩码值会被加到结果上。在编码器自注意力里,这个掩码用来屏蔽掉填充值,让它们不参与注意力分数的计算。

在解码器自注意力和解码器的编码器-解码器注意力里,会用到不同的掩码,我们稍后在流程中会讲到。

8.1

接下来,结果被缩放:除以查询尺寸的平方根。然后对它应用一个 Softmax

8.2

最后,再做一次矩阵乘法:这次是 Softmax 的输出 与 V 矩阵相乘。

8.3

编码器自注意力里,完整的注意力分数计算如下所示:

8.4

把每个头的注意力分数合并起来

现在我们为每个头都得到了独立的注意力分数,需要把它们合并成一个单一的分数。这个合并操作,基本上就是拆分操作的逆过程。

它的做法很简单:重塑结果矩阵,去掉这个维度。具体步骤是:

  • 通过交换序列维度,来重塑注意力分数矩阵。换句话说,矩阵形状从 (批量, 头, 序列, 查询尺寸) 变成 (批量, 序列, 头, 查询尺寸)。

  • 再通过重塑成 (批量, 序列, 头 * 查询尺寸),来坍缩掉「头」维度。这实际上就是把每个头的注意力分数向量,拼接成了一个合并后的注意力分数。

因为 嵌入尺寸 = 头数量 * 查询尺寸,所以合并后的分数维度是 (批量, 序列, 嵌入尺寸)。下面的图示展示了示例分数矩阵合并的完整过程。

9 将每个头部注意力得分合并在一起

端到端的多头注意力

把上面所有步骤整合起来,就是多头注意力的端到端流程。

10 端到端的多头注意力

多头拆分如何带来更丰富的解读

一个嵌入向量承载了一个词的含义。正如我们所见,在多 head 注意力里,输入(和目标)序列的嵌入向量会被逻辑上拆分到多个头上。这有什么意义呢?

11 多头拆分如何带来更丰富的解读

这意味着:嵌入向量的不同部分,可以去学习每个词含义的不同方面——特别是当这个词与序列里其他词产生关联的时候。这让 Transformer 能够捕捉到对序列更丰富的解读

下面的例子可能不太现实,但能帮你建立直觉。比方说,嵌入向量的一个部分可能捕捉名词的性别属性(阳性、阴性、中性),而另一个部分可能捕捉名词的数量属性(单数 vs 复数)。这在翻译时可能很重要——因为很多语言里,到底该用哪个动词,取决于这些属性。

解码器自注意力和掩码

解码器自注意力的工作方式,与编码器自注意力几乎一样,唯一的区别是,它操作的是目标序列的每个词。

12 解码器自注意力和掩码

类似地,这里的掩码,也是用来屏蔽掉目标序列里的填充词。

解码器中的编码器-解码器注意力和掩码

编码器-解码器注意力,从两个来源接收输入。因此,它和那两种自注意力都不一样:

  • 编码器自注意力计算的是:每个输入词与其他输入词之间的交互。

  • 解码器自注意力计算的是:每个目标词与其他目标词之间的交互。

  • 编码器-解码器注意力计算的是:每个目标词与每个输入词之间的交互。

13 解码器中的编码器-解码器注意力和遮蔽

所以,最终注意力分数矩阵里的每个格子,都对应一个 Query(也就是一个目标序列词)与所有 Key(输入序列词)和所有 Value(输入序列词)之间的交互。

同样,这里的掩码也会屏蔽掉目标输出中位置靠后的词。关于这一点,在系列的第二篇文章里已经详细解释过了。

结语

希望本篇文章能让读者对 Transformer 中的注意力模块的功能有一个大致的了解。结合我们在前面文章中介绍的 Transformer 整体端到端流程,现在已经详细阐述了整个 Transformer 架构的运作方式。

现在我们已完全理解了 Transformer 的工作原理。但是,我们还没有完全解答 Transformer 的 Attention 为什么要执行这些计算。为什么会使用查询、键和值的概念,以及为什么会执行我们刚才看到的矩阵乘法?

我们从直觉上介绍了 Transformer是捕捉每个词与其他词之间的关系,但这究竟是什么意思?是如何赋予 Transformer 的注意力机制理解序列中每个词的细微差别的能力的?

我们将在下篇文章给大家回答上述问题。

感谢阅读文章,如果觉得不错,欢迎点赞、关注、转发!

参考资料

Transformers Explained Visually (Part 3): Multi-head Attention, deep dive