这篇文章我们把**单头 Attention 扩展成 Multi-Head Attention**一行一行写出来每一步都打印 shape 验证。如果你还记得上篇的结论——单头 Attention 的问题是视角太单一一次计算只能关注一种语义关系。多头的解法是把维度切开让多个头并行各司其职。现在我们就把这个过程真正手撕出来。多头比单头多了哪几步单头 Attention 的流程是X → (W_Q, W_K, W_V) → Q, K, V → Attention → 输出Multi-Head Attention 只多了三步X → (W_Q, W_K, W_V) → Q, K, V → 拆分成 h 个头reshape → 每个头独立跑 Attention → 拼接concat → 乘输出投影矩阵 W_O → 最终输出就这四个新动作拆分 → 并行计算 → 拼接 → 输出投影。第一步线性层映射生成 Q、K、V这一步和单头完全一样。输入 X 分别乘三个权重矩阵得到 Q、K、V。import numpy as np# 超参数设置L 7 # 序列长度远方有颗苹果树7个字d_model 8# embedding 维度实际是512这里用8方便演示h 2 # 注意力头数实际常用8或16d_k d_model // h # 每个头的维度 8 // 2 4np.random.seed(42)# 模拟 embedding 后的输入X np.random.randn(L, d_model)print(f输入 X: {X.shape}) # (7, 8)# 三个权重矩阵实际中是可学习参数W_Q np.random.randn(d_model, d_model)W_K np.random.randn(d_model, d_model)W_V np.random.randn(d_model, d_model)# 线性映射得到 Q、K、VQ X W_Q # (7, 8) (8, 8) (7, 8)K X W_KV X W_Vprint(fQ: {Q.shape}) # (7, 8)print(fK: {K.shape}) # (7, 8)print(fV: {V.shape}) # (7, 8)输出输入 X: (7, 8)Q: (7, 8)K: (7, 8)V: (7, 8)Q、K、V 的形状都是(L, d_model)和单头完全一样。区别在于下一步单头直接拿去算 Attention多头要先把它切开。第二步Head 拆分——把 d_model 切成 h 份这是多头和单头最关键的差异所在。以 、为例每个头拿到 维# reshape: (L, d_model) → (L, h, d_k)Q_split Q.reshape(L, h, d_k)K_split K.reshape(L, h, d_k)V_split V.reshape(L, h, d_k)print(f\n拆分后)print(fQ_split: {Q_split.shape}) # (7, 2, 4)print(fK_split: {K_split.shape}) # (7, 2, 4)print(fV_split: {V_split.shape}) # (7, 2, 4)# 转置为 (h, L, d_k)方便每个头独立计算Q_heads Q_split.transpose(1, 0, 2)K_heads K_split.transpose(1, 0, 2)V_heads V_split.transpose(1, 0, 2)print(f\n转置后便于并行计算)print(fQ_heads: {Q_heads.shape}) # (2, 7, 4)print(fK_heads: {K_heads.shape}) # (2, 7, 4)print(fV_heads: {V_heads.shape}) # (2, 7, 4)输出拆分后Q_split: (7, 2, 4)K_split: (7, 2, 4)V_split: (7, 2, 4)转置后便于并行计算Q_heads: (2, 7, 4)K_heads: (2, 7, 4)V_heads: (2, 7, 4)怎么理解这个 reshape原来的 Q 是(7, 8)也就是 7 个 token每个 token 8 维。拆成(7, 2, 4)之后变成7 个 token每个 token 有 2 个视角每个视角 4 维。转置成(2, 7, 4)之后变成2 个头每个头看 7 个 token每个 token 4 维。这样第 0 个头和第 1 个头就可以独立并行地做 Attention 计算了。第三步每个头独立跑 Attention拆分好之后每个头的计算和单头完全一样QKᵀ → 缩放 → Softmax → 加权 V。def softmax(x): e np.exp(x - np.max(x, axis-1, keepdimsTrue)) return e / e.sum(axis-1, keepdimsTrue)def single_head_attention(Q, K, V): 单个头的 Attention输入输出都是 (L, d_k) d_k Q.shape[-1] scores Q K.T # (L, L) scores scores / np.sqrt(d_k) # 缩放 weights softmax(scores) # (L, L) return weights V # (L, d_k)# 对每个头分别计算head_outputs []for i in range(h): out_i single_head_attention(Q_heads[i], K_heads[i], V_heads[i]) head_outputs.append(out_i) print(fHead {i} 输出: {out_i.shape}) # (7, 4)输出Head 0 输出: (7, 4)Head 1 输出: (7, 4)2 个头每个头输出(7, 4)。注意每个头看到的是同一个句子但是在不同的 4 维子空间里理解它所以结果是不同的。第四步Concat 拼接——把多个头合并回来2 个头算完了怎么合并横向拼接沿着最后一个维度 concat。# 先把 list 转成数组 (h, L, d_k)head_outputs np.stack(head_outputs, axis0)print(f\n拼接前stack: {head_outputs.shape}) # (2, 7, 4)# 转置回 (L, h, d_k)再 reshape 成 (L, d_model)head_outputs head_outputs.transpose(1, 0, 2)print(f转置后: {head_outputs.shape}) # (7, 2, 4)concat_output head_outputs.reshape(L, d_model)print(f拼接后reshape: {concat_output.shape}) # (7, 8)输出拼接前stack: (2, 7, 4)转置后: (7, 2, 4)拼接后reshape: (7, 8)两个头各自的 4 维输出拼在一起重新变回了 8 维。维度和输入 X 完全一样。第五步输出投影 W_O——让多个头融合对话拼接之后还差最后一步乘输出投影矩阵 。# 输出投影矩阵 W_O: (d_model, d_model)W_O np.random.randn(d_model, d_model)# 最终输出final_output concat_output W_Oprint(f\n输出投影后: {final_output.shape}) # (7, 8)print(f输入 X 形状: {X.shape}) # (7, 8)print(f形状是否一致: {final_output.shape X.shape})输出输出投影后: (7, 8)输入 X 形状: (7, 8)形状是否一致: True为什么还要乘 W_OConcat 之后8 维向量的前 4 维来自 Head 0后 4 维来自 Head 1。它们各自是独立计算的彼此之间还没有交流过。做的事就是让不同头的信息能够互相混合产生一个统一的表示传给后续的 FFN 子层。完整代码把五步打包成一个函数import numpy as npdef multi_head_attention(X, W_Q, W_K, W_V, W_O, h): Multi-Head Attention 完整实现 参数: X: 输入矩阵, shape (L, d_model) W_Q, W_K, W_V: 线性投影矩阵, shape (d_model, d_model) W_O: 输出投影矩阵, shape (d_model, d_model) h: 注意力头数 返回: output: shape (L, d_model) L, d_model X.shape d_k d_model // h # ① 线性映射 Q X W_Q K X W_K V X W_V print(f[①线性映射] Q/K/V: {Q.shape}) # ② 拆分成 h 个头: (L, d_model) → (h, L, d_k) def split_heads(M): return M.reshape(L, h, d_k).transpose(1, 0, 2) Q_h split_heads(Q) K_h split_heads(K) V_h split_heads(V) print(f[②Head拆分] Q_h/K_h/V_h: {Q_h.shape}) # ③ 每个头独立计算 Attention def softmax(x): e np.exp(x - np.max(x, axis-1, keepdimsTrue)) return e / e.sum(axis-1, keepdimsTrue) head_outs [] for i in range(h): scores Q_h[i] K_h[i].T / np.sqrt(d_k) attn softmax(scores) V_h[i] head_outs.append(attn) print(f[③并行Attention] 每个head输出: {head_outs[0].shape}) # ④ Concat 拼接: (h, L, d_k) → (L, d_model) concat np.stack(head_outs, axis0).transpose(1, 0, 2).reshape(L, d_model) print(f[④Concat拼接] 拼接后: {concat.shape}) # ⑤ 输出投影 output concat W_O print(f[⑤输出投影] 最终输出: {output.shape}) return output# ——— 运行测试 ———if __name__ __main__: np.random.seed(42) L, d_model, h 7, 8, 2 X np.random.randn(L, d_model) W_Q np.random.randn(d_model, d_model) W_K np.random.randn(d_model, d_model) W_V np.random.randn(d_model, d_model) W_O np.random.randn(d_model, d_model) print( Multi-Head Attention ) out multi_head_attention(X, W_Q, W_K, W_V, W_O, h) print(f\n输入形状: {X.shape}) print(f输出形状: {out.shape}) print(f形状一致: {X.shape out.shape})运行输出 Multi-Head Attention [①线性映射] Q/K/V: (7, 8)[②Head拆分] Q_h/K_h/V_h: (2, 7, 4)[③并行Attention] 每个head输出: (7, 4)[④Concat拼接] 拼接后: (7, 8)[⑤输出投影] 最终输出: (7, 8)输入形状: (7, 8)输出形状: (7, 8)形状一致: True五步流程步骤操作输入形状输出形状① 线性映射X 乘② Head 拆分reshape transpose③ 并行 Attention每个头独立跑完整 Attention④ Concat 拼接transpose reshape⑤ 输出投影乘(从头到尾输入是 输出还是 。中间经历了一次维度的分家再合并但整体维度始终保持一致这也是 Transformer 能一层层堆叠的基础。这篇文章我们把 Multi-Head Attention 的五个步骤完整手撕了一遍代码加注释不到 60 行。核心只有一句话★多头不是用更多参数而是用同样的参数在多个子空间里并行理解语言。学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】