堆路由——SPR 的矩阵化设计

本文是一个概念记录。当前无法实现——需要验证的组件太多。但它代表一个方向。

一、问题

Transformer 的输出层做了这件事:

hidden h (256 维) → 矩阵 O ∈ R^{32000×256} → softmax → argmax → 词

每一步都在问全部 32000 个词「是不是你」。32000 次内积,哪怕上下文已经把候选范围压缩到几个词。

能不能不这样?

二、SPR:用排除替代打分

2.1 物理图像

想象一个巨大的投票箱,32000 个槽。Transformer 走到每个槽前问「像不像」。SPR 不这样——它站在箱子前面,只看第一个槽:「在左边还是右边?」「左边」。然后看左边的第二排:「在左上还是左下?」「右上」。逐层排除,15 步走到一个具体槽。

这 15 步从来不问「你有多像」,只问「在不在这个方向」。

2.2 树的掩码表示

每个内部节点存一个布尔掩码——一个 0/1 的向量,标记哪些词在这个子树里:

                  root (mask: 1111...1111  全 1,所有词都在)
                 /      \
        左节点(A组)      右节点(B组)
       mask: 前半=1      mask: 后半=1
       /        \        /         \
      ...       ...     ...        ...

查询从根开始。每到一个节点,hidden 的一个维度跟节点的比较规则做一次判定——「去左还是去右」。错了的那一侧的所有词,从此不再考虑。

核心: 树不做「打分排名」,做「逐级排除」。每步砍掉一半候选词。

三、堆 = 天然的矩阵

3.1 堆数组存储

二叉树存成数组堆——根在 H[0],左子在 H[2i+1],右子在 H[2i+2]:

H = [root, L1, R1, LL2, LR2, RL2, RR2, ...]
      ↑     ↑   ↑    ↑    ↑    ↑    ↑
     idx0  1   2    3    4    5    6

连续的地址空间——GPU 一笔读出来 H[idx],O(1) 访问。32000 个内部节点,每个节点存一个 32000 位的掩码和一个比较规则,总共约 128MB——RTX 3090 的 24GB 装得下,还剩 200 倍空间。

3.2 推理 = 堆数组逐级查

def route(h):
    idx = 0
    for _ in range(K):        # K = log₂V ≈ 15
        node = H[idx]
        if h[某些维度] 满足 node.cmp:
            idx = 2*idx + 1   # 走左
        else:
            idx = 2*idx + 2   # 走右
    return leaf_word[idx]

每步:一次数组访问 + 一次比较 + 一次地址计算。15 步,不涉矩阵乘。

四、学习 = 堆调整

4.1 堆排序的类比

堆插入一个元素——先放最后,然后 percolate-up:每一步跟父亲比,比父亲小就交换,一直换到合适位置。

SPR 的学习也是 percolate:

堆操作 SPR 对应
percolate-up 新词出现 → 沿路上移
percolate-down 拓扑重组 → 子树交换
heapify 初始建堆(从数据统计批量建树)

**不需要梯度。**每个节点的掩码是一位比特——「这个词在不在这个子树里」。训练数据每出现一个新的(前缀,词)组合,就在相应节点的掩码上把那个词的位设成 1。没出现过的组合天然就是 0。

4.2 “不知道"的物理实现

前缀"哆啦A梦爱吃" → 沿树路径走到某个节点
  在此节点的掩码里: "铜锣烧"=1, "饭"=1
                    "樱木花道"=0, "孙悟空"=0
                    
掩码为 0 的词 → 从根开始就被排除 → 永远不可达

这不是「把概率设成 0」——是「从一开始就没有通路通往那个词」。

五、哈希碰撞

5.1 同路径异前缀

路径: root → L → RL → LLR
  德语 "die Katze" → 走这条路径
  英语 "the cat"   → 也走这条路径

两个不同语言的前缀在同一个节点上发生碰撞。该节点的掩码同时保持两个词的「可达」状态:

node[LLR].mask: ... "Katze"=1 ... "cat"=1 ...

碰撞不导致重组——只在该节点的掩码上多开一位。高频碰撞的节点自然拥有更大的孤岛。

5.2 孤岛

每个词的「孤岛」= 它的子树里所有掩码为 1 的词

已知知识 = 孤岛内的词
未知知识 = 孤岛外的词 → 从根开始就不可达

物理图像:不是「所有词的概率表」,而是一个连通图。你只能在当前孤岛里选词。通往岛外的路径不存在——不存在的东西不需要被否定。

六、与 Transformer 的关系

SPR 不是替代 Transformer——是替代 Transformer 的输出层

Transformer (不变)
  ├── Embedding
  ├── Self-Attention (Q·K^T)        ← 保留
  ├── Cross-Attention               ← 保留
  └── FFN (hash 碰撞引擎)            ← 保留
       │
       └── hidden h
            │
            ▼
       SPR 输出层(堆数组)            ← 只换这里
            │
            └── 词

分工: FFN 负责「撞出语义」→ hidden 里已经有丰富的上下文信息。SPR 只负责「路由到正确的词」——15 次堆查找替代 32000 次 softmax。

七、当前状态

组件 状态
线性回归砍半实验 ✅ 已验证——排除法可追平梯度
自适应砍半(方差分叉) ✅ 已验证——叶子效率 4x
堆矩阵化设计 📝 本文记录
Test 1: 孤岛可达性 (V=100) ⏳ 待做
Test 2: n-gram 前缀查询 (8K) ⏳ 待做
Test 3: 翻译对比 (8K) ⏳ 待做

May the Code be with us.


License: GPLv3