【Transformer系列(2)】Multi-head self-attention 多头自注意力

一、多头自注意力

多头自注意力机制与自注意力机制的区别在于,Q,K,V向量被分为了num_heads份。

实现流程

(1)根据num_heads参数将单头变成多头,获取多头注意力中的各个头的Q,K,V值

(2)Q叉乘K的转置,再使用softmax,获取attention

(3)attention叉乘V,得到输出

二、代码实现

(1)根据num_heads参数将单头变成多头,获取多头注意力中的各个头的Q,K,V值

cpp 复制代码
# 每个token(Q,K,V)的尺寸
values_length = 33
# 原始单头长度
hidden_size = 768
# 单头qkv
# [33,768]
Query = np.random.rand(values_length, hidden_size)
Key = np.random.rand(values_length, hidden_size)
Value = np.random.rand(values_length, hidden_size)

# 单头 -> 分组为8个头
# [33,768] -> [33,8,96]
# 8个头
num_attention_heads = 8
# 原始单头拆分为多头后,我们单头的长度
attention_head_size = hidden_size // num_attention_heads
Query = np.reshape(Query, [values_length, num_attention_heads, attention_head_size])
Key = np.reshape(Key, [values_length, num_attention_heads, attention_head_size])
Value = np.reshape(Value, [values_length, num_attention_heads, attention_head_size])

# [33,8,96] -> [8,33,96] 头放最前面 M,H*W,C
Query = np.transpose(Query, [1, 0, 2])
Key = np.transpose(Key, [1, 0, 2])
Value = np.transpose(Value, [1, 0, 2])

(2)Q叉乘K的转置,再使用softmax,获取attention

cpp 复制代码
# qv -> attention
# [8,33,96] @ [8,96,33] -> [8,33,33] [m1,n] @ [n,m2] -> [m1,m2]
scores = Query @ np.transpose(Key, [0, 2, 1])
print(np.shape(scores))
# qv+softmax -> attention
scores = soft_max(scores)
print(np.shape(scores))

(3)attention叉乘V,得到输出

cpp 复制代码
# attention+v -> output
# [8,33,33] @ [8,33,96] -> [8,33,96] [m1,n] @ [n,m2] -> [m1,m2]
out = scores @ Value
print(np.shape(out))
# [8,33,96] -> [33,8,96]
out = np.transpose(out, [1, 0, 2])
print(np.shape(out))
# [33,8,96] -> [33,768]
out = np.reshape(out, [values_length , 768])
print(np.shape(out))

三、完整代码

cpp 复制代码
# multi-head self-attention #
# by liushuai #
# 2024/2/6 #

import numpy as np

def soft_max(z):
    t = np.exp(z)
    a = np.exp(z) / np.expand_dims(np.sum(t, axis=-1), -1)
    return a

# 每个token(Q,K,V)的尺寸
# 相当于H*W
values_length = 33
# 原始单头深度
# 相当于Channels
hidden_size = 768
# 单头qkv
# [33,768]
Query = np.random.rand(values_length, hidden_size)
Key = np.random.rand(values_length, hidden_size)
Value = np.random.rand(values_length, hidden_size)

# 单头 -> 分组为8个头
# [33,768] -> [33,8,96]
# 8个头
num_attention_heads = 8
# 原始单头拆分为多头后,我们单头的深度
attention_head_size = hidden_size // num_attention_heads
Query = np.reshape(Query, [values_length, num_attention_heads, attention_head_size])
Key = np.reshape(Key, [values_length, num_attention_heads, attention_head_size])
Value = np.reshape(Value, [values_length, num_attention_heads, attention_head_size])

# [33,8,96] -> [8,33,96] 头放最前面 M,H*W,C
Query = np.transpose(Query, [1, 0, 2])
Key = np.transpose(Key, [1, 0, 2])
Value = np.transpose(Value, [1, 0, 2])

# qv -> attention
# [8,33,96] @ [8,96,33] -> [8,33,33] [m1,n] @ [n,m2] -> [m1,m2]
scores = Query @ np.transpose(Key, [0, 2, 1])
print(np.shape(scores))
# qv+softmax -> attention
scores = soft_max(scores)
print(np.shape(scores))

# attention+v -> output
# [8,33,33] @ [8,33,96] -> [8,33,96] [m1,n] @ [n,m2] -> [m1,m2]
out = scores @ Value
print(np.shape(out))
# [8,33,96] -> [33,8,96]
out = np.transpose(out, [1, 0, 2])
print(np.shape(out))
# [33,8,96] -> [33,768]
out = np.reshape(out, [values_length , 768])
print(np.shape(out))
相关推荐
AnalogElectronic25 分钟前
人工智能初级工程师认证复习纲要(高频重点标记)
人工智能
前端不太难27 分钟前
AI 原生架构:鸿蒙App的下一代形态
人工智能·架构·harmonyos
Fzuim28 分钟前
从 LLM 接口到 Agent 接口:AI 融合系统的架构演进与未来趋势分析报告
人工智能·ai·重构·架构·agent·runtime
GISer_Jing7 小时前
AI自动化工作流:智能驱动未来(升级研究生项目!!!)
人工智能·前端框架·自动化
草捏子7 小时前
Agent Skills:让 AI 一次学会、永远记住的能力扩展方案
人工智能
NocoBase7 小时前
【2.0 教程】第 1 章:认识 NocoBase ,5 分钟跑起来
数据库·人工智能·开源·github·无代码
后端小肥肠7 小时前
OpenClaw实战|从识图到公众号内容自动化,我跑通了完整链路
人工智能·aigc·agent
Elastic 中国社区官方博客7 小时前
快速 vs. 准确:衡量量化向量搜索的召回率
大数据·人工智能·elasticsearch·搜索引擎·ai·全文检索
qq_381338507 小时前
【技术日报】2026-03-18 AI 领域重磅速递
大数据·人工智能
NocoBase8 小时前
开源项目管理工具选型指南(2026年最新)
人工智能·开源·无代码