手把手拆解Q、K、V计算:Transformer的“相亲匹配”算法

7 阅读6分钟

手把手拆解Q、K、V计算:Transformer的“相亲匹配”算法

想象一下,你正在组织一场“词语相亲大会”。每个词都要找到和自己最匹配的词,然后交换信息。Q、K、V就是这个相亲大会的“自我介绍卡”、“择偶标准卡”和“真心话卡”。

一、先看整体:三个矩阵的“角色扮演”

在Transformer里,每个词都要准备三张“卡片”:

  1. Q(Query,查询卡) :“我想了解什么?”

    • 好比相亲时你说:“我想找个喜欢读书的人”
  2. K(Key,钥匙卡) :“我有什么特点?”

    • 好比对方说:“我喜欢读书、旅行、看电影”
  3. 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]

计算匹配度(点积)

点积公式:对应元素相乘再相加

  1. Q 与 我K 的点积

    0.89×0.57 + 1.07×0.75 + 1.25×0.93
    = 0.5073 + 0.8025 + 1.1625
    = 2.4723
    
  2. Q 与 爱K 的点积

    0.89×0.23 + 1.07×0.41 + 1.25×0.59
    = 0.2047 + 0.4387 + 0.7375
    = 1.3809
    
  3. 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)

  1. 计算每个值的指数:

    exp(1.427) ≈ 4.166
    exp(0.797) ≈ 2.219
    exp(2.354) ≈ 10.529
    
  2. 计算总和:

    4.166 + 2.219 + 10.529 = 16.914
    
  3. 计算每个的权重:

    我: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)
  • 但也融合了“我”和“爱”的信息

五、完整流程总结

  1. 输入词向量:每个词有自己的向量
  2. 线性变换:用W_Q、W_K、W_V矩阵生成Q、K、V
  3. 计算注意力分数:Q和K的点积
  4. 缩放:除以√d_k防止数值过大
  5. Softmax:变成注意力权重(概率分布)
  6. 加权求和:用权重对V向量加权平均
  7. 输出新向量:包含上下文信息的新表示

六、多头注意力:多个“相亲角”并行

实际中,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)

运行这段代码,你会得到和我们手工计算一样的结果。

八、关键要点总结

  1. Q、K、V来自同一源头:都是同一个词向量乘以不同的权重矩阵得到的
  2. 点积测量相似度:Q·K越大,两个词越相关
  3. Softmax归一化:把相似度变成概率分布
  4. 加权求和融合信息:用权重混合所有V向量
  5. 每个词都做一遍:不只是“吃”,每个词都会计算自己的新向量

简单记忆

  • Q:我要找什么?(查询)
  • K:我有什么特点?(钥匙)
  • V:我的具体信息是什么?(价值)
  • Q·K:我们有多匹配?(匹配度)
  • Softmax:分配注意力比例(权重)
  • 加权V:汇总大家的信息(融合)

这就是Transformer理解语言的核心机制——通过计算每个词与其他词的“匹配度”,然后智能地融合信息,让每个词都能“看到”整个句子的上下文。