TreeHeap 为什么会复读
C11 修复了一个重要问题:短输入后面不再铺满可见的 EOS,而是使用不可见 PAD。实验也证明,打乱或清空 source 会让 NLL 变差。因此,TreeHeap 的 H_state 确实携带了输入信号。
但 CLI 仍然出现了明显复读:
输入:为什么鲨鱼不坐沙发?
输出:我只想说,我只想说,我只想说……
输入:夜幕、星光、草木清香……
输出:一股香味,香味浓郁,香味浓郁……
第二个例子甚至有一点语义相关性:输入中的“清香”影响了输出中的“香味”。这说明模型不是完全忽略输入。但是,它只能释放一个很短的主题,随后便进入循环。
Houming818 提出了一个关键问题:
如果 decoder 真在递归生成一个 64-token 结构,为什么会如此稳定地把一个短句重复八次?这里会不会存在代码逻辑错误?
代码审计表明,这个怀疑是正确的。
1. 当前的 RecursiveDecoder 并没有递归消费 TreeHeap
当前 decoder 的单次 read() 确实会从 root 向下遍历 TreeHeap 的多个深度,并计算各层的加权 context。因此,把它称为“树上递归读取”并非完全错误。
问题在于:每生成一个新 token,read() 都重新执行,并重新初始化:
active = root
其实际流程是:
第 1 步:从 root 重新读取全树摘要 → token 1
第 2 步:从 root 重新读取全树摘要 → token 2
第 3 步:从 root 重新读取全树摘要 → token 3
……
它没有保存以下状态:
上一步走到哪个 subheap
哪些节点已经被消费
下一步应读取哪个地址
递归栈位于哪里
当前分支何时结束
所以,当前结构不是“跨 token 的递归解压”,而是:
同一棵 H_state
→ 每一步重新计算近似的全树摘要
→ GRU 根据上一个 token 生成下一个 token
TreeHeap 只参与了 context 的计算。输出过程本身仍然是一台普通的流式自回归生成器。
2. 为什么重复八次不是小概率事件
直觉上,连续八次生成同一个短句似乎概率很低。但 CLI 使用的是 greedy argmax,不是每一步独立随机抽样。
每一步执行的是一个确定函数:
$$ (h_t, y_{t-1}, C_t) \longmapsto y_t $$其中 $h_t$ 是 GRU hidden state,$y_{t-1}$ 是上一个 token,$C_t$ 是重新从同一棵 TreeHeap 读出的 context。
一旦系统进入类似下面的状态环:
“我只想说” → “,” → “我只想说”
下一轮输入状态就会再次接近上一轮。确定性 argmax 会把它送回同一个环。这叫周期吸引子。它不是八次巧合,而是一次进入循环后被稳定地重复。
因此,增加 max-output 只会给循环更多执行次数,不会增加 H_state 中可以依次解出的细节。
3. 256 个 leaf 在哪里
C11 的 256 个 leaf 属于输入 encoder:
最多 256 个 source piece
→ 256-leaf TreeHeap
→ FOLD
→ source H_state
当前 decoder 并没有一棵 256-leaf 输出树。它只是最多循环 128 次或 CLI 指定的 64 次。
这两个“长度”在概念上完全不同:
输入 leaf 数量:结构容量
输出循环次数:自回归执行次数
循环 64 次不等于解压了 64 个不同的 TreeHeap 地址。
4. Houming818 的新假设:先形成整体,再突然生成 string
Houming818 提出,TreeHeap 的输出未必应该是 stream。模型可能先形成一个完整的输出结构,再一次性坍缩为 string。
工程上可以把“突然生成”严格定义为:
source H_state
→ target H_state
→ 递归 UNFOLD
→ 全部 target leaf
→ 并行 token 概率
→ 完整 string
如果输出上限是 64 个 token,只需要 6 层展开:
1 → 2 → 4 → 8 → 16 → 32 → 64
若输出上限为 128,则需要 7 层。这不是在一个计算步骤中凭空出现文本,而是在 $\log_2 N$ 层结构计算后,并行得到 $N$ 个带地址的输出位置。
5. 完整 H_state 不是只有 root
TreeHeap encoder 的完整状态应写成:
$$ H = \left(r, \{d_k\}, \{g_k\}, \{m_k\}\right) $$其中:
| 符号 | 含义 |
|---|---|
| $r$ | root,全局压缩状态 |
| $d_k$ | 第 $k$ 层折叠留下的 detail |
| $g_k$ | 左右方向或手性 gate |
| $m_k$ | 节点是否存在的 mask |
只有 root 通常无法无损恢复全部 leaf。TreeHeap 的可逆性来自 root + details + gates,不是来自 root 独自记住整句话。
因此,新 decoder 不应直接让 root 输出所有 token,也不应反复读取同一个摘要。它应该先预测目标 TreeHeap 的完整状态:
$$ H_{target} = K_{\theta}(H_{source}) $$然后执行:
$$ Y = \operatorname{UNFOLD}(H_{target}) $$6. 不重新发明 split:复用现有 TreeHeap 逆运算
当前 encoder 已经有一套严格的 FOLD/UNFOLD 方程。给定 parent、detail 和 gate,可以恢复左右子节点:
$$ a = p - U(d) $$$$ \hat{b} = d + P(a) $$$$ left = g \cdot a + (1-g)\cdot\hat{b} $$$$ right = g \cdot\hat{b} + (1-g)\cdot a $$这里:
p:parent state
d:detail state
g:左右方向 gate
U:update kernel
P:predictor kernel
所以新 decoder 不需要手写一个任意的“父节点拆成左右节点”函数。它只需学习预测目标树的参数:
target_root = K_root(source H_state)
target_detail[k] = K_detail(
target_parent,
source_subheap,
source_root,
depth,
address
)
target_gate[k] = K_gate(...)
随后使用同一套 UNFOLD 代数确定性地得到左右子节点。这使 encoder 与 decoder 共享同一种 TreeHeap 数学协议。
7. 新 decoder 的最小版本
第一版先不解决可变长度,固定生成 128 个输出 leaf:
source tokens
→ source TreeHeap FOLD
→ source H_state
→ 预测 target root/details/gates
→ TreeHeap UNFOLD 七层
→ 128 个 target leaf
→ 共享 output head
→ 128 组 token logits
所有目标位置同时计算交叉熵:
$$ L_{token} = \frac{1}{128}\sum_{i=1}^{128} CE\left(W_o z_i, y_i\right) $$$z_i$ 是第 $i$ 个目标 leaf。梯度路径为:
token loss
→ target leaf
→ UNFOLD
→ target detail/gate/root predictor
→ source H_state
→ encoder FOLD
这就是 encoder 和 decoder 共同形成私有协议的可微路径。训练过程不需要把正确的前一个 token 喂给 decoder,因此也没有当前意义上的 teacher forcing 断层。
8. 为什么它可能减少复读
新结构中,第 17 和第 18 个 token 对应不同地址:
leaf[17] = root → left → right → ...
leaf[18] = root → left → right → ...
它们共享上层轮廓,但必须经过不同路径和 detail。后一个 token 不会因为前一个 token 是“我只想说”而再次收到同样的输入。
不过,非流式生成并不自动保证成功。如果所有预测 detail 都接近零,不同 leaf 仍可能坍缩成相似状态。因此,不能只看 NLL。
9. Claim、Predict 与 Proof
Claim
给定同一个 TreeHeap encoder,使用持久输出地址和代数 UNFOLD 的整体 decoder,应该比当前“每 token 重置 root”的 GRU decoder 更能保持输出位置差异,并降低自由生成中的周期重复。
Predict
如果 Claim 成立,应同时观察到:
- 自由输出的 token-level distinct-2/4 明显提高;
- 最长重复片段和重复运行长度下降;
- 打乱目标 leaf 地址后,NLL 显著升高;
- 清零不同深度的 target detail,会产生可重复但不同的损伤;
- leaf 间方差不坍缩为零;
- source shuffle 和 empty source 仍保持显著损伤;
- 相同训练数据、参数量和计算预算下,与旧 GRU decoder 正面对比。
Falsification
以下任一结果都会否定强 Claim:
UNFOLD decoder 仍产生相同短语循环;
打乱 leaf 地址几乎不影响 NLL;
不同 leaf 的状态趋于相同;
detail 清零没有损伤;
source shuffle 不再影响结果;
改进仅来自更多参数或更多计算。
第一轮实验只验证固定 128-leaf 输出。只有结构和自由生成闸门通过后,才增加节点 STOP/SPLIT 概率来学习可变长度,避免一次引入过多不确定变量。
10. 当前航行状态
C11 已经证明:修正 PAD 协议后,TreeHeap 的 source H_state 对目标概率存在因果影响。
C11 同时暴露:当前 decoder 只会反复读取全树摘要,并不沿 TreeHeap 递归解压输出位置。因此,“输入结构参与了计算”和“已经形成 TreeHeap decoder”是两个不同结论。
现在的修复航线是:
保留:现有 FOLD encoder 和 C11 source-conditioning 证据
删除:流式 GRU 作为最终 TreeHeap decoder 的假设
新增:source H_state → target H_state → UNFOLD → string
先测:固定长度、结构因果性、自由生成复读
后测:可变长度、翻译、对话和规模化训练
这不是把失败改名为新架构。恰恰相反,它来自 CLI 反例和代码审计:当前所谓“递归 decoder”只在一次读取内部递归,却没有在输出过程里递归。只有把输出地址、detail 和 UNFOLD 真正放进计算图,TreeHeap 才拥有与 FOLD encoder 对称的另一半。
License: GPLv3。本文的代码审计、H_state 整体解码方案、Claim/Predict/Falsification 与 SameTime/ARA 一并公开。