SPR Echo Test 全流程验证
从 hash map 到句子级递归二分树——echo test 的完整实验记录。
1. 什么是 echo test
训练一个翻译模型之前,先问一个更简单的问题:你能不能表示"输入等于输出"?
echo 就是自映射:f(x) = x。三种结构,三种实现方式:
| hash map | 邻接矩阵(TF) | 堆(SPR) | |
|---|---|---|---|
| 怎么做 | hash_map[k]=k |
Attn → 单位阵 I | 递归二分,路径即哈希 |
| 参数 | 0 | W_q,W_k,W_v,W_o | per-node 权重 + E |
| 核心机制 | 自碰撞 | 自环 | 确定性路由 + mean hash |
2. hash map echo 的三个威胁
碰撞覆盖:hash_map[A]=A 被 hash_map[B]=B(同桶)覆盖。不可压缩:每 token 独占一桶,V token=V 桶。不可泛化:新 token 没有桶。
这些问题不在 key 空间大小——在 hash 算法本身。hash_map 算法 = identity(token),天生不可压缩。SPR 堆算法 = semantic_route(token),相似 token 走同路径、共享叶子。
3. 堆 echo:递归二分,路径即哈希
整句 → 根节点 hash(整句组) → median 半分
├─ 前半组 → hash(前半) → median 半分
│ ├─ 子组...
│ └─ 子组...
└─ 后半组 → hash(后半) → median 半分
每个内部节点 hash 的是一个 token 组(子串),不是单个词。 每层节点存当前组的语义哈希(mean of token embeddings)。路径是 token 从整句到自身的分裂轨迹。
echo 验证:同 token → 同路径 → 确定性 100%。手工版(中位数阈值,per-node 随机权重)不需要训练即可通过。
4. 实验验证
代码:spr_echo_test.py,深度 4,15 节点/16 叶子,100 个随机 token。中位数分层二分。
| 断言 | 结果 |
|---|---|
| 同 token 路径一致 | ✅ 100/100 |
| 所有叶子有 token | ✅ 16/16 |
| 每叶 6-7 tokens(碰撞) | ✅ |
5. 自修复:sigmoid 离散路由
全 0 参数会产生鞍点(乘法链 gradient=0)。改用 sigmoid 离散路由:
每个节点:sigmoid(token @ W) → 右转概率
训练期:软概率(梯度可穿透 sigmoid)
推理期:sign(token @ W) → 硬方向
Adam 500 步 + 平衡正则(防止全挤一叶),结果:确定性 128/128、16/16 活跃、路径 100→117/128 匹配。
6. 词级 BLEU
每词独立路由(P7 简化版),per-node 随机权重 + 中位数二分构建树。叶子存 token 列表,输出最频繁词。
编解码
编码: token → E[token_id] (E: V×d embedding 矩阵,查行)
树: E[token] → 递归二分 → H_leaf = mean(token embeddings in leaf)
解码: H_leaf @ E^T → token_scores → argmax → output_token
同一份 E 双向用,和 TF 的 hidden → Linear(d,V) → softmax 同构。
结果
| 数据 | 词数 | 深度 | 叶子 | BLEU-4 | 训练 |
|---|---|---|---|---|---|
| 随机词 | 200 | 9 | 512 | 99.2% | 无 |
| WMT14 真实 | 3011 | 12 | 4096 | 100% | 无 |
独叶时(叶子数 ≥ 词数)BLEU=100%——中位数二分保证每词独占一叶,解码精确还原。这是数据结构的保证,不是训练结果。
7. 句子级递归二分(共享树 + E^T decode)
P8 做句子级修正:多句共享一棵树,每句 pad 到同长,token 以句组形式参与递归二分。
编码(句子级)
"the cat slept" → E[the], E[cat], E[slept], ∅×5 (pad to 8)
→ 整组进根 → median半分 → 递归
→ 叶子 H_leaf = mean(token embeddings in leaf)
解码: H_leaf @ E^T → output token
BLEU 退化曲线
WMT14 4 句,pad 到 8 token,26 个 unique 词。变深度测碰撞退化。
| depth | leaves | tokens/leaf | solo | BLEU-4 |
|---|---|---|---|---|
| 5 | 32 | 1.2 | 22 | 92.1% |
| 4 | 16 | 2.1 | 3 | 21.3% |
| 3 | 8 | 4.0 | 0 | 0.1% |
碰撞 → H_leaf 是多个 embedding 的均值 → 最近邻可能是第三个词 → BLEU 崩塌。
碰撞率 = 压缩率。 叶少则碰撞多、BLEU 低。这不是 bug,是参数压缩 vs 精度的 tradeoff。
8. Hash 函数设计:Merkle 组合
叶子的哈希不是从 token 重算——是递归组合子节点哈希:
H_leaf = E[token] # 叶子:embedding 查表
H_parent = combine(H_left, H_right) # 内部节点:组合子哈希
三种 combine 方案:
| 方案 | 公式 | 顺序敏感 | 参数 | 适用 |
|---|---|---|---|---|
| mean | (L+R)/2 | 否 | 0 | echo |
| weighted | αL+(1-α)R | 是(单 α) | 1/层 | 语序编码 |
| LSTM | LSTM_cell(L,R) | 是 | W_xh,W_hh,b | 可训练 |
验证(A-then-B vs B-then-A):
mean : H_AB == H_BA ← 顺序不敏感
weighted: H_AB != H_BA ← 顺序敏感
LSTM : H_AB != H_BA ← 顺序敏感(但随机权重无意义,需训练)
echo test 用 mean 就够了。翻译需要 order-aware。
8.1 零成本有序 hash:Cyclic Shift + 符号破缺
另一个船长的洞见:torch.roll 是零成本的——只是内存地址的重新映射。但纯循环位移有碰撞漏洞。
漏洞复现
纯 roll 在加法中会产生"巧合对冲":
"我打你": [1,2,3,4] + roll([5,6,7,8], 1) = [1+8, 2+5, 3+6, 4+7] = [9,7,9,11]
"你打我": [5,6,7,8] + roll([1,2,3,4], 1) = [5+4, 6+1, 7+2, 8+3] = [9,7,9,11]
碰撞!完全相同的哈希!
位移只是重排维度,加法中的交换律使相同元素对上后就无法区分顺序。
修复:符号交替破缺
右子树位移后,乘以交替正负号 [1, -1, 1, -1, ...]——一个位翻转操作,零成本破坏加法对称性:
H_parent = H_left + sign_alt(torch.roll(H_right, shifts=depth+1))
where sign_alt(x) = x * [1, -1, 1, -1, ...]
验证:
"我打你": [1,2,3,4] + [8,-5,6,-7] = [9, -3, 9, -3]
"你打我": [5,6,7,8] + [4,-1,2,-3] = [9, 5, 9, 5]
↑ 第二位永久分离!
分离 ✅ 确定性 ✅ 零硬件成本 ✅
| 方案 | 序敏感 | 碰撞 | 硬件成本 |
|---|---|---|---|
| mean | 否 | - | + |
pure torch.roll |
是 | 有!加法对冲 | 0 |
| roll + sign alt | 是 | 无 | 0 |
9. 训练路由:让碰撞语义化
node_W + E 联合训练可以把凝聚度从 0.21 推到 0.99+。但随机 embedding 下分组缺乏真实语义——训练方向正确,瓶颈在 embedding 质量。
| 版本 | 凝聚度 | 关键瓶颈 |
|---|---|---|
| echo(手工) | 0.21 | 无训练,纯哈希 |
| 训练(Adam+平衡) | 0.99 | 随机 E 无真实语义 |
| 目标(真嵌入) | TBD | 接 GloVe/Transformer embedding |
10. 当前状态
已验证
| 模块 | 状态 | 数据 |
|---|---|---|
| hash_map echo | ✅ | BLEU=100%(自碰撞) |
| 堆 echo(词级) | ✅ | BLEU=100%(中位数二分,每词独叶) |
| 句子级递归二分 | ✅ | BLEU=92→0%(碰撞退化曲线) |
| Hash 设计(Merkle + cyclic shift) | ✅ | mean/weighted/LSTM + roll, zero-cost |
| 有序 hash | ✅ | fwd≠rev,硬件零成本 |
| 训练路由(node_W+E) | ✅ | 凝聚度+350%,随机E缺语义 |
| 自修复(sigmoid 离散路由) | ✅ | 128/128 确定性 |
待突破
| 模块 | 状态 | 关键 |
|---|---|---|
| 真语义嵌入 | ⚠️ | 需接 GloVe/Transformer embedding |
| 翻译 decoder | ⬜ | 下一步 |
代码链路
spr_echo_test.py → spr_echo_repair.py → spr_hash_design.py
→ spr_p7_bleu.py → spr_p8_sentence.py → spr_007_train_nodeW.py
github.com/houming818/sametime/experiments/
当前完整代码链路:spr_echo_test.py → spr_echo_repair.py → spr_p7_bleu.py → spr_p8_sentence.py。
License: GPLv3 本文《SPR》系列采用 GPLv3 协议开源发布。