ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SciCode基准审计:修复缺陷后LLM科学编程能力被低估的真相

SciCode基准审计:修复缺陷后LLM科学编程能力被低估的真相 这次我们来看一个评测类研究工作SciCode-Verified。名字看起来像是一个版本号实际上它是在 SciCode 基准之上做了一次“基准审计”。核心结论用一句话概括就是原版基准里存在不少缺陷这些缺陷让 LLM 在科学编程任务上的真实能力被明显低估。换句话说模型可能没你想的那么弱是考题本身出了问题。如果你平时关心大模型评估、benchmark 可靠性、科学计算类代码生成或者正在用 SciCode 这类基准做模型选型和能力对比这篇文章值得直接收藏。我会把这项研究的核心思路、缺陷类型、验证流程和评估实践一次讲清楚。先给一个整体判断这个工作不是又造了一个新 benchmark 让你去刷分而是给已有 benchmark 做“体检”再把体检结果和修正后的版本一起公开。它最有价值的地方在于提出了一个系统性的验证方法逐题核查、自动重跑、人工复审、对比分数变化。这套方法不只能用在 SciCode也能迁移到其他代码生成评测集。1. 核心观点速览能力项说明研究对象SciCode 科学编程基准针对 LLM 科学计算代码生成能力核心问题基准存在多处缺陷导致 LLM 能力被系统性低估研究方法逐题分析 自动验证 人工复审定位缺陷并修复主要缺陷类型测试用例错误、问题描述不清、参考代码不一致、评价逻辑问题产出结果修正后的 SciCode-Verified 版本以及前后评估分数对比适用读者LLM 评测工程师、模型选型人员、科学计算方向开发者、benchmark 设计者需要部署不需要 GPU不涉及本地推理属于评测方法论研究文章后面所有内容都围绕“缺陷如何影响评测结论”展开。如果你关心的是“我要不要在自己的任务里用这个方法”可以直接跳到第 5 节和第 7 节。2. 为什么 LLM 科学编程评测容易“失真”科学编程评测和普通代码评测不一样。普通代码评测通常有一个明确的输入输出规格模型写出的代码跑用例就能判断对错。但科学编程任务往往是从自然语言描述的科学问题出发要求模型完成建模、实现数值方法、处理数据、输出结果。这类任务有几个天然难点第一问题描述通常很长且包含大量领域背景。比如一道量子化学或生物信息学题目背景信息里可能涉及公式、近似条件、边界设定。模型需要先理解物理或数学语义再转为代码。如果问题描述本身存在歧义不同模型理解到的东西就完全不一样。第二测试用例难以覆盖所有正确实现。科学计算里同一个问题可以有多种数值解法精度、稳定性、收敛速度都不同。如果基准的测试用例只覆盖某一种实现路径那么采用不同但正确思路的模型就可能被误判。第三参考实现不一定正确。很多代码生成基准的参考代码由人工编写或早期模型生成只要“跑通”就算通过没有经过严格的正确性验证。一旦参考代码本身有 bug用它生成的预期输出就成了错误答案模型即使写出了更正确的代码也会因为输出对不上而被判负。SciCode-Verified 这项研究的背景正是这些难点。它不是第一个发现 benchmark 缺陷的工作但它的贡献在于把缺陷识别和修复的流程系统化了并且量化了缺陷对最终分数的影响。从研究材料看结论是修正之后的评估分数普遍高于原版说明原基准的系统性误判方向是“低估”。3. 基准缺陷通常藏在哪里任何代码生成基准从设计到发布要经过题目编写、测试用例构造、参考实现验证、自动评测脚本开发等多个环节。每个环节都可能埋入缺陷。按 SciCode-Verified 的核查思路缺陷大致可以分为四类。3.1 测试用例本身错误这是最直接的缺陷类型。测试用例的预期输出如果来自一个错误的参考实现那么所有模型都会被这个错误答案带偏。尤其在科学计算场景浮点数误差、迭代次数、收敛阈值都会影响输出测试用例必须严格说明匹配精度。否则模型写对了算法只是因为输出的小数位数不同、舍入方式不同就被判为失败。从核查经验看这类问题最常见的表现是模型代码逻辑完全正确但测试输出和预期不一致。排查时需要先看测试文件的预期结果是如何生成的是人工手算、参考代码跑出来的还是某些数值库版本跑出来的。如果预期结果来源不可靠这个用例就应该标记为缺陷。3.2 问题描述与测试用例不一致有些题目描述里的输入范围、边界条件、函数签名和测试用例实际调用的方式对不上。例如描述里说输入是整数列表测试却传入了浮点数组描述里说函数返回数组测试却期望返回排序后的索引。这类不一致会让模型按照描述实现代码然后因为函数签名不匹配直接编译失败。这类缺陷的危害很大因为不是模型没能力而是评测框架把接口定义搞错了。SciCode-Verified 在核查时会把每个题目的自然语言描述、参考代码、测试文件放在一起做一致性检查。一旦发现接口或边界条件不一致就需要重新定义标准。3.3 参考代码有 bug 或实现思路偏窄科学编程任务的参考实现往往不是唯一解。如果参考代码采用了某种特定算法而测试用例也围绕这个算法构造那么其他正确解法就会被误伤。更严重的是参考代码如果包含隐藏 bug在某些边界输入下会产生错误输出这些错误输出成为测试基线后会污染整个题目的评分。核查参考代码时比较实用的方法是做差分测试找多个独立实现包括不同模型的输出跑同一组输入看结果是否互相印证。如果某个模型的实现结果和参考实现不一致但和其他独立实现一致那就基本可以确定参考实现有问题。3.4 评测逻辑缺陷除了题目和代码本身评测脚本也有出错空间。比如只检查标准输出而忽略错误输出、浮点比较没有设置容差、超时设置不合理、内存限制过严、或者对部分通过的测试按比例计分的方式有误。评测逻辑缺陷会导致模型即使大部分功能正确也拿不到相应分数。SciCode-Verified 在核查过程中会重新审视评测逻辑把“合理实现但未被评分函数覆盖”的场景单独统计。这一步需要非常细致因为有些评测脚本只在特定 Python 版本或特定库版本下才能正确运行环境变化也可能引发误判。4. 缺陷如何导致能力被低估有了缺陷清单接下来的问题是这些缺陷对最终评估结果的影响有多大SciCode-Verified 的做法是对比修复前后的通过率变化。虽然我手上没有论文的完整数据但从方法逻辑上可以推导出几条明确的影响路径。4.1 错误负例拉低通过率当基准里存在错误测试用例时模型做对了也会被判错。这种情况对整体通过率的拉低是直接的一个题目如果只有 3 个测试用例其中一个被污染那么这个题目的满分率上限就从 100% 降到了 66.7%。在科学编程这种长尾任务中单个题目的失败可能让模型直接从“通过”掉到“未通过”。4.2 描述歧义导致模型“答非所问”问题描述有歧义时模型可能按照常见的通用解法写代码但评测期望的是特定领域解法。比如题目问的是“用蒙特卡洛方法估计圆周率”但描述里没有明确随机抽样次数和随机种子模型的输出就会因为随机性无法复现而失败。这类失败不是模型不理解任务而是评测条件没有闭环。修复后的基准会补充随机种子、精度要求、输出格式等关键约束让模型和评测脚本站在同一条起跑线上。4.3 接口不一致造成系统性编译失败当多个题目都存在接口描述和测试调用不一致时某些模型会被系统性扣分。这类模型的代码风格可能偏向“严格按照描述实现”遇到不一致时反而吃亏。而另一些模型习惯“猜测测试预期”结果分数虚高。这就会导致模型能力排名被扭曲不只是低估还包括错位。4.4 分数修复后暴露真实能力分布SciCode-Verified 最有意思的地方在于修复缺陷后不仅平均分上升模型的相对排名也可能变化。因为不同模型的错误模式不同有的模型问题理解强接口一致性强受接口缺陷影响小有的模型数值计算强但容易受歧义描述干扰。修复缺陷后真正强的模型会更容易拉开差距。5. 验证子集与复现评估流程SciCode-Verified 这套方法本身是可以迁移的。如果你不想完全复现论文实验也可以把它当作一套“基准体检方案”来使用。下面给出一个通用验证流程适配科学编程类评测集。5.1 准备阶段先把评测集所有题目拆成四个文件维度问题描述、参考代码、测试用例、评测脚本。然后建立一份题目清单记录每道题的状态未核查、待验证、已修复、已废弃。# 示例题目状态跟踪结构 tasks [ { id: scicode_001, status: pending, description: 描述文件路径, reference: 参考代码路径, test: 测试用例路径, issues: [] } ]这一步不需要 GPU只需要代码阅读能力和领域知识。5.2 自动一致性检查写一个脚本逐题加载参考代码和测试用例确认函数签名匹配、导入路径正确、测试用例能独立运行。同时把问题描述中出现的接口词和测试代码中的调用方式做对比。# 通用检查命令示意跑测试并输出完整日志 python -m pytest test_scicode_001.py -v --tblong如果一个测试用例连参考代码都跑不过它作为基准就没有意义。这个环节能快速定位接口不一致和依赖缺失问题。5.3 差分测试定位“争议题”对每个题目用多种方式生成实现可以调用不同 LLM也可以找人手工实现。把这些实现放到同一组测试用例下跑比较结果一致性。如果多个实现不一致说明题目或测试有问题需要人工介入。# 差分测试示意多个实现输出对比 implementations { reference: run_reference(), llm_a: run_llm_a(), llm_b: run_llm_b() } for name, output in implementations.items(): print(f{name}: {output})重点观察的是“参考实现失败但其他实现成功”和“其他实现一致但参考实现失败”两种模式。5.4 人工复审缺陷清单自动检查能找出表面问题但很多语义缺陷需要人来看。人工复审时需要逐题确认问题描述的数学条件是否完整。参考代码是否实现了描述中所有步骤。测试用例是否覆盖了主要边界条件。评测脚本的浮点比较是否有容差。是否有多个合法解法被评测脚本排除。人工复审是整个流程中最耗时、最依赖领域知识的环节。SciCode-Verified 的核查深度也主要体现在这里。5.5 修复后重新评测把确认的缺陷修复后保持原评测集的其他部分不变重新跑一遍所有模型。然后对比修复前后的分数分布。如果修正后的分数整体上升说明原基准确实存在低估问题。同时记录每题的变化幅度找出哪些题目对分数波动贡献最大。# 演示修复前后分数对比 scores_before {model_a: 40.0, model_b: 45.0} scores_after {model_a: 55.0, model_b: 60.0} for model in scores_before: diff scores_after[model] - scores_before[model] print(f{model}: {diff:.1f})这一步会直观呈现“低估”的幅度。6. 评估结果对模型选型的实际影响如果你正在做 LLM 代码生成能力选型SciCode-Verified 的发现对你意味着什么最关键的一点是不要只看 benchmark 分数排名要深挖分数背后的题目质量。6.1 分数差 5 分可能只是基准噪声原基准里因为测试用例错误、描述歧义导致的误判一旦修复某些模型可能提升 10 分以上。这意味着如果你基于原基准在 A 模型和 B 模型之间做了选型决策而这个决策只差 5 分那这个决策可能是不稳定的。更稳妥的判断方式是在选型前先做一轮题目质量抽检至少确认自己关心的任务类型没有被缺陷题污染。6.2 领域内分数比综合分数更重要SciCode 覆盖多个科学领域包括生物、化学、物理、数学等。缺陷的影响不是均匀分布的。有些领域题目描述更依赖领域常识模型理解偏差大有些领域题目结构简单基本都是接口翻译题。修复后不同领域的分数变化幅度会不同。你在选型时应该按目标领域过滤题目而不是看整体平均分。6.3 需要保留一份“验证后低置信度题目列表”在评测实践中最好把修复后的基准里仍然存在争议的题目单独标记。之后跑新模型时可以同时看两组分数一组是全体题目分数一组是排除低置信度题目后的分数。如果两组分数的排名不一致说明新模型的排名对题目质量敏感需要进一步人工评估。{ benchmark: SciCode-Verified, low_confidence_tasks: [task_012, task_047], scoring_mode: exclude_low_confidence }这个操作很简单但能显著提升评测结论的鲁棒性。7. 构建高质量科学编程评测集的工程建议如果你自己也在构建代码生成评测集SciCode-Verified 的核查思路可以直接用到评测集开发流程中。下面几条工程建议来自常见的 benchmark 开发实践结合这个研究的方向做一些整理。7.1 测试用例要独立于参考实现生成设计测试用例时不要只依赖单个参考实现。可以先用自然语言描述写出输入输出示例再让多个独立实现交叉验证。如果只有一份参考代码至少要在代码里明确注释哪些输出是手算推导的哪些是代码生成的。测试用例的预期结果最好有二级来源比如数学推导、数据集统计值或外部工具输出。7.2 浮点比较必须有显式容差科学计算离不开浮点数。评测脚本里如果直接用assert output expected基本等于宣判所有数值计算题都有缺陷。正确做法是使用相对误差或绝对误差比较并且给出可配置的容差参数。# 演示浮点比较容差 import math def assert_float_close(actual, expected, rel_tol1e-6, abs_tol1e-9): if not math.isclose(actual, expected, rel_tolrel_tol, abs_tolabs_tol): raise AssertionError(f{actual} is not close to {expected})7.3 明确随机种子和依赖版本科学编程代码经常涉及随机数、数值积分、迭代求解。评测时必须固定随机种子、库版本、并行线程数否则结果无法复现。基准发布时需要提供一份环境文件包含 Python 版本、NumPy/SciPy 等核心库版本。# environment.yml 示例 name: scicode-verified channels: - conda-forge dependencies: - python3.10 - numpy1.26 - scipy1.117.4 区分“实现正确”和“输出一致”代码生成评测的最终目标是判断模型的实现是否正确。如果两个实现采用不同算法但都满足问题要求那么它们应该都被视为正确答案。可以给每个题目标记多种可接受的解决路径评测脚本里为每种路径准备独立的测试集或者用性质测试property testing代替固定输出比对。这样能大幅减少误杀。7.5 发布时包含缺陷报告和修订记录基准不应该是一个静态文件。SciCode-Verified 这类工作提示我们基准需要持续维护每次修订都要公开变更日志。使用者应该能清楚知道当前版本对比上一版修订了哪些题目、删除了哪些测试、调整了哪些容差参数。没有修订记录的 benchmark 长期来看都是不可靠的。8. 常见误判场景与排查方法很多人拿到代码生成 benchmark 后第一反应是跑模型、看分数、出报告。但分数出来之后一定要做一层“基准体检”否则结论可能建立在错误数据上。下面整理几个常见误判场景和处理思路。误判场景可能原因排查方式处理建议所有模型在某一题都失败测试用例预期输出错误或依赖缺失人工跑参考实现检查测试是否通过修复测试用例或标记废弃某模型在某一题明显弱于其他模型接口描述和测试调用不一致该模型严格按描述实现对照描述和测试代码的签名统一接口定义后重测浮点结果时对时错评测没有设置容差或随机种子未固定检查评测脚本的断言方式使用 math.isclose 并固定种子高分模型在真实任务中表现差基准题目和真实任务分布偏差大对比题目类型检查是否存在刷分空间引入验证子集按领域加权两次实验分数不稳定模型输出随机性或评测环境差异固定温度参数、推理后端、随机种子多次运行取平均值新版本模型分数反而降低新模型适配原基准的“缺陷模式”被修复后失效对比缺陷题和非缺陷题分数用修正版基准重新评估这些场景不是 SciCode-Verified 独有的而是所有代码生成评测都会遇到的问题。关键是要建立“分数出来后不急于下结论”的习惯。9. 总结与下一步SciCode-Verified 这项研究最值得关注的地方不是某一个数字而是它提供了一个“基准自查”的范式。它告诉我们LLM 在科学编程任务上的能力评估远比跑一个脚本拿 60 分要复杂。题目描述、参考代码、测试用例、评测逻辑任何一个环节有缺陷最终分数都可能失真。而且从研究标题就能看出这种失真的方向很可能是“低估”。如果你要基于这个方向做自己的验证最先应该做的事有两件第一把你正在用的科学编程评测集里分数最低的 10 道题拉出来人工过一遍看看是模型真不会还是题目本身有问题第二如果你正在构建自己的评测集把测试用例独立生成、浮点容差、随机种子固定这三条规则先加进去。最容易踩的坑是拿修复前的分数直接做模型选型。尤其是当候选模型之间的分数差异在 5 到 10 分以内时这个差异可能完全来自基准缺陷而不是模型能力的真实差异。后续你可以继续关注的方向包括自动化缺陷识别的工具比如通过差分测试自动定位争议题、科学编程评测集的领域加权评分、以及把 SciCode-Verified 的核查方法迁移到其他代码生成基准上。这些方向都能让 LLM 能力评估变得更可信。建议把修正后的验证流程保存成你自己的评测模板之后每评估一个新模型先跑一遍基准体检再下结论。
返回列表