Math 评测
数学评测通过可验证答案考察计算、证明和多步求解。它的答案通常有客观唯一值,适合自动化评分,但「答案对」与「过程对」是两回事。
数学评测基准的分层源自题源难度的差异:GSM8K 收集约 8500 道小学数学应用题,测多步算术与自然语言转公式的基础能力;MATH 则取自 AMC、AIME 等竞赛,含约 1.25 万道按 7 个学科与 5 档难度标注的题目,考察竞赛级求解。二者之外还有 AIME、FrontierMath 等更高难度题源。从 GSM8K 到 MATH 再到 AIME,难度逐级抬升,评测从「能做对应用题」推进到「能解竞赛与证明题」。
快速开始¶
使用标准化答案解析器,并将格式错误、超时和正确答案分别统计;固定采样次数。
先为评测题集准备一个答案解析器,负责从模型输出中提取最终答案并归一化。归一化要处理常见的表示差异,例如分数与小数、多余的空白、$ 或 \boxed{} 包裹、以及不同的等价写法,规则需要与题集的标准答案格式对齐。
评分时把结果分为多类分别计数:解析成功且正确、解析成功但错误、以及格式错误或超时。把格式错误单列,是因为它可能来自模型输出风格而非数学能力,混入平均分会掩盖真实水平,也不利于后续针对性优化。
同时固定每题的采样次数,例如每题采样 4 次,据此计算 pass@1 与 pass@4。这样可以区分「一次性就能做对」和「多次采样才偶尔蒙对」两种情况。一个基于 sympy 做等价判定的最小解析器如下:
from sympy import sympify
def normalize_answer(raw: str) -> str:
return raw.strip().replace("$", "").replace("\\boxed{", "").replace("}", "")
def equivalent(a: str, b: str) -> bool:
try:
return sympify(a) == sympify(b)
except Exception:
return normalize_answer(a) == normalize_answer(b)
assert equivalent("1/2", "0.5") # True
assert equivalent("\\boxed{3}", "3") # True
验证成功的标准:把「1/2」与「0.5」判为等价、把「3」与「3.0」判为等价;对一个已知正确率的样本集,解析器的判定与人工复核一致。
方法¶
最终答案验证客观但不证明过程正确。对证明或开放推导,应结合符号检查、人评或多个验证器。
答案验证 (answer checking) 是对数值题最常用的方式,它自动化、可复现,但只能确认结果,无法确认推导是否正确。模型可能在中间步骤出错后碰巧得到正确答案,也可能用错误方法得到正确数值,这类错误会被答案验证漏掉。
对证明题或开放推导题,答案不再是一个数值,无法直接字符串匹配。此时需要符号检查(把结论交给符号计算或形式化工具验证)、人工评阅,或用多个独立验证器对每一步做局部正确性判断。这类评测成本更高,通常作为数值评测的补充而非替代。GSM8K 原论文提出的「验证器」思路——用另一个模型判断解法是否正确而非只看答案——正是从「答案对」走向「过程对」的早期尝试。
综合做法是分层评测:数值题用答案解析器批量打分,证明题抽样人工复核,并在报告中标明两类题目的比例与各自的评分方式,避免把不同严格程度的分数混为一谈。
案例¶
模型对每题采样 4 次,报告 pass@1 与 pass@4,并单列无法解析答案的比例,避免把格式问题隐藏在平均分中。
以一组求解题为例,对每一题独立采样 4 次,用解析器提取最终答案并判分。若 4 次中至少一次正确,则该题计入 pass@4;若首次即正确,则计入 pass@1。两者之差反映「多采样带来的边际收益」。pass@k 的无偏估计与代码评测一致:
其中 \(n\) 为每题采样数,\(c\) 为通过数,\(k\) 为考察的采样次数。
同时统计无法解析答案的题目比例,例如模型输出了一段推理但没有给出可解析的最终答案,或答案格式与解析器不匹配。若该比例很高,应先修正解析规则或要求模型固定输出格式,而不是直接下调数学能力结论。
常见失败点包括:解析器把等价答案判为错误、模型在单位或符号上不一致、以及超时导致长推导被截断。排查时应先看错误样本是否集中在解析环节,再判断是模型还是评测工具的问题。