Files
ars-opd-rebuild/ars_opd/estimator.py
T
iomgaa 630a9c4636 层3 E2: estimator.py——式(4) 几何均值先验 chunk_prior + 式(5) 贝叶斯目标 bayesian_target,17 单测
对应 docs/04 §4 E2。detach 双防线(chunk_prior 内为本质防线、
bayesian_target 末尾为防御性第二道,分别对应参考 trainer:2196/2205);
log 域抗下溢;clamp 下限守定理 4.1(b);k_sem∈[0,N] 口径校验;
方差收缩 toy 模拟对拍 validate_chunk_mc_estimator.py 精神
(MSE_bayes < MSE_freq,含劣先验 ±0.1 稳健性)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 08:01:33 -04:00

90 lines
4.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""MC 估计与 Dirichlet 贝叶斯平滑——论文 §3.2.2 式(4)(5)。
把 similarity.py 产出的软计数 k_sem(外部 teacher 信号)与学生自身的
chunk 置信度 π̄(内部先验)融合成有界目标 π̂ ∈ (0, 1],供层 5 的 chunk
损失当乘子:loss_c = −π̂ · mean(log p)。定理 4.1 三性质由此获得:
(a) π̂ 有界 → 无白盒式(2) 的梯度爆炸;(b) π̂ > 0 → k_sem=0 也不塌缩;
(c) 先验收缩 → 方差小于频率估计 k/N。
纯逻辑模块(CLAUDE.md §2):只依赖 torch,toy 张量本地 CPU 可测。
"""
import torch
def chunk_prior(log_probs: torch.Tensor) -> torch.Tensor:
"""式(4):π̄ = exp((1/C)·Σ_t log p_t)——学生对整个 chunk 的几何均值置信度。
C 个 token 概率的几何均值,充当式(5) 的贝叶斯先验:teacher 采样(k_sem)
是主信号,π̄ 只是"学生自己觉得这段有多稳"的地板,防 k_sem=0 时目标归零。
参数:
log_probs: 学生对 chunk 内各 token 的对数概率,shape (C,),值 ≤ 0。
(调用方从 log_softmax 后 gather 标签位置所得,层 5 负责。)
返回:
π̄,标量张量 shape ()、值 ∈ [1e-8, 1],**不带梯度**。
实现细节:
- log 域先均值再 exp:直接连乘 C=50 个小概率会下溢
(50 个 0.01 → 1e-100,超出 fp32 下限 ~1e-38),log 域安全。
- detach 命门(参考实现 distillation_trainer.py:2196 同):π̄ 是学生
自身概率的函数,若保留梯度,优化器会发现"压低自己的 chunk 概率
→ π̄→0 → π̂ 变小 → 损失权重变小"这条逃逸路径——恰在 k_sem=0
(teacher 否定)的 chunk 上最有利可图,这些 chunk 最先塌缩。
π̄ 只能当常数先验,不能当优化变量。锁死断言见
tests/test_estimator_detach.py。
- clamp 下限 1e-8:极端负的均值 exp 后可能下溢为 0,而定理 4.1(b)
的反塌缩要求 π̄ 严格为正。差异标注:参考实现 clamp(1e-8, 1.0),
上限实为冗余——log p ≤ 0 ⇒ mean ≤ 0 ⇒ exp ≤ 1,此处省去。
"""
if log_probs.numel() == 0:
raise ValueError("log_probs 为空:chunk 至少要含 1 个 token")
log_pi_bar = log_probs.detach().mean() # (C,) -> ()
return log_pi_bar.exp().clamp(min=1e-8)
def bayesian_target(
k_sem: float,
pi_bar: torch.Tensor,
n_rollouts: int,
alpha: float,
) -> torch.Tensor:
"""式(5):π̂ = (k_sem + α·π̄) / (N + α)——chunk 接受概率的贝叶斯估计。
等价凸组合视角(论文式10):
π̂ = N/(N+α) · (k_sem/N) + α/(N+α) · π̄
即"teacher 频率估计"与"学生先验"的加权平均;默认 N=10、α=1 时权重
约 91% : 9%,teacher 主导,先验只兜底。
参数:
k_sem: 式(3) 的软匹配计数,∈ [0, N](aggregate_similarity 产出)。
pi_bar: 式(4) 的先验 π̄,标量张量(chunk_prior 产出)。
n_rollouts: teacher rollout 数 N。**必须等于算 k_sem 时的
len(teacher_rollouts)**——分子分母口径不一致会系统性偏移 π̂。
alpha: 先验强度 α ≥ 0。α=0 退化为频率估计 k/N(层 6 的
no_bayesian 消融,参考 config.py:315),失去定理 4.1(b) 保护。
返回:
π̂,标量张量 shape ()、值 ∈ [1e-8, 1],**不带梯度**。
实现细节:
- 差异标注:参考实现(distillation_trainer.py:2205)在此对 π̂ 整体
detach;我们的 π̄ 在 chunk_prior 内已 detach,此处的 detach 是
第二道防线——防止将来有人把带梯度的张量传进 pi_bar。
- clamp(1e-8, 1.0):下限防 α=0 且 k_sem=0 时 π̂=0(乘子归零则该
chunk 完全失去监督);上限防 pi_bar 越界传入时 π̂ 溢出概率语义。
"""
if n_rollouts < 1:
raise ValueError(f"n_rollouts 必须 ≥ 1,得到 {n_rollouts}")
if alpha < 0:
raise ValueError(f"alpha 必须 ≥ 0,得到 {alpha}")
if not 0.0 <= k_sem <= n_rollouts:
raise ValueError(
f"k_sem={k_sem} 越界 [0, {n_rollouts}]:检查是否与"
f" len(teacher_rollouts) 口径一致"
)
# 式(5): π̂ = (k_sem + α·π̄) / (N + α)
pi_hat = (k_sem + alpha * pi_bar) / (n_rollouts + alpha) # () -> ()
return pi_hat.clamp(1e-8, 1.0).detach()