手把手拆解Q、K、V计算:Transformer的“相亲匹配”算法
想象一下,你正在组织一场“词语相亲大会”。每个词都要找到和自己最匹配的词,然后交换信息。Q、K、V就是这个相亲大会的“自我介绍卡”、“择偶标准卡”和“真心话卡”。
一、先看整体:三个矩阵的“角色扮演”
在Transformer里,每个词都要准备三张“卡片”:
-
Q(Query,查询卡) :“我想了解什么?”
- 好比相亲时你说:“我想找个喜欢读书的人”
-
K(Key,钥匙卡) :“我有什么特点?”
- 好比对方说:“我喜欢读书、旅行、看电影”
-
V(Value,价值卡) :“我的详细信息”
- 好比对方详细描述:“我每周读2本书,去过20个国家...”
关键点:这三张卡都是从同一个词向量变来的,只是用了不同的“变身规则”(权重矩阵)。
二、详细计算步骤:从词向量到Q、K、V
步骤1:准备原始词向量
假设我们有3个词,每个词用4维向量表示(实际中可能是768维或4096维):
我 = [1.2, 0.8, -0.5, 0.3]
爱 = [0.5, 1.3, 0.2, -0.7]
吃 = [-0.3, 0.9, 1.1, 0.4]
步骤2:准备三个“变身器”(权重矩阵)
这三个矩阵是模型通过学习得到的,我们假设已经训练好了:
W_Q矩阵(4×3大小) :把4维词向量变成3维Q向量
W_Q = [[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9],
[1.0, 1.1, 1.2]]
W_K矩阵(4×3大小) :把4维词向量变成3维K向量
W_K = [[0.2, 0.3, 0.4],
[0.5, 0.6, 0.7],
[0.8, 0.9, 1.0],
[1.1, 1.2, 1.3]]
W_V矩阵(4×3大小) :把4维词向量变成3维V向量
W_V = [[0.3, 0.4, 0.5],
[0.6, 0.7, 0.8],
[0.9, 1.0, 1.1],
[1.2, 1.3, 1.4]]
步骤3:计算“我”的Q、K、V
计算“我”的Q向量:
我_Q = 我 × W_Q
= [1.2, 0.8, -0.5, 0.3] × [[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9],
[1.0, 1.1, 1.2]]
矩阵乘法就是“行乘列求和”:
- 第一个元素:1.2×0.1 + 0.8×0.4 + (-0.5)×0.7 + 0.3×1.0 = 0.12 + 0.32 - 0.35 + 0.30 = 0.39
- 第二个元素:1.2×0.2 + 0.8×0.5 + (-0.5)×0.8 + 0.3×1.1 = 0.24 + 0.40 - 0.40 + 0.33 = 0.57
- 第三个元素:1.2×0.3 + 0.8×0.6 + (-0.5)×0.9 + 0.3×1.2 = 0.36 + 0.48 - 0.45 + 0.36 = 0.75
所以:我_Q = [0.39, 0.57, 0.75]
计算“我”的K向量:
我_K = 我 × W_K
= [1.2, 0.8, -0.5, 0.3] × [[0.2, 0.3, 0.4],
[0.5, 0.6, 0.7],
[0.8, 0.9, 1.0],
[1.1, 1.2, 1.3]]
计算:
- 第一个元素:1.2×0.2 + 0.8×0.5 + (-0.5)×0.8 + 0.3×1.1 = 0.24 + 0.40 - 0.40 + 0.33 = 0.57
- 第二个元素:1.2×0.3 + 0.8×0.6 + (-0.5)×0.9 + 0.3×1.2 = 0.36 + 0.48 - 0.45 + 0.36 = 0.75
- 第三个元素:1.2×0.4 + 0.8×0.7 + (-0.5)×1.0 + 0.3×1.3 = 0.48 + 0.56 - 0.50 + 0.39 = 0.93
所以:我_K = [0.57, 0.75, 0.93]
计算“我”的V向量:
我_V = 我 × W_V
= [1.2, 0.8, -0.5, 0.3] × [[0.3, 0.4, 0.5],
[0.6, 0.7, 0.8],
[0.9, 1.0, 1.1],
[1.2, 1.3, 1.4]]
计算:
- 第一个元素:1.2×0.3 + 0.8×0.6 + (-0.5)×0.9 + 0.3×1.2 = 0.36 + 0.48 - 0.45 + 0.36 = 0.75
- 第二个元素:1.2×0.4 + 0.8×0.7 + (-0.5)×1.0 + 0.3×1.3 = 0.48 + 0.56 - 0.50 + 0.39 = 0.93
- 第三个元素:1.2×0.5 + 0.8×0.8 + (-0.5)×1.1 + 0.3×1.4 = 0.60 + 0.64 - 0.55 + 0.42 = 1.11
所以:我_V = [0.75, 0.93, 1.11]
步骤4:同样方法计算“爱”和“吃”的Q、K、V
为了节省篇幅,我直接给出结果(你可以自己验算):
爱_Q = [0.05, 0.23, 0.41]
爱_K = [0.23, 0.41, 0.59]
爱_V = [0.41, 0.59, 0.77]
吃_Q = [0.89, 1.07, 1.25]
吃_K = [1.07, 1.25, 1.43]
吃_V = [1.25, 1.43, 1.61]
步骤5:汇总所有Q、K、V
现在我们有:
词 Q向量 K向量 V向量
我 [0.39,0.57,0.75] [0.57,0.75,0.93] [0.75,0.93,1.11]
爱 [0.05,0.23,0.41] [0.23,0.41,0.59] [0.41,0.59,0.77]
吃 [0.89,1.07,1.25] [1.07,1.25,1.43] [1.25,1.43,1.61]
三、注意力计算:相亲匹配过程
步骤6:计算“吃”对其他词的注意力分数
现在以“吃”这个词为例,看看它如何关注其他词(包括自己)。
“吃”的Q向量:吃_Q = [0.89, 1.07, 1.25]
所有词的K向量:
- 我_K = [0.57, 0.75, 0.93]
- 爱_K = [0.23, 0.41, 0.59]
- 吃_K = [1.07, 1.25, 1.43]
计算匹配度(点积) :
点积公式:对应元素相乘再相加
-
吃Q 与 我K 的点积:
0.89×0.57 + 1.07×0.75 + 1.25×0.93 = 0.5073 + 0.8025 + 1.1625 = 2.4723 -
吃Q 与 爱K 的点积:
0.89×0.23 + 1.07×0.41 + 1.25×0.59 = 0.2047 + 0.4387 + 0.7375 = 1.3809 -
吃Q 与 吃K 的点积:
0.89×1.07 + 1.07×1.25 + 1.25×1.43 = 0.9523 + 1.3375 + 1.7875 = 4.0773
得到原始分数:[2.4723, 1.3809, 4.0773]
步骤7:缩放和Softmax
缩放:除以√d_k(d_k是K向量的维度,这里是3)
√3 ≈ 1.732
缩放后分数:
2.4723 ÷ 1.732 ≈ 1.427
1.3809 ÷ 1.732 ≈ 0.797
4.0773 ÷ 1.732 ≈ 2.354
Softmax:变成概率分布
公式:exp(x_i) / Σ exp(x_j)
-
计算每个值的指数:
exp(1.427) ≈ 4.166 exp(0.797) ≈ 2.219 exp(2.354) ≈ 10.529 -
计算总和:
4.166 + 2.219 + 10.529 = 16.914 -
计算每个的权重:
我:4.166 ÷ 16.914 ≈ 0.246 爱:2.219 ÷ 16.914 ≈ 0.131 吃:10.529 ÷ 16.914 ≈ 0.623
所以“吃”对各个词的注意力权重是:[0.246, 0.131, 0.623]
解读:“吃”最关注自己(62.3%),其次是“我”(24.6%),最后是“爱”(13.1%)。
步骤8:加权求和V向量
用这些权重对V向量加权平均:
所有V向量:
- 我_V = [0.75, 0.93, 1.11]
- 爱_V = [0.41, 0.59, 0.77]
- 吃_V = [1.25, 1.43, 1.61]
加权求和:
新向量 = 0.246×我_V + 0.131×爱_V + 0.623×吃_V
计算每个维度:
第一维:
0.246×0.75 + 0.131×0.41 + 0.623×1.25
= 0.1845 + 0.0537 + 0.7788
= 1.0170
第二维:
0.246×0.93 + 0.131×0.59 + 0.623×1.43
= 0.2288 + 0.0773 + 0.8909
= 1.1970
第三维:
0.246×1.11 + 0.131×0.77 + 0.623×1.61
= 0.2731 + 0.1009 + 1.0030
= 1.3770
所以“吃”的新向量 = [1.0170, 1.1970, 1.3770]
四、为什么这样计算?——直观理解
4.1 Q和K的点积:测量“相关性”
- 点积越大,两个向量越相似
- “吃”和“吃”自己最相似(4.0773)
- “吃”和“我”次之(2.4723)
- “吃”和“爱”最不相似(1.3809)
这反映了语义关系:“吃”这个动作,和“吃”自己最相关,和动作发出者“我”也相关,和情感“爱”关系稍远。
4.2 Softmax:归一化成“注意力分布”
- 把原始分数变成概率
- 保证所有权重和为1
- 让大的更大,小的更小(指数函数放大差异)
4.3 加权V:融合信息
- 高权重的V对结果影响大
- “吃”自己的V占主导(权重0.623)
- 但也融合了“我”和“爱”的信息
五、完整流程总结
- 输入词向量:每个词有自己的向量
- 线性变换:用W_Q、W_K、W_V矩阵生成Q、K、V
- 计算注意力分数:Q和K的点积
- 缩放:除以√d_k防止数值过大
- Softmax:变成注意力权重(概率分布)
- 加权求和:用权重对V向量加权平均
- 输出新向量:包含上下文信息的新表示
六、多头注意力:多个“相亲角”并行
实际中,Transformer使用多个注意力头(比如8个或16个),每个头有自己的W_Q、W_K、W_V矩阵,关注不同的方面:
- 头1:关注语法关系(主谓宾)
- 头2:关注语义关系(同义词、反义词)
- 头3:关注位置关系(词序)
- ...
最后把所有头的结果拼接起来,得到更丰富的表示。
七、用Python代码验证
import numpy as np
# 定义词向量
我 = np.array([1.2, 0.8, -0.5, 0.3])
爱 = np.array([0.5, 1.3, 0.2, -0.7])
吃 = np.array([-0.3, 0.9, 1.1, 0.4])
# 定义权重矩阵
W_Q = np.array([[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9],
[1.0, 1.1, 1.2]])
W_K = np.array([[0.2, 0.3, 0.4],
[0.5, 0.6, 0.7],
[0.8, 0.9, 1.0],
[1.1, 1.2, 1.3]])
W_V = np.array([[0.3, 0.4, 0.5],
[0.6, 0.7, 0.8],
[0.9, 1.0, 1.1],
[1.2, 1.3, 1.4]])
# 计算Q、K、V
我_Q = 我 @ W_Q # @表示矩阵乘法
我_K = 我 @ W_K
我_V = 我 @ W_V
爱_Q = 爱 @ W_Q
爱_K = 爱 @ W_K
爱_V = 爱 @ W_V
吃_Q = 吃 @ W_Q
吃_K = 吃 @ W_K
吃_V = 吃 @ W_V
print("我_Q:", 我_Q)
print("我_K:", 我_K)
print("我_V:", 我_V)
print()
print("爱_Q:", 爱_Q)
print("爱_K:", 爱_K)
print("爱_V:", 爱_V)
print()
print("吃_Q:", 吃_Q)
print("吃_K:", 吃_K)
print("吃_V:", 吃_V)
# 计算注意力分数(吃关注其他词)
scores = []
scores.append(吃_Q @ 我_K) # 点积
scores.append(吃_Q @ 爱_K)
scores.append(吃_Q @ 吃_K)
print("\n原始分数:", scores)
# 缩放
d_k = 3
scaled_scores = [score / np.sqrt(d_k) for score in scores]
print("缩放后分数:", scaled_scores)
# Softmax
exp_scores = [np.exp(score) for score in scaled_scores]
sum_exp = sum(exp_scores)
weights = [exp / sum_exp for exp in exp_scores]
print("注意力权重:", weights)
# 加权求和V
new_vector = weights[0] * 我_V + weights[1] * 爱_V + weights[2] * 吃_V
print("\n吃的新向量:", new_vector)
运行这段代码,你会得到和我们手工计算一样的结果。
八、关键要点总结
- Q、K、V来自同一源头:都是同一个词向量乘以不同的权重矩阵得到的
- 点积测量相似度:Q·K越大,两个词越相关
- Softmax归一化:把相似度变成概率分布
- 加权求和融合信息:用权重混合所有V向量
- 每个词都做一遍:不只是“吃”,每个词都会计算自己的新向量
简单记忆:
- Q:我要找什么?(查询)
- K:我有什么特点?(钥匙)
- V:我的具体信息是什么?(价值)
- Q·K:我们有多匹配?(匹配度)
- Softmax:分配注意力比例(权重)
- 加权V:汇总大家的信息(融合)
这就是Transformer理解语言的核心机制——通过计算每个词与其他词的“匹配度”,然后智能地融合信息,让每个词都能“看到”整个句子的上下文。