验证集使用推理式验证(Sampling Validation):
- 从纯噪声
x = randn() * σ_max开始 - 执行多步迭代去噪(VE-EDM / DDIM)
- 最终得到
y0_hat与真实y0计算 MSE
- 口径不一致:训练用 teacher-forcing(从真实y₀加噪),验证用采样(从纯噪声)
- 损失不可比:训练loss含λ(σ)加权,验证loss是纯MSE
- 监控失效:无法及时发现模型去噪能力退化、过拟合等问题
- 概念混淆:把"推理质量评估"当成了"训练验证"
扩散模型的训练/验证和推理是两类不同的事:
| 阶段 | 目的 | 输入来源 | 是否需要y₀ |
|---|---|---|---|
| 训练 | 学习去噪能力 | y₀ + σ·ε | ✅ 是 |
| 验证 | 测量去噪能力 | y₀ + σ·ε | ✅ 是 |
| 推理 | 应用去噪能力 | 纯噪声(多步) | ❌ 否 |
验证集必须使用真实y₀构造x_t,这不是"作弊",而是标准做法(teacher-forced validation)。
完全复制训练流程,只是不更新梯度:
def evaluate(model, loader, device, args):
"""去噪验证:与训练完全对齐"""
for batch in loader:
X, Y = batch # Y是真实y₀
# 1. 采样σ ~ LogUniform(σ_min, σ_max)
log_sigma = torch.rand(B) * (log_σ_max - log_σ_min) + log_σ_min
sigma = torch.exp(log_sigma)
# 2. 从真实y₀构造x_t(关键!使用真实标签)
eps = torch.randn_like(Y)
x_t = Y + sigma * eps
# 3. EDM预条件化
c_in = 1.0 / sqrt(σ² + σ_data²)
c_skip = σ_data² / (σ² + σ_data²)
c_out = σ·σ_data / sqrt(σ² + σ_data²)
x_scaled = c_in * x_t
f_θ = model.forward_score(X, x_scaled, t_idx)
ŷ₀ = c_skip * x_t + c_out * f_θ
# 4. 计算损失(与训练一致,含λ加权)
loss = MSE(ŷ₀, Y)
if edm_lambda:
λ = (σ² + σ_data²) / (σ·σ_data)²
λ = λ / λ.mean() # batch归一化
loss = λ * loss
# 5. mask机制(覆盖率权重)
loss = apply_coverage_mask(loss, mask)- 移除:所有采样逻辑(VE-EDM、DDIM、multi-step)
- 新增:与训练对齐的单步去噪验证
- 保留:统计指标计算(corr、rank_ic、hit_rate等)
移除(不再需要):
--sampler:验证采样器类型--steps:多步采样步数--use_yz_cond:验证是否启用yzvol
原因:去噪验证直接从训练参数读取σ分布、yzvol配置等。
# 旧的(混淆)
print(f"val_loss={val_loss:.6f}") # 不清楚是什么loss
# 新的(清晰)
print(f"val_denoise_loss={val_loss:.6f}") # 明确是去噪验证loss
print(f"[denoise_val] corr={r:.6g} rank_ic={rank_ic:.6g}")# 验证入口
print("[Validation] 去噪验证 (denoising validation, teacher-forcing)")
val_metrics = evaluate(model, val_loader, device, args)
# Early stopping 使用去噪验证loss
if val_loss < best_val_loss:
best_val_loss = val_loss
save_checkpoint(...)去噪验证只测量去噪能力,真实推理质量需要:
# backtest.py
predictions = ve_edm_samples( # 从纯噪声多步采样
model, X, num_samples=100, steps=10
)
# 计算业务指标:Sharpe、IC、收益等职责分离:
train.py的evaluate():监控模型学习状态(去噪验证)backtest.py:评估推理质量(完整采样)
- 扩散模型训练目标:学习
p(y₀|x_t, σ),即"给定噪声水平,从x_t恢复y₀" - 验证应该测量:模型是否学会了这个映射
- 如何测量:用相同的数据生成方式(y₀ + σ·ε)测试预测精度
类比:
- 语言模型训练用teacher forcing,验证也用teacher forcing计算perplexity
- 推理时才用自回归生成
扩散模型也是同理:
- 训练/验证:teacher-forcing(从y₀构造x_t)
- 推理:自由生成(从纯噪声逐步去噪)
| 项目 | 旧方式(错误) | 新方式(正确) |
|---|---|---|
| 验证方式 | 采样验证(从纯噪声) | 去噪验证(从y₀+噪声) |
| 与训练一致性 | ❌ 不一致 | ✅ 完全一致 |
| loss可比性 | ❌ 不可比 | ✅ 可直接对比 |
| 监控过拟合 | ❌ 滞后 | ✅ 及时 |
| 推理质量评估 | ❌ 间接 | 通过回测单独评估 |
| 代码复杂度 | 高(多采样器) | 低(单一流程) |
"验证集用y₀构造x_t"不是作弊
这是扩散模型的标准验证方式,目的是测量去噪能力,不是测量推理质量。
推理质量需要通过回测单独评估,那才是真正的"模型能不能做预测"。
✅ train.py:
evaluate()函数完全重写- 移除采样器相关参数
- 更新日志输出标识
- 保持early stopping逻辑不变(用去噪loss)
❌ 不需要修改:
backtest.py:推理采样逻辑保持不变model.py:模型结构无变化config.json:验证相关参数已不使用
- 训练更稳定:验证loss与训练loss口径一致,学习率调度更准确
- 问题发现更快:过拟合、崩塌等问题能及时在验证loss中体现
- 代码更清晰:职责分离,训练验证是一套,推理回测是另一套
- 符合规范:与学术界标准做法一致(EDM、DDPM等论文都这么做)
生成时间:2025-01-XX 版本:v1.0