M0 纯数学实验:先让 TreeHeap 成为工具箱

这篇文章记录一件更底层的事:

先不要直接做 WMT。
先证明 TreeHeap 作为数学对象能不能被操作。

WMT 是最终任务。

但它不是好的早期诊断工具。

因为如果直接训练翻译系统,最后 BLEU 没涨,我们很难知道失败来自哪里:

算子设计不对
世界模型没学到
loss 不合适
decoder 太弱
数据不够
训练没收敛
评估太粗

所以这次我们先建立一个更小的 ARA 主题:

ara/m0-treeheap-math/

它的目标不是证明 TreeHeap 会语言推理。

它只问一个问题:

TreeHeap 能不能先成为一个数学工具箱?

公开记录

公开实验仓:

https://github.com/houming818/sametime

对应 ARA 入口:

ara/m0-treeheap-math/logic/predicts.md
ara/m0-treeheap-math/logic/solution/algebra.md
ara/m0-treeheap-math/src/treeheap_math_probe.py
ara/m0-treeheap-math/evidence/treeheap_math_probe/summary.json

主仓内部路径:

/home/nio/log/ara/m0-treeheap-math/

为什么这是 toy 实验

toy 不是玩具。

toy 的意思是:

把问题缩小到刚好能看清楚。

如果我们要验证卷积核,第一步不会直接拿 ImageNet。

会先拿一个很小的矩阵:

1 0 1
0 1 0
1 0 1

看看核匹配到底能不能找到局部模式。

TreeHeap 也是一样。

这次我们不使用:

token
语法标签
WMT
BLEU
真实 checkpoint

只使用几个合成符号:

A, B, C, D, E, R, T

然后构造几棵小树:

H_ab = root(R, left=A, right=B)
H_ba = root(R, left=B, right=A)
H_cd = root(R, left=C, right=D)
H_nested = root(T, left=H_ab, right=E)

这个 toy 足够小。

小到我们知道正确答案。

也足够有用。

因为它能测试 TreeHeap 最基本的数学性质。

最小 TreeHeap 对象

第一版对象定义成:

H = (name, v, head_v, slot, q, children)

字段含义:

字段 含义
name 合成对象名字,比如 H_ab
v 整棵 TreeHeap 的结构向量
head_v root/head 参考向量
slot 当前节点的结构槽位
q 概率质量
children 子堆

这里最关键的是:

v != head_v

v 是整体结构坍缩后的向量。

head_v 是根节点自己的参考量。

这个区别是实验跑出来的,不是预先拍脑袋定的。

这次测试哪些工具

第一批工具箱:

compose
decompose
transpose
inverse_transpose
project
unproject
energy
match_subheap
probability container

对应要问的问题:

工具 问题
compose 两个子堆能不能合成一个合法 TreeHeap
decompose 合成后能不能拆回子堆
transpose 左右结构交换后是否仍然可追踪
inverse_transpose 转置两次能不能回来
project 降维后是否还保留结构排序
energy 正确结构和扰动结构能不能拉开距离
match_subheap 小子堆核能不能在大树里找到对应部分
probability container 匹配结果能不能形成稳定概率分布

非交换性:AB 不等于 BA

如果 TreeHeap 只是普通加法:

A + B = B + A

那它没法表达结构顺序。

所以我们给左右位置不同的结构基:

v(H) = normalize(root + L @ left + R @ right)

其中:

L != R

于是:

H_ab = root(R, left=A, right=B)
H_ba = root(R, left=B, right=A)

会得到不同向量。

这就像数字:

12 != 21

不是因为 1 和 2 变了。

而是因为位权变了。

实验结果:

noncomm_margin = 0.7117

这说明左右交换后,结构空间里确实拉开了距离。

第一次失败:只有 v 不够

第一版实现里,我只保存了整体向量:

H = (name, v, slot, q, children)

结果 transpose 出问题了。

我们希望:

inverse_transpose(transpose(H)) ~= H

但第一次跑出来:

transpose_inverse_error = 0.6248

这说明一个重要问题:

只有坍缩后的整体向量 v,不足以支持精确逆操作。

原因很直接。

当一棵树已经合成为整体向量后,这个整体向量混合了:

root
left
right

如果再拿它当 root 去重组,就会把整体误当成局部。

于是转置两次也回不来。

这不是坏事。

这是 toy 实验的价值。

它告诉我们 TreeHeap 对象定义漏了一个必要参考量。

修正:加入 head_v

修正后对象变成:

H = (name, v, head_v, slot, q, children)

其中:

head_v = root/head 自己的向量
v      = 整棵树合成后的向量

这样 transpose 时可以用原来的 head 重新组装,而不是把整体向量错当 root。

修正后:

transpose_inverse_error = 0.0

这个结论很重要:

TreeHeap 不能只是一个 collapsed vector。
它至少需要保留 root/head reference。

这也是从纯数学实验里得到的第一个设计约束。

子堆核匹配 toy

我们构造:

H_nested = root(T, left=H_ab, right=E)

也就是:

        T
       / \
    H_ab  E
    /  \
   A    B

然后用 kernel:

K = H_ab

H_nested 里找匹配。

结果:

subheap_hit_at_1 = 1.0
subheap_hit_at_3 = 1.0

匹配分数最高的是:

H_ab

对应概率:

0.9999798803

这说明在 toy 条件下:

match_subheap(H_nested, H_ab)

能找到正确子堆。

role swap 为什么看 margin

我们还测试了反向 kernel:

K = H_ba

H_nested 里并没有真正的 H_ba

这时系统仍然必须返回一个 top-1,因为概率容器总要在候选里排序。

它会把最接近的错误候选排前面。

所以不能只看:

top1 是谁

还要看分数差。

结果:

gold score = 1.0
role-swapped score on gold = 0.09085
role_swap_margin = 0.9091

这说明:

H_ab 和 H_ba 在结构空间里不是同一个东西。

这正是我们想要的。

本轮结果

实验命令在 ni 上执行,CPU 即可,不使用 GPU。

结果摘要:

pilot_pass = true
closure_ok = true
noncomm_margin = 0.7117
transpose_inverse_error = 0.0
compose_decompose_error = 0.0
projection_top1_preserved = true
projection_order_agreement = 0.8333
subheap_hit_at_1 = 1.0
subheap_hit_at_3 = 1.0
role_swap_margin = 0.9091
prob_mass_error = 0.0

对应证据文件:

ara/m0-treeheap-math/evidence/treeheap_math_probe/summary.json
ara/m0-treeheap-math/evidence/treeheap_math_probe/README.md
ara/m0-treeheap-math/evidence/treeheap_math_probe/matches.jsonl

这证明了什么

它证明了一个很小但很关键的点:

TreeHeap 可以先作为数学对象被操作。

至少在合成 toy 空间里,它支持:

闭包
非交换
精确转置逆
compose/decompose
投影保持
子堆核匹配
概率容器归一

这给下一步 Echo 提供了更干净的地基。

它没有证明什么

它没有证明:

TreeHeap 懂语言
TreeHeap 会翻译
TreeHeap 已经形成世界模型
SubHeap kernel 能处理真实句子
WMT 会涨分

这只是 M0。

但 M0 的意义是:

先把尺子做出来。

没有尺子,直接看 BLEU,就像还没造好温度计就讨论天气。

下一步

路线应该是:

M0 纯数学 TreeHeap
  -> M1 approximate inverse
  -> M2 TreeHeap-object echo
  -> M3 structure invariant
  -> S2 translation

下一步不应该直接回 WMT。

应该做:

exact inverse
approximate / learned inverse

也就是看看:

不直接保存 children 的时候,
模型能不能从 TreeHeap 表示里近似拆回结构。

如果 M1 过了,再做 Echo。

Echo 也不再是旧的 token echo。

而是:

TreeHeap object
operator transformations
reconstruct TreeHeap object

这样顺序更干净:

数学成立
信息可保留
结构可识别
语言再利用

License: GPLv3