【算法岗面试】手撕Self-Attention、Multi-head Attention

【算法岗面试】手撕Self-Attention、Multi-head Attention
输入 X: [B, L, d_model

Q/K/V 权重: [d_model, d_model] (合头写法,拆开后每头是 [d_model, d_k])

多头时:先全量 linear 得 [B, L, d_model],再 view/reshape 成 [B, L, num_heads, d_k],再 permute 成 [B, num_heads, L, d_k]

先用简单的Self-Attention捋一遍数据流动的过程:

import torch import torch.nn as nn import torch.nn.functional as F import math class SelfAttention(nn.Module): def __init__(self, embed_dim,d_k): super().__init__() self.embed_dim = embed_dim self.W_Q = nn.Linear(embed_dim, d_k) self.W_K = nn.Linear(embed_dim, d_k) self.W_V = nn.Linear(embed_dim, d_k) def forward(self, x): # x: [batch_size, seq_len, embed_dim] Q = self.W_Q(x) # [B, L, D] K = self.W_K(x) # [B, L, D] V = self.W_V(x) # [B, L, D] # Attention scores: [B, L, L] score = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.d_k) attn_weights = F.softmax(score, dim=-1) # [B, L, L] att_output = torch.matmul(attn_weights, V) # [B, L, D] return att_output 

然后再拓展到多头:

import torch import torch.nn as nn import math import torch.nn.functional as F class MultiHeadAttention(nn.Module): #定义参数 def __init__(self,embed_dim,head_num): super().__init__() self.embed_dim=embed_dim self.head_num=head_num self.head_dim=embed_dim//head_num #每个头的维度 #定义好Q,K,V矩阵和最后的输出变换矩阵 self.W_Q=nn.Linear(embed_dim, embed_dim) self.W_K=nn.Linear(embed_dim, embed_dim) self.W_V=nn.Linear(embed_dim, embed_dim) self.W_O=nn.Linear(embed_dim, embed_dim)# 注意力输出后再投回原维度 #前向传播 def forward(self,x): # x维度是BLD,batch_size seq_len embed_dim batch_size,seq_len,embed_dim=x.size() # 先全量投影得到了QKV矩阵再拆头 Q = self.W_Q(x) # [B, L, embed_dim] K = self.W_K(x) # [B, L, embed_dim] V = self.W_V(x) # [B, L, embed_dim] #拆分多头 # 方法:先view,再transpose # 拆分成[B, L, num_heads, head_dim],再变成[B, num_heads, L, head_dim] Q=Q.view(batch_size, seq_len, self.head_num, self.head_dim).transpose(1, 2) K=K.view(batch_size, seq_len, self.head_num, self.head_dim).transpose(1, 2) V=V.view(batch_size, seq_len, self.head_num, self.head_dim).transpose(1, 2) # 此时shape均为[B, num_heads, L, head_dim] # Q @ K^T:最后两维做乘法 # K.transpose(-2, -1): [B, num_heads, head_dim, L] score = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.head_dim) # [B, num_heads, L, L] attn_weights = F.softmax(score, dim=-1) # [B, num_heads, L, L] # 得到每个头的注意力输出 att_output = torch.matmul(attn_weights, V) # [B, num_heads, L, head_dim] # 变回 [B, L, embed_dim] # 先transpose(1,2): [B, L, num_heads, head_dim] # 然后view为 [B, L, num_heads*head_dim] = [B, L, embed_dim] att_output = att_output.transpose(1, 2).contiguous().view(batch_size, seq_len, self.embed_dim) output = self.W_O(att_output) # [B, L, embed_dim] return output

为什么要拆分成 (num_heads, head_dim)

  • 背景:你的输入每个 token 有 embed_dim 维(比如 768)。多头注意力机制本质上是把输入特征维度切成 num_heads 块,每块 head_dim 维,分别做自注意力,然后拼回去。
  • 本质:每个头都是一个“小的单头 self-attention”,但只用一部分特征(head_dim = embed_dim // num_heads)。
  • 举例:如果 embed_dim=768, num_heads=12, 每头 head_dim=64。768=12*64。

原始Q的shape

  • Q = [B, L, embed_dim] (batch, sequence, feature维)

目标:希望得到一个 shape = [B, num_heads, L, head_dim]

这样后续每个head可以独立做 Attention(矩阵乘法/softmax/加权 …)。


为什么用 view(B, L, num_heads, head_dim).transpose(1, 2)

Step 1: view(B, L, num_heads, head_dim)

  • 把最后一维 embed_dim 拆成 num_heads * head_dim
  • 假设 embed_dim=768, num_heads=12, head_dim=64,则拆分成 [B, L, 12, 64]

Step 2: transpose(1, 2)

  • 把 head 数移到序列长度前面
  • [B, L, num_heads, head_dim] --> [B, num_heads, L, head_dim]
  • 这样每个 batch 下,对每个头进行独立计算(更方便并行处理多头)

过程可视化

比如有 Q: [2, 10, 768](batch=2, seq=10, 768维)

  • view(2, 10, 12, 64) -- 12个头,每头64维
  • transpose(1, 2) 得到 (2, 12, 10, 64)

为什么顺序不能交换?

如果你写成 view(B, num_heads, L, head_dim),就完全不对了!因为:

  • 原始数据是按 [B, L, embed_dim] 顺序排列的。
  • view 顺序必须是先序列后特征,特征维度用于拆分
  • 而且在 PyTorch、Tensorflow 中,view 后的数据不会自动乱序分配,只是“重新组织 shape”,不会帮助你把循环顺序换掉。
  • transpose(1, 2) 是在 [B, L, num_heads, head_dim] 基础上,把 head 放到序列之前。

如果互换 num_heads、L 顺序,会把 batch 里的时间步和头搞混,后续 Attention 计算也会错。


为什么最终要 [B, num_heads, L, head_dim]

  • 这样每个头彼此独立,并且都遍历了全部 batch 的序列。
  • 方便后续在每个头上分别做 Attention 计算。

总结口诀

view 拆头之前,总是最后一维(embed_dim)先拆成 (num_heads, head_dim),再用 transpose 把 head 移到 L 之前,得到 [B, num_heads, L, head_dim]。不能交换顺序,因为原始数据排列是 batch, seq, feat,再拆 feat。

Read more

AI的提示词专栏:LLaMA-2 与 Mixtral 的提示词调优技巧

AI的提示词专栏:LLaMA-2 与 Mixtral 的提示词调优技巧

AI的提示词专栏:LLaMA-2 与 Mixtral 的提示词调优技巧 本文围绕 LLaMA-2 与 Mixtral 两大模型的提示词调优展开,先分析二者核心特性,再针对性给出适配原则与实战技巧。LLaMA-2 因参数规模差异大、通用领域训练数据为主、指令敏感度低,需按参数分层设计提示词、补充领域知识、强化指令约束,还提供了结构化指令、Few-Shot 示例等 5 个实战技巧;Mixtral 凭借混合专家架构、长上下文窗口、强多语言能力,需引导激活对应专家模块、合理处理长文本、规范多语言输出,配套专家引导指令等 4 个技巧。文章还对比二者调优重点与适用场景,指出常见误区并给出避坑方案,最后总结核心思路并提供后续实践建议,助力开发者优化提示词、发挥模型性能。 人工智能专栏介绍     人工智能学习合集专栏是 AI 学习者的实用工具。它像一个全面的 AI 知识库,把提示词设计、AI 创作、智能绘图等多个细分领域的知识整合起来。

By Ne0inhk
2026最火的6款免费AI写作软件测评:ai写网文哪个好用?这款ai消痕工具

2026最火的6款免费AI写作软件测评:ai写网文哪个好用?这款ai消痕工具

很多朋友想在业余时间写写番茄、起点网文或者搞搞短剧赚点外快,但总是卡在“憋不出字”或者“大纲写崩”上。现在都2026年了,用ai写作软件来辅助写小说早就不是秘密了。 但是,网文平台的审核越来越严,很多新手直接用AI生成的文章发出去,立马就被平台判定为“AI生成”导致限流,不仅没流量,连全勤奖都拿不到。 今天,我们就抛开那些晦涩难懂的技术术语,用大白话给大家实测目前市面上热度最高的6款免费ai写作平台。到底ai写网文哪家强?怎么解决让人头疼的“机器味”?这篇超详细的避坑指南,建议想靠文字搞钱的朋友直接收藏! 一、 6大热门免费AI小说工具优缺点大盘点 我们选了大家最常搜的几款工具,直接看它们在实际写小说、写剧本时的真实表现。 1. 豆包:起名和找灵感的“点子王” * 优点:速度飞快,完全免费。你如果卡文了,或者不知道主角叫什么、书名怎么起才能吸引人,直接问豆包,它能一秒钟给你吐出几十个极其符合抖音、小红书调性的网感标题和名字。 * 缺点:千万别让它直接给你写正文!它的AI味太重了,动不动就是“嘴角勾起一抹弧度”、“倒吸一口凉气”。把这种文发到小说平台,

By Ne0inhk

VSCode + Copilot下:配置并使用 DeepSeek

以下是关于在 VSCode + Copilot 中,通过 OAI Compatible Provider for Copilot 插件配置并使用 DeepSeek 系列模型 (deepseek-chat, deepseek-reasoner, deepseek-coder) 的完整汇总指南。 🎯 核心目标 通过该插件,将支持 OpenAI API 格式的第三方大模型(此处为 DeepSeek)接入 VSCode 的官方 Copilot 聊天侧边栏,实现原生体验的调用。 📦 第一步:准备工作 在开始配置前,请确保已完成以下准备: 步骤操作说明1. 安装插件在 VSCode 扩展商店搜索并安装 OAI Compatible Provider for Copilot。这是连接 Copilot 与第三方模型的核心桥梁。2. 获取 API

By Ne0inhk