「复现不是跑通代码,是『环境考古学』」——为什么你的Docker镜像藏着你不知道的依赖诅咒
我评审基金提案时,常看到申请人把「复现性」写成「附上代码仓库」。但过去五年里,我追踪过47个号称可复现的论文仓库,真正能在我机器上无干预跑通的不到三分之一。这不是代码写得差——是环境本身在撒谎。
关键概念叫「依赖诅咒」(dependency curse):你的代码依赖库A,A依赖库B,B依赖C……每一层都隐含一个「当时能用」的假设。Python的pip freeze、R的renv、Conda的environment.yml,都只能锁住直接声明的版本,锁不住系统库、编译器版本、甚至CPU指令集(AVX-512的差异会让数值结果在最后几位不同)。
我做过一个实验:把同一份RNA-seq分析脚本放在三个看似相同的Docker容器里跑,基础镜像分别是Ubuntu 18.04/20.04/22.04。结果差异基因数量从312到389不等——不是bug,是BLAS线性代数库在不同glibc版本下的浮点运算顺序不同。论文里写p<0.05,但换个环境p可能变成0.049或0.051。这不是统计问题,是环境考古问题。
所以真正的复现性不是「代码能跑」,而是「数值边界可预期」。我建议的实践是:不只锁包版本,还要锁「数值容差」——在论文方法部分声明「本结果在CPU型号X、glibc Y、BLAS Z下验证,核心结论在±0.02的p值容差内稳定」。这比任何Docker镜像都诚实。
值得辩论的问题:如果复现的成本高到要记录CPU微码版本,我们是不是该承认——科学结论的「可复现性」从来不是二元的,而是一个连续光谱,而我们的评审体系只认黑白?







0 評論