Amit Shekhar (@amitiitbhu)
2026-04-03
D
原文
---
title: "注意力机制背后的数学:Q、K 和 V"
author: "Amit Shekhar (@amitiitbhu)"
source_url: "https://x.com/amitiitbhu/status/2040027305297526811"
published_at: "2026-04-03T11:23:06.000Z"
fetched_at: "2026-06-27T23:39:59Z"
updated_at: "2026-07-23T16:07:01Z"
language: "zh"
review_status: "draft"
---

# 注意力机制背后的数学:Q、K 和 V
在这篇文章里,我们会通过一个逐步展开的数值例子,学习注意力机制背后的数学:查询(Query,Q)、键(Key,K)和值(Value,V)。
我们会讲到:
- 注意力公式
- 准备工作:从词到向量
- 创建 Q、K 和 V 矩阵
- 计算注意力分数(Q x K^T)
- 缩放分数
- 应用 softmax
- 计算最终输出(注意力权重 x V)
- 把所有步骤串起来
开始吧。
### 注意力公式
**缩放点积注意力(Scaled Dot-Product Attention)**的核心公式是:
```markdown
Attention(Q, K, V) = softmax(Q x K^T / sqrt(d_k)) x V
```
这里:
- **Q** 是查询矩阵
- **K** 是键矩阵
- **V** 是值矩阵
- **K^T** 是键矩阵的转置
- **d_k** 是键向量的维度
- **sqrt** 表示平方根
如果这个公式看起来复杂,不用担心。我们会用真实数字一步步拆开它。读完这篇文章后,这个公式的每一部分都会变得清楚。
思路很简单:我们把每个词想寻找的内容(**查询**),和其他每个词能提供的内容(**键**)进行比较,再根据比较结果,从最相关的词中收集实际信息(**值**)。
现在,用真实数字来看一遍。
### 准备工作:从词到向量
我们取一个只有 3 个词的简单句子:
**"I love AI"(我爱 AI)**
在 Transformer 里,每个词会先被转换成一个向量(也就是一组数字),称为[嵌入(embedding)](https://www.youtube.com/watch?v=LedXW6xl21s)。为了便于理解,我们会使用非常小的 4 维向量(也就是 **d_emb = 4**)。
```markdown
"I" → [1.0, 0.0, 1.0, 0.0]
"love" → [0.0, 1.0, 0.0, 1.0]
"AI" → [1.0, 1.0, 0.0, 0.0]
```
我们把这些向量堆叠成一个形状为 **3 x 4** 的**输入矩阵 X**(3 个词,每个词有 4 个数字):
```markdown
X = | 1.0 0.0 1.0 0.0 | ← "I"
| 0.0 1.0 0.0 1.0 | ← "love"
| 1.0 1.0 0.0 0.0 | ← "AI"
```
这里,每一行代表一个词,每一列对应嵌入向量中的一个数字。这就是我们的起点。
### 创建 Q、K 和 V 矩阵
现在,我们需要创建三个单独的矩阵:**Q(查询)**、**K(键)** 和 **V(值)**。
怎么创建它们?我们把输入矩阵 **X** 分别乘以三个不同的**权重矩阵**:**W_Q**、**W_K** 和 **W_V**。这些权重矩阵是在训练过程中学习得到的。
```markdown
Q = X x W_Q
K = X x W_K
V = X x W_V
```
在我们的例子里,假设 **d_k = 3**(也就是我们想让 Q、K、V 向量的大小为 3)。因此,每个权重矩阵的形状都是 **4 x 3**(输入维度 4,输出维度 3)。
为了便于理解,我们使用下面这些权重矩阵:
```markdown
W_Q = | 1 0 1 |
| 0 1 0 |
| 1 0 0 |
| 0 1 1 |
W_K = | 0 1 0 |
| 1 0 1 |
| 0 0 1 |
| 1 1 0 |
W_V = | 1 0 0 |
| 0 1 0 |
| 0 0 1 |
| 1 1 0 |
```
现在,我们来计算 Q、K 和 V。
**计算 Q = X x W_Q:**
```markdown
Q = | 1.0 0.0 1.0 0.0 | | 1 0 1 |
| 0.0 1.0 0.0 1.0 | x | 0 1 0 |
| 1.0 1.0 0.0 0.0 | | 1 0 0 |
| 0 1 1 |
```
第一行("I"):**[1*1 + 0*0 + 1*1 + 0*0, 1*0 + 0*1 + 1*0 + 0*1, 1*1 + 0*0 + 1*0 + 0*1]** = **[2, 0, 1]**
第二行("love"):**[0*1 + 1*0 + 0*1 + 1*0, 0*0 + 1*1 + 0*0 + 1*1, 0*1 + 1*0 + 0*0 + 1*1]** = **[0, 2, 1]**
第三行("AI"):**[1*1 + 1*0 + 0*1 + 0*0, 1*0 + 1*1 + 0*0 + 0*1, 1*1 + 1*0 + 0*0 + 0*1]** = **[1, 1, 1]**
```markdown
Q = | 2 0 1 | ← "I"
| 0 2 1 | ← "love"
| 1 1 1 | ← "AI"
```
**计算 K = X x W_K:**
第一行("I"):**[1*0 + 0*1 + 1*0 + 0*1, 1*1 + 0*0 + 1*0 + 0*1, 1*0 + 0*1 + 1*1 + 0*0]** = **[0, 1, 1]**
第二行("love"):**[0*0 + 1*1 + 0*0 + 1*1, 0*1 + 1*0 + 0*0 + 1*1, 0*0 + 1*1 + 0*1 + 1*0]** = **[2, 1, 1]**
第三行("AI"):**[1*0 + 1*1 + 0*0 + 0*1, 1*1 + 1*0 + 0*0 + 0*1, 1*0 + 1*1 + 0*1 + 0*0]** = **[1, 1, 1]**
```markdown
K = | 0 1 1 | ← "I"
| 2 1 1 | ← "love"
| 1 1 1 | ← "AI"
```
**计算 V = X x W_V:**
第一行("I"):**[1*1 + 0*0 + 1*0 + 0*1, 1*0 + 0*1 + 1*0 + 0*1, 1*0 + 0*0 + 1*1 + 0*0]** = **[1, 0, 1]**
第二行("love"):**[0*1 + 1*0 + 0*0 + 1*1, 0*0 + 1*1 + 0*0 + 1*1, 0*0 + 1*0 + 0*1 + 1*0]** = **[1, 2, 0]**
第三行("AI"):**[1*1 + 1*0 + 0*0 + 0*1, 1*0 + 1*1 + 0*0 + 0*1, 1*0 + 1*0 + 0*1 + 0*0]** = **[1, 1, 0]**
```
V = | 1 0 1 | ← "I"
| 1 2 0 | ← "love"
| 1 1 0 | ← "AI"
```
现在,Q、K 和 V 矩阵都准备好了。
**注意:** 在真实模型中,这些权重矩阵不是手工设置的,而是在训练过程中学习得到的。这里为了便于理解,我们使用了简单数字。
到目前为止,我们已经学会了如何从输入创建 Q、K 和 V。现在进入下一步。
### 计算注意力分数(Q x K^T)
注意力公式里的第一步,是计算 **Q x K^T**。
这个结果表示每个词应该在多大程度上关注其他各个词。计算时,要把每个词的查询向量与其他每个词的键向量相乘。
首先,我们需要 **K^T**(K 的转置)。转置的意思很简单:交换行和列。第一行变成第一列,第二行变成第二列,依此类推:
```
K^T = | 0 2 1 |
| 1 1 1 |
| 1 1 1 |
```
现在,计算 **Q x K^T**:
```
Q x K^T = | 2 0 1 | | 0 2 1 |
| 0 2 1 | x | 1 1 1 |
| 1 1 1 | | 1 1 1 |
```
"I" 对所有词的注意力分数:
- "I" → "I":**2*0 + 0*1 + 1*1** = **1**
- "I" → "love":**2*2 + 0*1 + 1*1** = **5**
- "I" → "AI":**2*1 + 0*1 + 1*1** = **3**
"love" 对所有词的注意力分数:
- "love" → "I":**0*0 + 2*1 + 1*1** = **3**
- "love" → "love":**0*2 + 2*1 + 1*1** = **3**
- "love" → "AI":**0*1 + 2*1 + 1*1** = **3**
"AI" 对所有词的注意力分数:
- "AI" → "I":**1*0 + 1*1 + 1*1** = **2**
- "AI" → "love":**1*2 + 1*1 + 1*1** = **4**
- "AI" → "AI":**1*1 + 1*1 + 1*1** = **3**
```markdown
"I" "love" "AI"
Scores = | 1 5 3 | ← "I"
| 3 3 3 | ← "love"
| 2 4 3 | ← "AI"
```
这里可以看到,"I" 给 "love" 的注意力分数最高,为 **5**,意味着 "I" 认为 "love" 最相关。"love" 给所有词的分数都一样,都是 **3**。"AI" 给 "love" 的分数最高,为 **4**。
注意力分数的计算就到这里。现在进入缩放步骤。
### 缩放分数
现在,我们把每个分数都除以 **sqrt(d_k)**。
在这个例子里,**d_k = 3**(也就是键向量的维度),所以 **sqrt(3) = 1.732**。
问题来了:为什么要缩放?答案是:如果点积的值太大,下一步里的 softmax 函数会产生非常极端的值(接近 0 或 1)。这会让模型更难学习。缩放可以把数值保持在一个更容易处理的范围内。
```markdown
Scaled Scores = Scores / sqrt(3)
```
```markdown
"I" "love" "AI"
Scaled Scores = | 0.577 2.887 1.732 | ← "I"
| 1.732 1.732 1.732 | ← "love"
| 1.155 2.309 1.732 | ← "AI"
```
这里可以注意到,分数的相对顺序没有改变。"I" 仍然最关注 "love"。但数值变小了,这正是我们想要的。
以后,我计划写一篇文章,解释为什么选择除以 **sqrt(d_k)**,而不是其他因子。
接下来应用 softmax。
### 应用 softmax
现在,我们对每一行应用 **softmax** 函数。[Softmax](https://www.youtube.com/watch?v=2Zx6x01WwWM) 会把分数转换成概率,并且这些概率加起来等于 1。
一行里某个值 **x_i** 的 softmax 公式是:
```markdown
softmax(x_i) = e^(x_i) / sum(e^(x_j) for all j in that row)
```
这里,**e** 是数学常数(约等于 2.718)。不用太纠结这个公式。我们会一步步算出来。
**对 "I" 这一行:[0.577, 2.887, 1.732]**
- **e^0.577** = 1.781
- **e^2.887** = 17.940
- **e^1.732** = 5.651
- 总和 = 1.781 + 17.940 + 5.651 = 25.372
注意力权重:
- "I" → "I":1.781 / 25.372 = **0.070**
- "I" → "love":17.940 / 25.372 = **0.707**
- "I" → "AI":5.651 / 25.372 = **0.223**
**对 "love" 这一行:[1.732, 1.732, 1.732]**
所有值都相等,所以每个词获得相同的注意力:
- "love" → "I":**0.333**
- "love" → "love":**0.333**
- "love" → "AI":**0.333**
**对 "AI" 这一行:[1.155, 2.309, 1.732]**
- **e^1.155** = 3.174
- **e^2.309** = 10.063
- **e^1.732** = 5.651
- 总和 = 3.174 + 10.063 + 5.651 = 18.888
注意力权重:
- "AI" → "I":3.174 / 18.888 = **0.168**
- "AI" → "love":10.063 / 18.888 = **0.533**
- "AI" → "AI":5.651 / 18.888 = **0.299**
最终的**注意力权重矩阵**是:
```markdown
"I" "love" "AI"
Attention Weights = | 0.070 0.707 0.223 | ← "I"
| 0.333 0.333 0.333 | ← "love"
| 0.168 0.533 0.299 | ← "AI"
```
这里可以看到:
- **"I"** 把 70.7% 的注意力放在 **"love"** 上,22.3% 放在 **"AI"** 上,只有 7.0% 放在自己身上。
- **"love"** 对三个词的注意力相同(各 33.3%)。
- **"AI"** 把 53.3% 的注意力放在 **"love"** 上,29.9% 放在自己身上,16.8% 放在 **"I"** 上。
每一行加起来都是 1.0(也就是 100%)。这正是 softmax 所保证的。
现在,我们有了注意力权重。接下来计算最终输出。
### 计算最终输出(注意力权重 x V)
现在,我们把注意力权重矩阵和值矩阵 **V** 相乘,得到最终输出。
```markdown
Output = Attention Weights x V
```
```markdown
Output = | 0.070 0.707 0.223 | | 1 0 1 |
| 0.333 0.333 0.333 | x | 1 2 0 |
| 0.168 0.533 0.299 | | 1 1 0 |
```
**对 "I":**
- **0.070*1 + 0.707*1 + 0.223*1** = **1.000**
- **0.070*0 + 0.707*2 + 0.223*1** = **1.637**
- **0.070*1 + 0.707*0 + 0.223*0** = **0.070**
**对 "love":**
- **0.333*1 + 0.333*1 + 0.333*1** = **0.999**
- **0.333*0 + 0.333*2 + 0.333*1** = **0.999**
- **0.333*1 + 0.333*0 + 0.333*0** = **0.333**
**对 "AI":**
- **0.168*1 + 0.533*1 + 0.299*1** = **1.000**
- **0.168*0 + 0.533*2 + 0.299*1** = **1.365**
- **0.168*1 + 0.533*0 + 0.299*0** = **0.168**
```markdown
Output = | 1.000 1.637 0.070 | ← "I"
| 1.000 1.000 0.333 | ← "love"
| 1.000 1.365 0.168 | ← "AI"
```
这里可以看到,每个词现在都有了一个新向量,它是所有词的 V 向量的**加权组合**。"I" 得到的新表示受 "love" 的 V 向量影响很大(因为它对 "love" 的注意力权重最高,为 0.707)。这就是注意力机制的作用。
每个词的输出不再只与这个词本身有关。它现在包含了来自其他所有词的信息,并且这些信息会按相关性加权。这就是模型理解上下文和词与词之间关系的方式。
最终输出的计算就到这里。现在,把所有步骤串起来。
### 把所有步骤串起来
我们把整个计算过程集中总结一下:
**第 1 步:** 从输入嵌入向量(X)开始。
**第 2 步:** 用 X 乘以权重矩阵,得到 Q、K 和 V。
**第 3 步:** 用 Q 乘以 K 的转置(Q x K^T),计算注意力分数。
**第 4 步:** 除以 sqrt(d_k),对分数进行缩放。
**第 5 步:** 应用 softmax,得到注意力权重(概率)。
**第 6 步:** 用注意力权重乘以 V,得到最终输出。
整个过程可以写成一个公式:
```markdown
Attention(Q, K, V) = softmax(Q x K^T / sqrt(d_k)) x V
```
现在,这个公式的每一部分都清楚了。
这就是注意力机制中 Q、K 和 V 背后的数学。
每个大语言模型(LLM)的核心都会用到这套计算。为了便于理解,我们使用的是很小的数字;但在真实模型里,维度会大得多(512、1024 或更高),权重矩阵也是在训练过程中学习得到的。
现在,我们应该已经理解了注意力机制背后的数学:Q、K 和 V。
今天就到这里。
谢谢
**Amit Shekhar**
[Outcome School](https://outcomeschool.com/) 创始人
[**在这里阅读我的所有高质量博客。**](https://outcomeschool.com/blog)