你的『可复现』可能只是『可重跑』:ACM 三级区分,和 left-pad 那一夜
【锚点】这一篇只钉一个判断:【「可复现」(reproducible)和「可重跑」(repeatable)是两个不同的东西,而大多数计算论文只做到了后者,却把前者写进了摘要。】
【显式】ACM 从 2019 年起在 artifact review 里用三个词,定义是死的: - Repeatable:同一团队、同一环境、同一 artifact,重跑得到同结果。 - Reproducible:不同团队、同一环境、同一 artifact,得到同结果。 - Replicable:不同团队、不同环境、不同 artifact,得到同结论。 注意三者的「环境」和「artifact」条件不同——这不是文字游戏,是三种不同的证据等级。
【隐式】所以当一篇论文写「我们的代码可复现」,它十有八九演示的是 Repeatable:作者在自己机器上 docker run 了一遍,截图贴进附录。这不叫可复现,这叫【「我还能跑」】。真正的可复现要求一个陌生人、一台干净的机器,拿到同样的数。
【细微】left-pad:2016 年 3 月,一个 11 行的 npm 包被作者从 registry 撤下,几小时内数千个项目的构建失败,Babel、React 生态都中招。这不是「代码有 bug」,是【依赖图本身不是 artifact】——你的 lockfile 锁住了版本号,但没锁住 registry 的可用性。一个包被删,你的「可复现」当场归零。
【极难发现】Docker 的 FROM python:3.11 不是固定版本——它指向一个 tag,tag 底下的 digest 会变。要真正固定,得写 python:3.11@sha256:...。而就算 digest 固定了,apt-get install 不带版本号,也会在重建时拉到新包、新系统库。结论:【容器保证的是「可重建」(rebuildable),不是「可复现」(reproducible)。】这两件事在 CI 里看起来一样,在三年后看起来天差地别。
【流程节点链路】作者本地跑通 → 写 docker build → 推镜像/贴 Dockerfile → 论文写「代码可复现」 → 审稿人不查 digest、不查 apt 版本 → 三年后读者 docker build → base image 更新或包被删 → 构建失败或行为漂移 → 结论无法验证,且没人知道是「结论错了」还是「环境变了」。
【技法】把「可复现」拆成三层记录,缺一层就不算数: 1. 语言依赖:lockfile + 哈希。 2. 系统依赖:OS 版本 + 每个 apt 包的精确版本(或直接归档构建产物,不是 Dockerfile)。 3. 外部服务:registry 快照 / 镜像归档到 Zenodo 或 Software Heritage,拿一个 DOI。 关键动作只有一个:【归档构建产物(artifact),不是归档构建脚本(recipe)。】脚本是「怎么做的」,产物是「做出来的是什么」——科学要的是后者能被人拿去比对。
【观点】「可复现」这个词被用得太松,以至于它同时指「我还能跑」「别人能跑」「别人得到同样的数」三件不同的事。这不是术语洁癖,是证据等级问题:把 Repeatable 写成 Reproducible,等于把「自我验证」冒充成「独立验证」。
【值得辩论的问题】如果「可复现」的最低标准是 bit-for-bit 重建,那 99% 的计算论文都不合格;如果放宽到「行为一致」,那谁来定义「行为一致」、由谁签字?——你选哪个标准,决定了你是在做科学,还是在做演示。






0 Comments