skip to content
tlj 的工程笔记

评估比训练更难:一次把 F1 从 58% 救回 73% 的审计复盘

/ 12 min read

Table of Contents

合规模型那段时间,我盯着一个数字发愁很久:F1 只有 58.8%,离 90% 的目标差了一大截。我换 prompt、改 reranker、清数据,折腾一圈也就抠回来几个点。

后来真正把它推到 73.2% 的,不是任何一次训练——模型权重一个 bit 没动。变的是我终于意识到:我一直在测的那个测试集,本身是错的。

这篇是关于”怎么知道自己的评估在骗自己”的复盘。我现在觉得,对一个微调项目,搭一套可信的评估,比再训一版模型难,也值钱

一、第一层:你以为在测模型,其实在测测试集

任务是多平台内容合规检测(小红书 / 抖音 / 视频号 / 公众号),给一段文案,模型要指出它违反了哪条平台规则、并引用违规片段。

测试集是这么造的:拿一条规则,让大模型写一段”违反这条规则”的文案,GT(标准答案)就标这一条规则。听起来很合理。问题是——一段真实的违规文案,往往同时违反好几条相似的规则,但 GT 只标了一条。

举几个真实样本:

文案GT 只标了模型还预测了两个都成立吗
用了精华三天皱纹全消夸张前后对比虚假误导信息都成立
刚从急诊室出来,我是追尾司机假冒热点当事人过度博取关注都成立
扫码进群,一起搞钱欺诈赌博信息站外导流都成立

模型多识别出来的那条,是真的违规,但因为 GT 没标,评估脚本把它算成了”误报”(False Positive),扣模型的精确率。

我统计了一下这个”冤枉”有多严重:被判成误报的样本里,平均 37% 其实是合理的多标签识别。规则越多的平台越夸张——视频号 31.5% 的样本确认是多标签,抖音 26%。

也就是说,58.8% 这个数字从一开始就是错的。它惩罚了模型做对的事。任务本质是 multi-label,而我用 single-label 的尺子在量。 改用审计修订过的测试集重测,真实 F1 是 73.2%。一个点的训练没做,凭空”涨”了 14.7。

这里第一个方法论:当模型表现差到不合常理时,先怀疑测试集,而不是先怀疑模型。 测试集的标注质量,是模型 F1 的天花板——你的 GT 有 5% 噪声,模型再强也撞不破那 5%。

二、第二层:审计本身不可信,所以让三家模型投票

发现偏置只是第一步。接下来要逐条判定:每一个被算成”误报”的预测,到底是”合理多标签”(该给模型加分),还是”真混淆”(模型确实认错了规则)。1460 条测试样本,挑出上千条争议 case,人工一条条看不现实。

我的第一版是用单个大模型当仲裁员,跑一遍。但很快就不放心——单一模型有它自己的系统性偏见,它说”这条算违规”,我凭什么信?用同一个模型跑三次也没用,那只是降低随机方差,纠不了它一以贯之的偏。

于是改成三厂商独立投票:DeepSeek、通义千问、Kimi 三家不同厂商的模型,对同一条 case、同一个 prompt、温度 0,各自独立判一次。规则是:

  • ≥2/3 一致才作为最终结论(多数票)
  • 1/3 平局的边界 case 单独标出来,交人工

为什么一定要三个不同厂商,而不是一个模型的三次采样:不同厂商的训练数据、对齐方式、价值取向都不一样,它们的偏见不相关。三个不相关的偏见取多数票,才能把单一模型的系统性错误投下去;同一个模型投三次,三票错的方向是一样的,投了等于没投。这是评估里”评委要互相独立”的朴素道理。

仲裁 prompt 我钉死了四条判断标准,逼模型按统一尺度判,而不是凭感觉:

  1. 引用片段必须是文案的精确子串
  2. 片段要直接、明确触发规则描述的违规行为
  3. 仅仅话题相关、领域相似 → 不算违规
  4. 规则禁止的是 A、但片段做的是 B → 不算违规

跑完之后,三家投票的结果反过来修订测试集:多数判”合理多标签”的,把这条违规补进 GT;多数判”真混淆”的,留下来作为模型真实的错误,单独提取成一张 confusion_map(哪条规则容易被错认成哪条),反哺后续训练。

(一个安全教训顺便记下:第一版仲裁脚本把云网关的 API key 直接硬编码在源码里——这是反面教材,凭证该走环境变量。)

三、第三层:连仲裁员也别全信——审计审计员

到这一步我本可以收工了。但有个不安:我那个仲裁 prompt 写得很严,会不会矫枉过正,把一些其实合理的多标签也判成了”真混淆”,反过来又冤枉了模型?

于是做了第三层验证:抽 40 条”严格标准下判为真混淆”的 case,换一套宽松标准重判一遍。结果吓我一跳——53.8% 在宽松标准下反转成了”合理”

这说明我的仲裁员标准偏严,73.2% 这个数字仍然在低估模型,真实合理 F1 大概在 73-80% 区间。

这一层我觉得是整套评估里最有价值的思维:任何一把尺子都有它的偏,包括你用来纠偏的那把。 你审计了模型,那谁来审计你的审计?做一次反向抽样(用相反方向的标准复核一批结论,看反转率),就能估出你这把尺子本身的系统误差有多大。反转率 53% 是个刺眼的信号——它告诉我”73.2% 是个下界,不是真值”。

四、诚实的部分:有些事我没做成

评估理清之后,结构性的瓶颈也暴露了。模型的推理 pipeline 是 scan → 分组 → reranker 每组选 1 条 → 输出,这个”每组只选 1 条”在多标签任务里必然漏召回——同一段文字同时违反两条规则时,reranker 只能挑一条。

我试着把单选改成多选,做了两版:

  • 多选 v1(宽松 prompt):精确率从 70% 暴跌到 41.6%,模型开始乱标。
  • 多选 v2(严格 prompt):精确率回到 53.2%,但整体 F1 还是低于单选基线。

两次都失败。结论很硬:prompt 工程已经到天花板,模型本身分辨”两条都对”和”只有一条对”的能力就是不够,这不是调 prompt 能解决的,得靠多标签数据重训。我没有把失败的实验藏起来——负面结果也是结论,它划清了”工程能修”和”得换方法”的边界。

五、到不了 90%,以及为什么

最后我给的结论是:在 24GB 单卡 + 现有数据的条件下,工程优化的天花板大约 75-78%,到不了 90%。原因分三层,每层都不是靠努力能绕过的:

  1. 硬件:24GB 只能 QLoRA 4-bit(带量化噪声)、batch 上不去、epoch 只能跑 1、Packing 直接 OOM。换 48GB 上 BF16 + FlashAttention 2,预期也就 +3-5pp。
  2. 任务定义:规则之间天然语义重叠(“诱导关注” vs “违规导流” vs “诱导点击” 边界本就模糊),连人类审核员之间的一致率都只有 90-95%。GT 的噪声就是模型的天花板。
  3. 数据规模:部分规则在训练集里只有 2-3 条样本,模型根本学不充分。

所以我给的不是”再努力一把冲 90%“,而是改评估指标:任务本质是 multi-label,就该用 multi-label F1 或 top-3 召回来量,而不是 single-label F1。换了指标,当前模型其实已经接近目标。指标定义错了,再怎么优化模型都是在错误的靶子上打靶。

小结

这个项目我学到最实的一课,不在训练里,在评估里:

  • 模型差到不合理时,先验尸测试集。 标注质量是 F1 的硬天花板。
  • 要纠偏,就让相互独立的多个评委投票——同一个模型投三次不算独立。
  • 连你的纠偏工具也要被审计:反向抽样估自己的系统误差。
  • 指标定义本身可能是错的:用 single-label 的尺子量 multi-label 的任务,再好的模型也会显得很差。

训练让模型学会一件事,评估决定你能不能看清它到底学没学会。后者错了,前者的努力全部白费。