(二十九)选定模型后单独评估测试集
# 续写同一文件:training_project/src/readingtorch/engine.py
def evaluate(checkpoint, data):
# 选定 best 后单独读取测试集,训练循环里没有查询测试指标。
saved = load_checkpoint(checkpoint)
task = saved["task"]
bundle = load_bundle(task, data, saved["seed"])
if saved["source_hash"] != bundle.source_hash:
raise ValueError("评估数据与训练记录不一致")
model = build_model(task)
model.load_state_dict(saved["model"])
loss_fn = nn.MSELoss() if task == "regression" else nn.CrossEntropyLoss()
scale = bundle.preprocess["y_scale"].item() if task == "regression" else 1.0
metrics = one_epoch(
model, make_loader(bundle, "test"), loss_fn, torch.device("cpu"), task, scale
)
if task == "regression":
# 标准化后的 0 对应训练目标均值,这就是不训练模型的基线。
metrics["baseline_rmse"] = (
bundle.y[bundle.splits["test"]].square().mean().sqrt().item() * scale
)
else:
metrics["baseline_accuracy"] = 1 / 3
return {
"task": task,
"checkpoint_epoch": saved["epoch"],
"samples": len(bundle.splits["test"]),
**metrics,
}评估读取选定检查点,建立同一结构并装回权重。数据摘要对应后,使用原来的划分种子与训练预处理,读取测试编号,调用不带优化器的 one_epoch。
回归还比较一份简单基线:始终预测训练目标均值。标准化后这个预测就是零,因此可以直接用测试目标与零的差计算基线 RMSE。三类图形数量相同,固定猜一个类别的准确率为三分之一。
先比较基线,能看清模型实际改善了多少;再检查错误样本,判断剩余误差发生在什么输入上。测试指标没有参与训练循环和最佳轮数选择。