博客 注意力机制原理

注意力机制原理

eric_zhang 2026-06-10 8 阅读

一、注意力机制简述:

假如我要在教室里找一个人:
1. 首先我需要在心里想一下这个人的特征(查询Q),
2. 然后我开始扫视全班的学生(所有的键K),
3. 然后根据匹配程度给每个人打个分(注意力分数),
4. 最后我根据分数高低,从他们身上获取对应的信息(值V),分数越高,我从那个人那里听来的话就越重要

 

总结来说:
Q(查询):就是我心里想找的那个“目标特征”
K(键):等同于每个同学身上贴的“标签”
V(值):就是每个同学实际能提供的“信息”

 

二、举个例子

1. 现在用 “我爱你” 这句话举例,假设每个字用一个二维向量表示,那“我爱你”就可以表示为:

X = [[1.0, 0.0],

     [0.5, 0.5],

     [0.0, 1.0]]

 

2. 然后有三个矩阵W_Q, W_K, W_V,如下:

W_Q = [[0.5, 0.2],

       [0.1, 0.6]]

W_K = [[0.4, 0.3],

       [0.2, 0.7]]

W_V = [[0.8, 0.1],

       [0.3, 0.9]]

 

3. 接下来用矩阵乘法分别计算出Q, K, V
Q = X @ W_Q

K = X @ W_K

V = X @ W_V

这就是我们要求的Q, K, V了。

 

4. 计算注意力分数(Q乘以K的转置)

Scores = Q @ K.T

 

5. 缩放(Scores 除以d_k开方)

Scaled = ​Scores / d_k^0.5

这里的d_k和数据的维度有关

 

6.通过Softmax计算出注意力权重

attention_weights = Softmax(Scaled)

 

​7. 计算最终结果

output = attention_weights @ V

output就是最终的结果,它是输入 “X” 的新的表示

本例子中output为:

[[0.551, 0.498],

 [0.544, 0.509],

 [0.537, 0.521]]

 

每个词经过自注意力后,其新表示(Output 的每一行)都变成了整个句子中所有词 V 的加权混合。

例如“我”原本是[1,0],现在变成了[0.551,0.498],包含了“爱”和“你”的信息。

“你”也从[0,1]变成了[0.537,0.521]。

这种融合使得每个词都具有了上下文语义,这正是注意力机制的作用。

 

三、具体代码实现

import numpy as np
 
# ------------------------------
# 1. 输入数据:三个词“我”,“爱”,“你”
#    每个词用一个 2 维向量表示(为了手算方便)
# ------------------------------
X = np.array([
    [1.0, 0.0],   # “我”
    [0.5, 0.5],   # “爱”
    [0.0, 1.0]    # “你”
])
 
# ------------------------------
# 2. 定义投影矩阵 W_Q, W_K, W_V(可训练参数)
#    这里手动给出数值,模拟已经训练好的状态
# ------------------------------
W_Q = np.array([
    [0.5, 0.2],
    [0.1, 0.6]
])
 
W_K = np.array([
    [0.4, 0.3],
    [0.2, 0.7]
])
 
W_V = np.array([
    [0.8, 0.1],
    [0.3, 0.9]
])
 
# ------------------------------
# 3. 线性投影:计算 Q, K, V
#    Q = X · W_Q,  K = X · W_K,  V = X · W_V
#    输出形状均为 (3, 2)
# ------------------------------
Q = X @ W_Q
K = X @ W_K
V = X @ W_V
 
print("=== 步骤1: 计算 Q, K, V ===")
print("Q (查询矩阵):\n", Q)
print("K (键矩阵):\n", K)
print("V (值矩阵):\n", V)
 
# ------------------------------
# 4. 计算原始注意力分数 Scores = Q · K^T
#    K^T 形状 (2, 3)
#    Scores 形状 (3, 3)
# ------------------------------
scores = Q @ K.T
print("\n=== 步骤2: 原始注意力分数 Scores ===")
print(scores)
 
# ------------------------------
# 5. 缩放:除以 sqrt(d_k)
#    d_k = 2, sqrt(2) ≈ 1.41421356
# ------------------------------
d_k = Q.shape[1]   # 此时 d_k = 2
scaled_scores = scores / np.sqrt(d_k)
print("\n=== 步骤3: 缩放后的分数 Scaled Scores ===")
print(scaled_scores)
 
# ------------------------------
# 6. Softmax 归一化(按行),得到注意力权重
#    自己实现 softmax 避免使用 scipy
# ------------------------------
def softmax(x, axis=-1):
    # 减去最大值防止指数爆炸
    e_x = np.exp(x - np.max(x, axis=axis, keepdims=True))
    return e_x / np.sum(e_x, axis=axis, keepdims=True)
 
attention_weights = softmax(scaled_scores, axis=-1)
print("\n=== 步骤4: 注意力权重矩阵 (每行和为1) ===")
print(attention_weights)
 
# ------------------------------
# 7. 加权求和:output = attention_weights @ V
#    输出形状 (3, 2)
# ------------------------------
output = attention_weights @ V
print("\n=== 步骤5: 最终输出 Output (每个词融合全局信息后的新表示) ===")
print(output)
 
# 附加验证:检查每行注意力权重之和是否为1
row_sums = np.sum(attention_weights, axis=1)
print("\n=== 检查每行权重和 ===")
print(row_sums)

输出:

=== 步骤1: 计算 Q, K, V ===
Q (查询矩阵):
 [[0.5 0.2]
 [0.3 0.4]
 [0.1 0.6]]
K (键矩阵):
 [[0.4 0.3]
 [0.3 0.5]
 [0.2 0.7]]
V (值矩阵):
 [[0.8  0.1]
 [0.55 0.5]
 [0.3  0.9]]
 
=== 步骤2: 原始注意力分数 Scores ===
[[0.26 0.25 0.24]
 [0.24 0.29 0.34]
 [0.22 0.33 0.44]]
 
=== 步骤3: 缩放后的分数 Scaled Scores ===
[[0.18384776 0.1767767  0.16970563]
 [0.16970563 0.20506082 0.24041631]
 [0.15556349 0.23334524 0.31112698]]
 
=== 步骤4: 注意力权重矩阵 (每行和为1) ===
[[0.33614837 0.33310516 0.33074647]
 [0.32184448 0.33343385 0.34472167]
 [0.30815179 0.33241613 0.35943208]]
 
=== 步骤5: 最终输出 Output (每个词融合全局信息后的新表示) ===
[[0.55158713 0.49825904]
 [0.5443457  0.50918687]
 [0.53701315 0.52096187]]
 
=== 检查每行权重和 ===
[1. 1. 1.]<em id="__mceDel"> </em>

发表评论

评论列表

评论 0

暂无评论

来抢个沙发,成为第一个评论的人吧~

0%
❄️