拓展阅读

手算注意力与因果掩码

计算稳定softmax、缩放点积注意力和Value加权。自检修改未来位置的Value,确认前面位置输出不受影响。

状态:自检已实际通过 · 环境:Python标准库 · 性质:合成/模拟教学实验
对应讲义 1.3 · 保存Python脚本

沿着对象与状态理解本章

先在可见性表里选一个查询行,决定哪些Key位置允许参与;再看如何缩放和混合。多头改变表示的分组方式,位置旋转改变Q/K的比较,反向传播计算参数的影响,三者不是同一个步骤。

下方图解的固定输入在“输入与适用条件”中声明。它用于解释本章机制,不与本章其他独立例子的数值暗中接续。

动态图解 · F12

Mask是可见性表,不是额外语义

怎样防止读到未来,或者把PAD读成内容?

把注意力的分数表看成一张“谁可以读谁”的座位表。行代表接收者,列代表提供者;禁止格子不能参与概率分母。因果限制、PAD限制可以同时存在,但一个负责时间顺序,一个负责有效内容,不能互相替代。

因果当前位置不读取将来位置。
加性mask允许处加0,禁止处用−∞等价地排除。
绝对位置加上历史前缀后的位置,而不是当前小批次中的行号。

输入与适用条件:前3帧用3个有效位置;第4帧是明确的屏蔽数值对照;第5帧另设past=3、new=2展示缓存偏移。图内始终标出当前条件。

1 / 5 · 处理绝对位置0

第1步,共5步

窄屏可左右滑动图形,或点击“放大图形”。文字解释紧随图后。

Mask是可见性表,不是额外语义:处理绝对位置0行:当前Query;列:可被读取的KeyK0K1K2Q0100Q1110Q2111现在读第0行:1=允许,0=禁止。

位置不是数字大小比赛。因果规则只允许当前查询读取自己和更早的Key;后面的位置即使已经在训练输入数组中,也不能读。

允许Key位置 ≤ 0

默认静止。用“下一步”看完解释再继续;播放到最后一步自动停止。键盘聚焦播放器后,可用左右键逐步查看、空格播放/暂停。图中颜色辅助区分,含义同时用文字注明。

为什么需要

防止训练偷看目标,并排除只为组批存在的填充内容。

怎样实现

先按实际位置和有效长度构造关系,再核对所用API布尔mask语义。

可能的代价与误解

不同API的True可能表示允许或禁止;全行无可见Key还需明确策略,不能直接做普通softmax。

如何判断是否做对

用能暴露未来泄漏的数据,比较正确掩码与故意写错偏移的结果。

怎样观察与验证

比较有无因果掩码,并改变分数差距。softmax([0,1,2])约为[0.0900,0.2447,0.6652];这不是事实置信度。

运行命令

python3 reading-notes/experiments/02_attention.py

完整教学代码

"""小矩阵注意力、稳定 softmax 与因果掩码;所有数据为人工构造。"""
import math


def softmax(values: list[float], temperature: float = 1.0) -> list[float]:
    if not values or not math.isfinite(temperature) or temperature <= 0:
        raise ValueError("需要非空分数和正有限温度")
    if any(math.isnan(x) or x == math.inf for x in values):
        raise ValueError("分数不可含 NaN 或正无穷")
    m = max(values)
    if m == -math.inf:
        raise ValueError("不能屏蔽全部候选")
    exps = [math.exp((x - m) / temperature) for x in values]
    total = sum(exps)
    return [x / total for x in exps]


def validate_matrix(a: list[list[float]], name: str) -> tuple[int, int]:
    if not a or not a[0] or any(len(row) != len(a[0]) for row in a):
        raise ValueError(f"{name} 必须是非空矩形矩阵")
    if any(not math.isfinite(x) for row in a for x in row):
        raise ValueError(f"{name} 必须全部有限")
    return len(a), len(a[0])


def attention(q, k, v, causal: bool = True):
    nq, dq = validate_matrix(q, "Q")
    nk, dk = validate_matrix(k, "K")
    nv, dv = validate_matrix(v, "V")
    if dq != dk or nk != nv or (causal and nq != nk):
        raise ValueError("维度不匹配;本演示因果模式要求相同序列长度")
    all_weights, outputs = [], []
    for i, query in enumerate(q):
        scores = [sum(a * b for a, b in zip(query, key)) / math.sqrt(dk) for key in k]
        if causal:
            scores = [s if j <= i else -math.inf for j, s in enumerate(scores)]
        weights = softmax(scores)
        all_weights.append(weights)
        outputs.append([sum(weights[j] * v[j][d] for j in range(nk)) for d in range(dv)])
    return all_weights, outputs


def self_test() -> None:
    assert abs(sum(softmax([1000, 1001, 1002])) - 1) < 1e-12
    q = k = [[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]
    v1, v2 = [[10.0], [20.0], [30.0]], [[10.0], [20.0], [9999.0]]
    w, a = attention(q, k, v1)
    _, b = attention(q, k, v2)
    assert a[:2] == b[:2], "未来 Value 不应影响前两个位置"
    assert w[0][1:] == [0.0, 0.0]
    assert w[1][2] == 0.0
    assert a[2] != b[2]


def run_demo() -> dict:
    q = k = [[1.0, 0.0], [0.0, 1.0], [1.0, 1.0]]
    weights, output = attention(q, k, [[10.0], [20.0], [30.0]])
    p = softmax([0, 1, 2])
    return {"性质": "原创小矩阵,无训练模型", "softmax_0_1_2": p,
            "加权10_20_30": sum(a*b for a,b in zip(p,[10,20,30])),
            "因果权重": weights, "输出": output}


if __name__ == "__main__":
    import json
    self_test()
    print(json.dumps(run_demo(), ensure_ascii=False, indent=2))

运行结果示例

以下结果由随包脚本产生。它们不是LLM或产品性能数据。

{
  "性质": "原创小矩阵,无训练模型",
  "softmax_0_1_2": [
    0.09003057317038046,
    0.24472847105479764,
    0.6652409557748218
  ],
  "加权10_20_30": 25.75210382604441,
  "因果权重": [
    [
      1.0,
      0.0,
      0.0
    ],
    [
      0.3302384506733431,
      0.6697615493266569,
      0.0
    ],
    [
      0.2482550782577231,
      0.2482550782577231,
      0.5034898434845538
    ]
  ],
  "输出": [
    [
      10.0
    ],
    [
      16.697615493266568
    ],
    [
      22.55234765226831
    ]
  ]
}

输入关键词即可检索。

↑ ↓ 选择 Enter 打开Esc 关闭 · 仅本地搜索

阅读设置

阅读设置仅保存在本机浏览器,不上传任何数据。