【Bug已解决】Mean Token Accuracy not found at evaluation while finetuning an model with SFTTrainer 解决方案

【Bug已解决】Mean Token Accuracy not found at evaluation while finetuning an model with SFTTrainer 解决方案
【Bug已解决】Mean Token Accuracy not found at evaluation while finetuning an model with SFTTrainer 解决方案原始报错Mean Token Accuracy not found at evaluation while finetuning an model with SFTTrainer 场景用 SFTTrainer 微调时想在评估阶段看到一个自定义指标平均 token 准确率mean token accuracy预测 token 与标签 token 一致的比例。但评估跑完日志里只有 loss没有这个指标——训练器只算默认的 eval_loss没有把平均 token 准确率接进评估指标计算。用户期望它能像 loss 一样自动出现在评估报告里。正确做法是把自定义指标注册/实现到评估逻辑中让它在每个 eval batch 上累积、最终平均。 关键词自定义指标、mean token accuracy、评估指标、metric 注册、SFTTrainer、compute_metrics、准确率累积、评估钩子。一、现象长什么样想要的指标评估里没有用户定义了平均 token 准确率作为关心的指标SFTTrainer 的评估默认只算eval_loss其它指标需要用户通过compute_metrics提供用户没接或接错了评估结束只有 loss没有 token 准确率用户以为训练器应该自带这个常见指标发现没有就以为坏了即使接了compute_metrics如果只在完整 batch 上算、没做跨 batch 累积平均最后报的值也会错被最后一个 batch 覆盖或未被正确聚合表现评估报告缺指标或指标值看着不对。核心问题自定义评估指标没有被正确注册与跨 batch 累积要么没接compute_metrics要么累积方式错导致最终值失真。二、背景评估指标是怎么算出来的Trainer 类含 SFTTrainer的评估流程是对每个 eval batch 跑前向得到logits和labels如果用户提供了compute_metrics(eval_pred)就用(logits/labels)调它返回一个指标字典Trainer 在一个 batch上调用compute_metrics得到该 batch 的指标多个 batch 的指标怎么聚合成整个 eval 集的指标取决于接口约定——很多实现是把所有 batch 的预测先拼成大数组再调一次compute_metrics这样你只需在全量上算一次即可。平均 token 准确率的正确算法是(predicted_token label_token 且 label ! -100).sum() / (label ! -100).sum()即在所有非填充标签 token 上求一致率。如果compute_metrics收到的是全量拼接的预测与标签一次算对即可如果收到的是单 batch则必须自己累积计数、最后除总数。三、根因指标未注册或累积方式错根因拆解未接 compute_metrics用户没传compute_metrics训练器只报 loss只算最后一个 batch有人在每个 batch 调compute_metrics却没聚合最终值被最后 batch 覆盖忽略 -100 标签算准确率时没排除labels-100的填充位分母错argmax 轴错对 logits 取argmax(-1)的维度错预测 token 不对指标名不一致用户找mean_token_accuracy但代码记的是acc对不上无注册机制没有指标注册表加指标要改评估核心。下面用最小模型复现只算 loss 没有准确率再给compute_metrics 正确累积的修复。四、最小可运行复现import torch def eval_loss_only(logits, labels): 错误评估只算 loss没有准确率指标。 return {eval_loss: 0.5} # 用户想要的 token 准确率根本没出现 if __name__ __main__: logits torch.randn(2, 3, 10) # (batch, seq, vocab) labels torch.tensor([[1, 2, -100], [3, -100, -100]]) print(评估结果:, eval_loss_only(logits, labels)) # 只有 loss运行可见评估结果只有 loss没有 token 准确率——用户想要却找不到的现场。五、方案用 compute_metrics 算全量 token 准确率第一层实现compute_metrics在全量拼接的logits/labels 上算平均 token 准确率排除 -100 标签位import torch def compute_metrics(eval_pred): logits, labels eval_pred if isinstance(logits, torch.Tensor): logits logits preds logits.argmax(dim-1) # (N, seq) 预测 token # 只在非填充标签(-100)上比较 mask labels ! -100 correct (preds labels) mask acc correct.sum().item() / mask.sum().item() return {mean_token_accuracy: acc} if __name__ __main__: logits torch.randn(2, 3, 10) labels torch.tensor([[1, 2, -100], [3, -100, -100]]) # 构造 eval_pred实际中 Trainer 会拼全量后传入 print(正确指标:, compute_metrics((logits, labels)))排除 -100 后准确率分母是真实标签 token 数指标正确且会被 Trainer 上报。六、方案逐 batch 累积计数最后聚合防御单 batch 调用第二层若接口是逐 batch 调 compute_metrics而非全量一次则需自己累积正确数/总数用闭包状态跨 batch 聚合class TokenAccuracyAccumulator: def __init__(self): self.correct 0 self.total 0 def update(self, logits, labels): preds logits.argmax(dim-1) mask labels ! -100 self.correct ((preds labels) mask).sum().item() self.total mask.sum().item() def result(self): return {mean_token_accuracy: self.correct / self.total if self.total else 0.0} if __name__ __main__: acc TokenAccuracyAccumulator() for _ in range(3): # 模拟多个 eval batch acc.update(torch.randn(2, 3, 10), torch.tensor([[1, 2, -100], [3, -100, -100]])) print(跨 batch 聚合准确率:, acc.result())累积计数保证多 batch 平均正确不被最后 batch 覆盖。七、方案指标注册表按需启用第三层用注册表管理可选指标评估时按配置启用新增指标只注册不改核心METRICS {} def register_metric(fn): METRICS[fn.__name__] fn return fn register_metric def mean_token_accuracy(logits, labels): preds logits.argmax(dim-1) mask labels ! -100 return (preds labels)[mask].float().mean().item() def evaluate_selected(logits, labels, names): return {n: METRICS[n](logits, labels) for n in names} if __name__ __main__: out evaluate_selected(torch.randn(2, 3, 10), torch.tensor([[1, 2, -100], [3, -100, -100]]), [mean_token_accuracy]) print(注册表指标:, out)注册表让指标可插拔评估核心不硬编码任何具体指标。八、验证把准确率正确且可累积锁进测试def test_accuracy_excludes_pad(): logits torch.zeros(1, 2, 5) logits[0, 0, 1] 10; logits[0, 1, 2] 10 # 预测 token 1 和 2 labels torch.tensor([[1, 2]]) # 全对 assert compute_metrics((logits, labels))[mean_token_accuracy] 1.0 def test_accuracy_with_pad(): logits torch.zeros(1, 3, 5) logits[0, 0, 1] 10; logits[0, 1, 9] 10 # 第2个预测错(9) labels torch.tensor([[1, 9, -100]]) # 第3个是填充 # 2 个有效标签1 对 - 0.5 assert compute_metrics((logits, labels))[mean_token_accuracy] 0.5 if __name__ __main__: test_accuracy_excludes_pad() test_accuracy_with_pad() print(token 准确率指标测试通过。)九、排查清单评估找不到 token 准确率按顺序查未接 compute_metrics是否传了compute_metrics没传则只有 loss。标签掩码算准确率是否排除 labels-100 填充位没排除分母错。argmax 轴是否对 logits 最后一维 argmax轴错预测 token 不对。单 batch 覆盖逐 batch 调 compute_metrics 是否累积否则被最后 batch 覆盖。指标名日志找的指标名是否与代码返回的一致mean_token_accuracy注册机制是否有指标注册表没有则加指标要改核心。全量 vs 单批接口是传全量还是单批决定用一次算还是累积。十、小结SFTTrainer 评估找不到平均 token 准确率是自定义评估指标没被注册与正确累积训练器默认只报 eval_losstoken 准确率需要用户用compute_metrics提供且必须排除 -100 填充标签、正确跨 batch 聚合否则要么指标缺失、要么值失真。修复三层compute_metrics在全量logits/labels 上算平均 token 准确率排除 -100累积计数逐 batch 调用时用累加器跨 batch 聚合不被末批覆盖指标注册表可选指标注册管理评估按配置启用新增不改核心。核心原则评估想要自定义指标如 mean token accuracy必须由compute_metrics提供且严格排除填充标签-100、正确跨 batch 聚合。凡是评估报告里找不到某指标的情况第一反应都是去确认 compute_metrics 是否实现、是否排除了填充、是否做了跨 batch 平均。

最新新闻

日新闻

周新闻

月新闻