层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>
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""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()
|
||||
Reference in New Issue
Block a user