自己攒一套模型评测:40 道题、两个脚本、一个 CSV
不抄别人的排行榜。用一晚上攒一套属于自己的评测,之后每次换模型都能省掉半天争论。
选模型这件事,我一开始完全靠"看别人评测 + 自己聊两句"。前者是别人的任务分布,后者是我的印象分,两个都不牢靠。后来一晚上攒了套最小评测,之后每次选型都靠它。
一、为什么公开的排行榜帮不上忙
三个原因,我每个都实际吃过亏:
- 题目泄漏。公开基准的题目很可能在训练数据里。分数高,可能只说明它见过。
- 任务分布不同。通用基准里数学和代码占了不少比重,而我实际 80% 的调用是从中文材料里抽取结构化字段。这两件事的能力相关性没我想象中高。
- 判分方式不同。同一份输出,按"包含关键词"判和按"结构合法"判,得分可以差出十几分。
结论不是"评测没用",而是你自己的 40 道题,比任何排行榜都更接近你的真实场景。
二、评测集怎么攒
我用了 40 道题,分四类,每类 10 道:
- 抽取:给一段文本,输出规定的 JSON 字段。判分客观——结构合法、字段值逐条比对。
- 归纳:给一段长材料,回答三个指定问题。判分要点清单:每个必答点是否出现。
- 改写:给定风格约束重写一段文字。判分只能人工,但约束违反项可以自动查(比如"每句不超过 25 字")。
- 拒答:给材料里没有答案的问题。判分客观——正确行为是回答"材料未提及",凡是给出了具体数值的都算错。
前三类大家都会做,第四类我强烈建议加上。它测的是编造倾向,而编造是我实际工作里代价最高的失败模式。
题目从我的真实历史任务里挑,不做修饰。每道题旁边写"标准答案 + 判定依据",全部存进一个 CSV,进 git。这一步是最有价值的资产——半年后我换模型,直接重跑同一份题。
三、脚本:两个文件,加起来不到 200 行
run.mjs 负责打题、落盘原始响应;score.mjs 负责判分。核心是"原始响应必须留档"——不然你事后发现判分逻辑有 bug,题就得重跑,钱和配额都白花。
// run.mjs —— 逐题请求,原始响应按题号落盘
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
const rows = readFileSync('set.csv', 'utf8').trim().split('\n');
const head = rows.shift().split(',');
const items = rows.map((r) => Object.fromEntries(head.map((k, i) => [k, r[i]])));
mkdirSync('raw', { recursive: true });
for (const it of items) {
const body = {
model: 'qwen2.5-7b-instruct',
messages: [{ role: 'user', content: it.prompt }],
temperature: 0,
max_tokens: 800,
stream: false,
};
const t0 = Date.now();
const res = await fetch('http://127.0.0.1:11434/v1/chat/completions', {
method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body),
});
const j = await res.json();
const ms = Date.now() - t0;
writeFileSync(`raw/${it.id}.json`, JSON.stringify({
ms, usage: j.usage, content: j.choices?.[0]?.message?.content ?? null,
}, null, 2));
console.log(it.id, ms + 'ms');
}
温度钉死为 0,是为了让失败可归因。温度不为 0 时同一道题时时对时错,判分就变成了测运气。想评估稳定性,就把同一道题跑 5 次看一致率,那是另一项指标,不要混在准确率里。
四、判分规则要能复述
score.mjs 里我坚持一条:任何一条打分规则,我都要能用一句话说清它为什么这样判。说不清的,规则本身就有问题。
抽取类的判分是:响应必须是合法 JSON、键集合与期望完全一致、每个值等于期望值或包含期望值(按题目标注的匹配模式)。这里有个坑值得单独说——"包含"这个宽松规则,会让"答案里同时出现了正确值和错误值"也算对。我最初的版本就是这么宽松,后来加了"不得含冲突值"这条,得分掉了 6 个点。掉分不是评测变差了,是它终于开始测真实行为了。
五、这套东西帮我拦下过什么
一次具体经历:某个 14B 模型在公开对话质量上口碑很好,我换上去之后,抽取类的"输出合法 JSON"这一项从 98% 掉到 71%,主要是多输出一段解释文字、以及把数字字段写成了带单位的字符串。这个差异,靠"我拿它聊两句"是完全感觉不到的——它的对话质量确实更好。
另一次:把提示词从"仅输出 JSON"改成"输出 JSON,不要任何解释",合法率反而降了两三个点。这类"改着改着变差了"的情况,只有在有回归测试时才会被发现。
所以这套评测真正的价值不在"选出最强模型",而在把变更变成可回滚的判断。
六、我知道它的局限
- 40 道题的统计噪声不小。两个模型差三五个百分点,我不会下结论,会加测。
- 人工判分的改写类,我的标准本身在漂移,隔几个月重判同一份输出,结论可能变。
- 题库会泄漏。我自己的题不会进训练集,但方法传开了,别人用同类题就会有这个问题。
- 只测了单轮。我的真实业务里多轮和长上下文才是难点,这部分我目前只有那篇长上下文的探针法,还没有系统化。
内容标识:本文正文、评测集与统计脚本均为本人自己编写,未使用 AI 生成;文中数据为我本机跑出的结果,只用于说明方法。
本文出处:http://honeysss.cn/post/model-eval-on-a-budget.html 转载请注明出处。