第 11 章:LLM 评估¶
1. 本章要解决的问题¶
第 10 章里,我们已经回答了一个很重要的问题:
为什么在大模型时代,大家会持续增加参数、数据和算力。
但紧接着,一个更现实的问题就会出现:
模型变大了,loss 下降了,我们到底该怎样证明它“真的更强了”?
这就是评估要解决的事情。
如果没有评估,很多讨论都会变得非常空泛:
- 某个模型“感觉更聪明”,但不知道强在哪
- 某次微调“看起来有效”,但不知道是不是偶然样本
- 某个 prompt“似乎不错”,但换一批问题就失效
- 某个版本上线后“用户好像没意见”,但其实已经发生能力回退
所以评估不是最后补的一张成绩单,而是整个 LLM 开发流程里的方向盘。
从全书结构上看,这一章有三个作用:
- 它承接第 10 章,把“模型为什么继续 scale”推进到“scale 之后该怎么判断收益”
- 它为第 12 章的 SFT 做准备,因为后训练目标本质上也是在优化某类评估结果
- 它也为第 16 章的 RAG、以及第 17 章的 Agent 评估打基础,因为应用层评估会比通用模型评估更复杂
如果第 10 章回答的是:
为什么我们愿意花更大成本去训练更大的模型。
那么这一章要回答的就是:
一个 LLM 到底应该从哪些维度被衡量,不同评估方法各自可信到什么程度,以及我们怎样建立一套真正能服务开发迭代的评估体系。
2. 你学完后应该会什么¶
- 能解释为什么 LLM 评估比普通分类任务更复杂
- 能区分 benchmark、自动指标、人工评估分别适合解决什么问题
- 能理解 perplexity、Exact Match、F1、BLEU、ROUGE、pass@k 这些指标各自的边界
- 能说清 LLM-as-a-judge 为什么有用、为什么不能盲信
- 能设计一个最小可用的 evaluation set 和回归测试集
- 能把“评估”这件事自然衔接到后续的 SFT、RAG 和 Agent 开发
3. 为什么 LLM 评估比传统任务难得多¶
先看一个对比。
在二分类任务里,我们通常有:
- 明确标签
- 固定输出空间
- 统一指标,比如 accuracy、precision、recall
例如垃圾邮件分类,模型输出要么是 spam,要么是 ham,对错边界很清楚。
但 LLM 不一样。
它的输出往往是开放生成的,自然语言答案可能有很多种等价表达。比如同一个问题:
模型可能回答:
- “因为 self-attention 不像 RNN 那样依赖严格的时间步递归。”
- “因为序列位置可以同时计算,只需要用 mask 控制信息流。”
- “因为它去掉了 RNN 的逐步传递结构,所以更容易做 GPU 并行。”
这三种答案本质上都对,但字符串并不相同。
所以 LLM 评估的第一个难点是:
正确答案往往不是唯一字符串。
第二个难点是,LLM 的能力不是单轴的。
我们评估的可能包括:
- 知识问答
- 推理
- 数学
- 代码
- 指令遵循
- 格式稳定性
- 安全性
- 多轮对话体验
这意味着“模型强不强”通常不是一个数字就能说清。
第三个难点是,评估目标和使用场景高度相关。
一个擅长写摘要的模型,不一定擅长代码生成; 一个 MMLU 很高的模型,也不一定适合客服机器人; 一个通用 benchmark 很强的模型,到了企业内部文档问答场景,可能依然表现很差。
所以这一章最重要的一个判断是:
评估从来不是找一个万能分数,而是要建立“任务目标 - 数据集 - 指标 - 人工检查”之间的对应关系。
4. 我们到底在评估什么¶
说“评估模型”,其实常常混着几种不同层次的问题。
4.1 评估模型的语言建模能力¶
这一层最贴近预训练本身。
它关心的是:
- 模型对文本分布拟合得怎么样
- 下一个 token 预测能力强不强
- 在通用语料上的 loss 是否足够低
这类指标里最经典的是 perplexity。
4.2 评估模型的任务完成能力¶
这一层更像“模型能不能把事情做对”。
例如:
- 选择题能不能选对
- 数学题最终答案对不对
- 代码能不能通过测试
- 摘要有没有覆盖关键信息
这里常见的是 benchmark 和任务指标,比如:
- MMLU
- GSM8K
- HumanEval
- Exact Match
- F1
- pass@k
4.3 评估模型的交互质量¶
这一层开始接近真实产品体验。
它关心的问题通常是:
- 是否听懂指令
- 是否按格式输出
- 是否啰嗦
- 是否稳定
- 是否愿意承认不知道
- 是否容易胡编
这时单纯靠自动指标往往不够,需要人工评估,或者引入 LLM-as-a-judge 做近似筛选。
4.4 评估模型在特定场景中的业务价值¶
这已经不是“通用 LLM 排名”问题,而是“你的系统有没有变好”。
例如在一个 RAG 系统里,我们可能真正关心的是:
- 回答是否引用了正确文档
- 是否答非所问
- 是否把检索噪声带进答案
- 用户问题的解决率有没有提高
也就是说,越往后走,评估对象就越不像“裸模型”,而更像“整条系统链路”。
5. benchmark:为什么大家都爱排行榜,但又不能只看排行榜¶
benchmark 可以理解为:
一组公开、相对标准化的测试任务,用来让不同模型在相似条件下可比较。
它的好处很明显。
5.1 benchmark 的价值¶
- 它提供了一个公共语言,方便横向比较模型
- 它能快速暴露模型在哪些任务上强、哪些任务上弱
- 它对研究和工程都很有用,因为大家至少能先在同一套题上对齐
比如常见 benchmark:
MMLU:偏知识与学科理解GSM8K:偏小学到中学水平的数学推理HumanEval:偏代码生成HellaSwag:偏常识与续写判断
5.2 benchmark 的局限¶
但 benchmark 也有明显边界。
第一,它可能和真实场景不一致。
你的业务是电商客服,结果天天盯着数学题分数;你的业务是企业知识库问答,结果只看通识考试题,这样就很容易发生“榜单很好,产品不好”的错位。
第二,它容易被“刷榜”。
当某个 benchmark 被反复公开使用后,模型开发者会逐渐围绕它优化,甚至训练数据里可能已经混入类似题目。这时高分不一定代表真正泛化。
第三,它通常覆盖不了稳定性和体验问题。
同一个模型也许平均分不错,但:
- 温度稍微调高就开始飘
- 指令格式经常不遵守
- 多轮对话容易遗忘约束
这些问题常常不会直接反映在公开 benchmark 分数里。
所以 benchmark 最适合做的是:
作为通用参照,而不是作为唯一结论。
6. 自动指标:快,但要知道它到底在测什么¶
自动指标的优点是便宜、稳定、可批量运行,所以它是工程迭代的基础设施。
但自动指标的危险也在于:
如果你不知道它到底测的是什么,就很容易把“可计算”误当成“真正重要”。
6.1 perplexity:最贴近预训练,但不等于用户体验¶
perplexity 可以理解为模型对测试文本“不惊讶”的程度。
如果交叉熵是:
那么困惑度通常写作:
直觉上:
- PPL 越低,说明模型越擅长给真实 token 更高概率
- 它很适合衡量语言建模质量
但它不能直接回答:
- 模型是否会遵循指令
- 模型是否会拒答危险请求
- 模型是否会输出结构化 JSON
所以在 LLM 时代,perplexity 很重要,但通常不够。
6.2 Exact Match 与 F1:适合答案相对短、边界较清楚的任务¶
在问答、抽取、阅读理解里,经常使用:
Exact Match:预测答案和标准答案是否完全一致F1:预测文本和标准答案在 token 层面的重合程度
这类指标的优点是:
- 简单
- 易复现
- 对短答案任务比较有效
但局限也明显:
如果标准答案是“北京”,模型输出“北京市”,有时语义上可以接受,但 EM 会直接判错。
6.3 BLEU 和 ROUGE:适合看重叠,不适合单独代表生成质量¶
BLEU 常见于机器翻译,偏 precision;
ROUGE 常见于摘要,偏 recall。
它们衡量的是:
生成文本与参考答案之间的 n-gram 重叠程度。
这对批量比较模型版本很方便,但问题是:
- 表达不同但意思相同,分数可能不高
- 语言自然但没覆盖重点,人工觉得差,指标有时不一定低
所以今天大家一般不会只靠 BLEU / ROUGE 评价一个开放生成系统。
6.4 pass@k:代码生成里比“只看一发”更合理¶
代码任务有个很典型的问题:
模型第一次生成不一定对,但多采样几次也许能出可运行解。
这时常见指标是 pass@k,意思是:
采样 k 个候选,只要其中至少一个通过测试,就算成功。
它更接近很多代码助手的真实使用方式,因为用户也常常会重试、采样多个答案、再人工选择。
但要注意,pass@k 会受到采样温度、样本数和去重策略影响,不能脱离评测设置单独解读。
7. 人工评估与 LLM-as-a-judge:为什么最终还是要看“人”¶
当任务开始变得开放,例如写作、解释、对话体验、指令遵循时,自动指标通常不够细。
这时就会进入人工评估。
7.1 人工评估通常在看什么¶
常见维度包括:
- 正确性
- 完整性
- 有用性
- 清晰度
- 安全性
- 风格一致性
人工评估最大的优点是:
它最接近真实用户感受。
但代价也很明显:
- 贵
- 慢
- 主观性强
- 不同标注员之间可能不一致
所以工程里常见做法不是“全部人工”,而是“自动筛 + 人工复核”。
7.2 pairwise comparison 往往比绝对打分更稳¶
相比让评审员直接打 1-5 分,很多团队更喜欢做 A/B 对比:
原因是绝对分数容易漂,评审员标准不一致; 而二选一比较更接近“这个版本到底有没有变好”。
这也是很多偏好数据集和对齐数据集的来源。
7.3 LLM-as-a-judge 为什么流行¶
因为人工评估太贵,所以大家开始尝试让一个更强的模型去判断另一个模型的输出。
它的优点是:
- 成本低于大规模人工
- 可快速批量跑
- 对开放生成任务比纯字符串指标更灵活
例如可以让 judge 模型从以下维度打分:
- 是否回答了问题
- 是否出现事实性错误
- 是否遵守了格式要求
- 是否比 baseline 更简洁清楚
7.4 为什么不能盲信 LLM judge¶
LLM-as-a-judge 很有用,但它不是客观真理。
它可能有:
- 对某种文风的偏好
- 对长答案的偏好
- 对自己家模型风格的偏好
- 对提示词非常敏感
更现实一点说:
judge 也是模型,模型就会有偏差。
所以更稳妥的做法通常是:
- 先用 LLM judge 大规模筛选
- 再抽样做人审
- 对关键任务保留人工 golden set
8. 一个真正能用的评估集,应该怎么设计¶
这一节最重要,因为它最接近你以后做项目和面试时真正会遇到的问题。
很多人说“我做了评估”,其实只是随手找了十几个例子试一下。
这不叫评估,这更像演示。
8.1 先从任务拆解开始,而不是先找指标¶
正确顺序通常是:
- 先定义系统要解决什么任务
- 再拆成若干关键能力
- 再为每类能力收集样本
- 最后才决定每类样本用什么指标
例如一个企业知识库助手,能力可以拆成:
- 是否理解用户问题
- 是否检索到正确材料
- 是否基于材料回答
- 是否在不知道时明确说不知道
- 是否按要求输出引用
这时你的评估集就不应该只有“问答题”,而应该分桶。
8.2 evaluation set 最好分层¶
一个实用的评估集,通常至少包括三层:
smoke set:十几到几十条,快速冒烟,几分钟能跑完dev set:用于日常迭代比较,规模中等holdout set:不频繁查看,用来防止对 dev set 过拟合
如果只盯着一套固定样本不断调,很容易把系统调成“会做这套题”,而不是“真的更强”。
8.3 一定要保留 bad cases¶
评估集里最有价值的样本,往往不是最普通的,而是那些曾经失败过、而且业务上真的重要的 case。
比如:
- 检索到了错误文档
- 指令中包含多个约束,但模型漏掉一个
- 数学中间过程对,最终答案错
- JSON 输出少了一个字段
这些 bad case 一旦修好,就应该沉淀进回归测试集。
因为工程上最怕的不是“今天有 bug”,而是“明天它又回来了”。
8.4 评估集要覆盖分布,而不是只挑顺手样本¶
很多新手容易只收集“最像 demo”的题。
但真实评估更应该覆盖:
- 高频场景
- 高价值场景
- 高风险场景
- 容易失败的边界场景
也就是说,评估集不是给模型表演用的,而是给系统挑毛病用的。
9. 一个最小可用的评估流程¶
下面给一个足够小、但很实用的评估思路。
9.1 先准备统一格式的数据¶
例如用 jsonl 存:
{"id":"qa-001","question":"Transformer 为什么更容易并行化?","reference":"因为 self-attention 不需要像 RNN 一样按时间步串行计算。","tag":["reasoning","nlp"]}
{"id":"qa-002","question":"2 的 10 次方是多少?","reference":"1024","tag":["math"]}
这样做的好处是:
- 不同任务容易批量处理
- 可以按标签分桶统计
- 后续容易追加人工标注字段
9.2 自动打分先解决“回归”问题¶
比如对短答案任务,可以先做一个非常朴素的脚本:
import json
def normalize(text: str) -> str:
return "".join(text.lower().split())
def exact_match(pred: str, ref: str) -> int:
return int(normalize(pred) == normalize(ref))
scores = []
with open("eval.jsonl", "r", encoding="utf-8") as f:
for line in f:
item = json.loads(line)
pred = call_your_model(item["question"])
scores.append(exact_match(pred, item["reference"]))
print(sum(scores) / len(scores))
这个脚本并不高级,但已经能解决一个关键工程问题:
每次改完 prompt、模型或数据后,你至少能知道某类题是不是退步了。
9.3 再补一层抽样人工检查¶
自动分数跑完后,再从以下几类样本里抽查:
- 自动判错但你怀疑其实答对了
- 自动判对但你怀疑质量不高
- 新增功能相关样本
- 高风险场景样本
这样才能避免“指标很好,但输出其实很怪”。
9.4 最后记录版本间差异,而不是只看单次分数¶
真正有用的评估,不是只记“这次多少分”,而是比较:
- 相比上个版本,哪里提升了
- 哪些 bad case 被修复了
- 有没有新引入的退化
所以评估最好天然支持回归比较,而不是只做一次性展示。
10. 常见误区¶
10.1 误区一:benchmark 高分就等于产品表现好¶
这通常不成立。
benchmark 只能说明模型在某些公共任务上表现不错,但产品系统还受到检索、prompt、工具调用、输出格式、延迟和业务流程的共同影响。
10.2 误区二:只要有自动指标,就不需要人工看输出¶
这也不对。
自动指标擅长大规模、低成本比较,但很多真正影响用户体验的问题,最后还是得靠人发现。
10.3 误区三:评估就是项目最后再补一下¶
实际上,评估越晚做,返工通常越大。
更合理的方式是:从一开始就维护最小评估集,让每一轮改动都有反馈。
10.4 误区四:评估集越大越好¶
评估集当然不能太小,但也不是无脑越大越好。
如果样本很多却没有分桶、没有代表性、没有 bad case 沉淀,那只是“很多题”,不一定是“有效评估”。
11. 面试问题¶
Q1:为什么说 LLM 评估比传统分类任务更复杂?¶
因为 LLM 输出是开放生成的,正确答案常常不唯一,而且模型能力是多维的,单一指标很难覆盖知识、推理、格式遵循和交互体验。
Q2:perplexity 高低能不能直接代表模型是否适合做聊天助手?¶
不能。perplexity 更贴近语言建模质量,但聊天助手还涉及指令遵循、安全性、风格稳定性和多轮交互体验,这些都不是 PPL 能单独覆盖的。
Q3:为什么很多团队会同时使用自动评估、人工评估和 LLM judge?¶
因为三者各有边界。自动评估便宜稳定,适合回归;人工评估最接近真实体验,但成本高;LLM judge 能放大评估规模,但本身也有偏差,所以通常需要组合使用。
12. 本章小结¶
这一章最核心的结论不是“哪一个指标最重要”,而是:
评估必须服务目标。
对于预训练模型,你可能更关心 perplexity 和通用 benchmark; 对于后训练模型,你更关心指令遵循和回答质量; 对于应用系统,你更关心业务场景里的解决率、稳定性和回归风险。
所以一个成熟的 LLM 评估体系,通常不是单个分数,而是几层东西的组合:
- 公共 benchmark 用来做通用参照
- 自动指标用来做快速回归
- 人工评估用来检查真实体验
- 自定义 evaluation set 用来贴合自己的任务
到这里,我们就可以自然进入下一章了。
因为一旦你知道“什么叫一个更好的回答”,下一个问题就会变成:
我们怎样用带指令的数据,把预训练模型进一步塑造成一个更会回答人类问题的助手?
这就是第 12 章要讲的 Supervised Fine-Tuning。