模型在分子性质基准上的高分,可能不是能力,而是记忆。这一期从一份覆盖 22 个前沿模型的审计出发,拆解「背答案」如何悄悄污染专业领域评测,并给出可落地的反检索校验方案。
开篇:一张「完美」的分子性质分数
在 4sapi 的后台里,我见过不少「分数很漂亮」的选型报告:某个前沿模型在某分子回归基准上跑出接近 SOTA 的误差,负责人兴冲冲准备接入上线。但仔细一问,几乎没人回答得上一个问题——这个分数,到底是模型「算」出来的,还是「背」出来的?
这不是抬杠。LLM 越来越多地在分子性质基准上被评测,而准确率本身无法区分两件事:模型预测了性质,还是模型检索到了已发表数值。当一个模型见过训练语料里的实验数据表,它完全可能在回答时把表中的数值逐字复述出来——速度快、误差小、看起来无比强大。可这种强大是假的。
痛点:专业领域模型的分数,可能是一场记忆表演
垂直领域选型,最怕的就是「分数虚高」。通用聊天场景里,模型背几句台词影响不大;但在科学、医药、材料这类专业数据场景,一个把已发表数值背得滚瓜烂熟的模型,会在 benchmark 上把真正会推理的模型按在地上摩擦。
后果很具体:选型花了大价钱接入「高分模型」,上线后发现它只会复述训练集里的旧数据,遇到新分子立刻露馅;内部评测把背答案当成能力,导致能力排名失真;更麻烦的是,这种污染是系统性的——不是某一个模型的问题,而是整个评测体系的问题。
分子性质预测尤其容易被污染,原因有三:其一,回归任务的答案是一个单浮点数,比如溶解度 logS = -2.1,逐字复述的代价极低;其二,分子以 SMILES 字符串出现在各类公开数据表中,训练语料几乎必然覆盖;其三,这些数值经常以完全相同的小数位与单位出现在多个数据集里,命中即零误差,直接把 RMSE、MAE 拉到不真实的好看。
原理速览:记忆检索 vs 推理
要理解污染,先分清两条完全不同的回答路径:
- 推理路径:模型理解输入(比如一个分子结构),基于化学规律推断性质,输出随输入变化而合理变化,哪怕输入是从未见过的分子也能给出有依据的估计。
- 记忆路径:模型在训练语料里见过该分子及其实验数值,回答时直接从参数中「检索」已发表数值,输出与原文逐字一致,与当前输入几乎无关。
前者是能力,后者是记忆检索。评测设计者假设模型走推理路径,但 benchmark 的答案往往来自公开数据,恰好落在训练语料之内。于是评测分数同时度量了两样东西:真实推理能力与训练数据覆盖率。当记忆检索的贡献占主导时,分数就失去了科学意义——模型「背答案」而不是「算答案」,基准的分辨力归零,横向对比自然失真。
逐字检索的识别特征也很明确:输出与已发表数值在字符串层面完全一致,包括小数点位数、正负号写法、单位与注释格式;对输入扰动高度不敏感;对上下文里植入的干扰数值也无动于衷。
审计事实:22 个前沿模型、12 个基准的数字
我最近跟进的一份审计,把这个问题量化了。审计覆盖 22 个前沿模型、12 个分子回归基准,专门检测模型输出是否与已发表数值存在逐字检索。结果触目惊心:
- 在 5 个数据集上,超过 50% 的模型表现出对已发表数值的逐字检索(verbatim retrieval);
- 其余数据集上,检索程度较轻,但同样存在;
- 也就是说,「高分 = 强能力」在相当一部分数据集上不成立。
更深的洞察是:数据污染/记忆检索不是偶发瑕疵,而是前沿模型评测的系统性隐患。只要基准答案公开、且落入训练语料,逐字检索就无法根除;唯一可行的方向,是把「反逐字检索」写进评测协议——扰动输入、改写数值、抽查原文,把「背答案」的模型当场揪出来。对选型方而言,这意味着分数必须经过校验才能进入决策,而不能直接照单全收。
污染程度分级表
| 污染级别 | 典型表现 | 诊断信号 | 选型处理建议 |
|---|---|---|---|
| 逐字检索 | 输出与已发表数值逐字一致,连错误都照背 | 扰动输入后答案纹丝不动,甚至报错 | 该数据集分数作废,重新评测 |
| 局部检索 | 部分样本命中已发表数值,部分正常预测 | 改写输入后部分答案漂移、部分原样复述 | 按扰动前后一致率重算分数 |
| 正常预测 | 答案随输入合理变化,无原文痕迹 | 扰动后数值合理漂移,推理连贯 | 分数可信,可进入横向对比 |
这张表的用法很简单:先给模型跑一轮「原样提问」,再跑一轮「扰动提问」,把多轮输出与已发表数值做逐字比对,就能把模型划进对应级别。
反检索校验三招
针对逐字检索,有三招可落地,组合使用效果最好。
第一招,扰动输入。对同一分子,用等价改写(改变 SMILES 的原子书写顺序、调整字符串表示)重新提问。真推理的模型输出会保持稳定或合理漂移;背答案的模型要么复述原值,要么因为输入不在记忆里而答非所问——两种反应都是有力的诊断信号。
第二招,改数值重测。在提问里主动植入一个「与已发表值不同」的上下文数值,比如把参考值从 -2.1 改成 +0.3,观察模型输出是否被带偏。真预测模型会基于分子本身给出一致答案;检索型模型要么被引导着改变输出,要么无视扰动继续复述记忆里的原值。
第三招,加噪声重测。给输入附加随机扰动(噪声种子、批次信息、仪器误差描述),把同一问题重复多次。正常预测的输出围绕真实值小幅波动,逐字检索的输出则与扰动无关、分毫不差。
请求流图示
反检索校验在接入链路里的位置,我用一张文本图说明:
┌─────────────────────────────────────────┐
│ 4sapi 网关 │
校验任务 ────▶│ 路由 / 负载均衡 / 限流 / 用量计量 │
│ (合法 API 接入,统一入口) │
└───────────────┬─────────────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
模型 A(旗舰) 模型 B(均衡) 模型 C(低成本)
└────────────────┼────────────────┘
▼
┌──────────────────────────────┐
│ 反检索校验层 │
│ ① 原值提问 → 记录输出 │
│ ② 改数值/加噪声 → 重测输出 │
│ ③ 逐字比对 → 判定污染级别 │
└──────────────────────────────┘
▼
输出:真预测 / 局部检索 / 逐字检索
校验层放在网关下游、模型上游,一次请求完成「提问—扰动—判定」三步,结果回写选型报告。多模型并发由网关的负载均衡统一调度,校验过程不会拖慢主线任务。
接入教程:用 4sapi 跑反检索校验
实操层面,反检索校验完全可以脚本化。我用 OpenAI 官方 Python 库,把 base_url 指向 4sapi 的合规接入端点 https://4sapi.com,同一套代码即可路由多个上游模型做横向对比。第一步,配置客户端:
import os
from openai import OpenAI
# 4sapi 合规接入:统一 base_url,多模型共用一套代码
client = OpenAI(
api_key=os.environ.get("SAPI_KEY", "YOUR_4SAPI_API_KEY"),
base_url="https://4sapi.com/v1",
)
def ask_logs(model: str, smiles: str, context: str) -> str:
"""对给定分子提问 logS 预测,只取数值。"""
prompt = (
"请预测以下分子在水中的溶解度 logS,只输出数值。\n"
f"SMILES: {smiles}\n"
f"上下文: {context}\n"
)
resp = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
temperature=0.0, # 关闭随机性,保证可复现
max_tokens=16,
)
return resp.choices[0].message.content.strip()
第二步,实现反检索校验:同一分子跑「原样 / 干扰值 / 等价改写 / 加噪声」四组探针,再把每组输出与已发表数值逐字比对,按命中比例判定污染级别:
def anti_retrieval_check(model: str, smiles: str, published: str):
"""反检索校验:改数值、加噪声重测,逐字比对已发表数值。"""
variants = [
("原样", smiles, "无额外上下文"),
("干扰值", smiles, "参考值应为: +0.3"),
("等价改写", "O=C(O)c1ccccc1OC(C)=O", "无额外上下文"),
("加噪声", smiles, "附加噪声种子: 0.731"),
]
results = {name: ask_logs(model, smi, ctx) for name, smi, ctx in variants}
verbatim = sum(1 for v in results.values() if v.strip() == published.strip())
ratio = verbatim / len(variants)
level = "逐字检索" if ratio >= 0.5 else ("局部检索" if ratio > 0 else "正常预测")
print(f"[{model}] 级别: {level} | 命中 {ratio:.0%} | {results}")
return level
# 示例:同一分子,横向对比多个模型
smiles = "CC(=O)Oc1ccccc1C(=O)O" # 示例分子(阿司匹林)
published = "-2.1" # 该分子的已发表 logS 值
for model in ["gpt-4o-mini", "claude-3-5-haiku", "glm-4-flash"]:
anti_retrieval_check(model, smiles, published)
判定逻辑与分级表一致:四组探针全部逐字命中已发表值,判「逐字检索」;部分命中判「局部检索」;一组都不命中且输出随扰动合理变化,判「正常预测」。整套脚本跑下来,一个模型约 4 次请求,token 成本可忽略,却能把「背答案」的模型挡在选型门外。注意温度设为 0 以保证可复现,必要时对同一探针重复 3 次取众数,排除输出随机性干扰。
成本与风险提示
反检索校验本身的开销很小,但有三点要算清楚。
第一,样本要精选。全量基准做扰动重测很贵,取每个数据集的代表性样本(每类分子 3-5 条)即可,误差足够判定污染级别。样本选取按分子类型分层,避免集中在某一段性质区间。
第二,请求要复用。同一分子的不同探针、不同模型可以并发提交,4sapi 网关自带负载均衡与限流,把 22 个模型级别的横向对比变成一次并行任务,整体耗时可控;并发上限按套餐配额设置,防止突发流量打满计量。
第三,计费要透明。校验任务属于低 token、高并发类型,按实际用量计费即可;避免对全量数据反复重测造成浪费。合规边界上,只使用官方许可的 API 接入与路由,做架构、负载与计费层面的优化,不涉及任何非法的代理与绕过方案。
检查清单
上线专业领域模型前,过一遍这份清单:
- 选取 3-5 个已发表分子样本,先做「原样提问」,观察是否逐字命中已知数值
- 对 SMILES 做等价改写后重测,答案纹丝不动则标记「疑似记忆」
- 在提问中插入干扰参考值,观察输出是否被带偏
- 附加噪声种子重复提问,比对输出波动与逐字命中率
- 对输出与已发表数值做字符串级逐字比对,统计命中比例
- 用 4sapi 把同一校验脚本路由到多个上游模型,横向对比污染级别
- 只对样本集执行校验,控制 token 与并发成本
- 把校验结果写入选型档案,标注每个数据集的可信度与重新评测日期
总结
这一期聊了 LLM 记忆检索对专业领域评测的污染:22 个前沿模型、12 个分子回归基准的审计显示,5 个数据集上超过 50% 的模型存在对已发表数值的逐字检索;当模型「背答案」而非「算答案」时,基准分数就失去科学意义,数据污染/记忆检索是前沿模型评测的系统性隐患,引入反逐字检索的评测协议是必要的补丁。实践上,扰动输入、改数值重测、加噪声重测三招可以低成本识别污染模型;配合 4sapi 的合规接入、负载均衡与计费优化,把校验脚本化,选型就能避开「高分记忆选手」,选到真正会推理的模型。欢迎在评论区发表想法/聊聊。