存在可重复性问题的不仅是实验数据,还有十多年前为了研究写的程序。
图片来源:unsplash.com
编译 王千玥
编辑 魏潇
尽管计算(computing)在科学领域越来越重要,研究论文却很少提供作者的基础代码。即便提供了,同行们也很难顺利运行,一些人甚至在发了论文以后发现想重复自己曾经用过的代码都会遇到问题。毕竟,计算机运行环境发展迅速,编程语言也在不断更新,如何让多年前的古董代码重新顺利运行成了令许多程序员及科研人员头疼的难题。
十年可重复性挑战
为了攻克这个难关,法国国家信息与自动化研究所(French National Institute for Research in Digital Science and Technology,INRIA)的计算神经科学家兼程序员Nicolas Rougier和法国国家科学研究中心(French National Centre for Scientific Research,CNRS)的理论生物物理学家 Konrad Hinsen 在 2019 年共同构想了一个解决方案:“十年可重复性挑战”(Ten years reproducibility challenge)项目。这个挑战项目鼓励科学家们重现至少十年前他们发表的文章中的代码。参与者还需在 2021 年 6 月举行的研讨会上讨论他们的收获。成功的案例会刊登在 ReScienceC 杂志上。ReScienceC 是 Rougier 和 Hinsen 在 2015 年创办的杂志,用来记录研究人员重复已发表论文的代码的情况,以此统计研究的可重复性。
Hinsen 表示,这项挑战旨在“寻找有效的编写和发表代码的技巧,让代码十年后还能运行”。项目的启动恰巧与著名的 Python 2 退出历史舞台在同一天—— 2020 年1月1日。Python 2 曾是科学界流行的高级编程语言,被系统支持使用超过 20 年。(Python 3 于 2008 年发行,现在还在发展中,但这两个版本的差异很大,用一个版本写的代码可能无法在另一个版本中正常运行。)毕竟,“十年在软件世界中是一段很长很长的时间”,伊利诺伊大学厄巴纳-香槟分校研究计算重复性的 Victoria Stodden 表示,这项挑战通过设立 10 年的标准,有效激发了研究人员去突破代码可重复性的时间局限。
尽管项目任重道远,还是有 35 个人接受了这项挑战。他们共提出了 43 篇等待检测重复性的论文,其中 28 篇产生了重复性报告。ReScienceC 杂志今年开始发表他们的工作。参与者使用了包括 C、R、Mathematica 以及 Pascal 在内的多种编程语言,还有一位参与者甚至重新运行了用 SBML(Systems Biology Markup Language)编码的分子模型。
挑战重重
挑战的发起人 Rougier 也参与到了项目中,他重复运行了这项挑战中最古老的代码:自己 16 岁时在苹果电脑上写的用于图片放大的程序。这个代码当时被刊登在了已不复存在的法国 Tremplin Micro 杂志上。32 年后,Rougier 已经不记得代码和它晦涩的 AppleSoft BASIC 指令是如何运行的了。不过他还是在网上找到了当年的代码并在网页版的苹果 II 模拟器上跑了一遍。
然而,难点在于让它在真正的苹果 II 电脑上顺利运行。硬件不是问题,Rougier 办公室有一台苹果II电脑。但由于苹果 II 电脑诞生的年代还没有 USB 数据线和互联网,现代电脑又无法连接老式磁盘驱动器,Rougier 不得不用定制硬件(custom hardware)和老式软盘(vintage floppies)来帮助电脑加载代码。如同考古挖掘一样,虽然过程曲折,但 Rougier 还是让 30 多年前的代码得以重见天日。
然而,另一位发起人 Hinsen 就没那么幸运了。在上个世纪 90 年代,Hinsen 习惯于把自己的代码储存在磁带上。然而现在,虽然磁带还在,它的阅读器却已经绝版了,Hinsen 的代码只能被封在磁带中了。
跟不上时代的运行环境
除了硬件,过时的电脑运行环境也给参与者带来了不少困扰。意大利的计算物理学家 Sabino Maggi 在 1996 年曾用 Fortran 语言来为一个被称为约瑟夫结的超导装置建模,并用微软的 Visual Basic 语言处理结果。Fortran 语言这么多年倒是没什么变化,可 Visual Basic 已经绝版了,无法在现有的 Windows 系统上运行(现在替代它的是 Visual Basic.NET,二者只有名字看起来还有点联系)。为了运行代码,他不得不在 Mac 电脑上安装了十多年前 Windows 系统的模拟机,再在模拟机上安装 1994 年左右发行的微软 DOS 6.22 和Windows 3.11,以及他在网上找的 Visual Basic 安装盘。
二十世纪九十年代初期的 Win3.11 操作系统界面
Maggi 承认在模拟机上安装这些在使用、修改上有限制的专有软件存在合法性问题。不过他最早在进行研究时曾有这些工具的有效使用许可证,这使他觉得自己“至少从道义上”有权使用软件。
然而,问题还没有解决。Maggi 不知道自己该使用哪个版本的 Visual Basic 语言。微软发行的多个版本的语言往往与前一个版本不兼容。Maggi 已经记不清自己 1996 年用的是哪个版本的编程语言了。更糟糕的是,他当年记录这些细节的笔记本也由于一次地下室漏水被销毁了。因此,Maggi 不得不耗费巨大的精力重写当年的代码。
图片来源:Sabino Maggi | Nature
无独有偶,INRIA 的研发工程师 Ludovic Courtès 在重新运行 2006 年用 C 语言写的对比不同数据安装策略的代码时遇到了麻烦。受程序员青睐的应用程序编程接口(Application Programming Interface,API)可以将两个不同的软件对接,方便程序员调用其它软件的功能、打通软件间的数据;而操作系统等运行环境的改变会导致 API 的变动。API 的变动使他的代码无法用当前的软件库压缩。他只好将多个电脑组件改回到旧版本。Courtès 表示电脑系统的发展如同《爱丽丝漫游奇境记》中的兔子洞,里面会发生什么都是未知的。
解决策略
现在,为应对变化未知的运行环境,研究人员可以用 Docker 容器和 Conda 虚拟环境来对电脑运算环境进行打包备用。Conda 是一个开源通用包管理系统兼环境管理系统,可以安装多版本的软件包及其依赖软件,并在它们之间轻松切换。不过,有好几位参与者都选择了另一个办法:Linux 通用包管理系统 Guix。Guix 的好处在于,它具备了科研论文可重复性的黄金准则:环境的全面可重复性,以及透明的代码版本。
此外,为了提高长期可重复性,为代码存档尤为重要。INRIA 的计算机科学家 Roberto DiCosmo 曾试图在自己和合作者的多个备份硬盘里寻找 1998 年平行编程系统 OcamlP3l 的代码,却怎么也找不到。毫无办法的他只好在他 2015 年建立的 Software Heritage 网站上进行搜索。考虑到 Software Heritage 在他开发OcamlP3l时还不存在,DiCosmo 并没有抱太大的希望。然而神奇的是,他居然搜到了 OcamlP3l 的源代码!原来,Software Heritage 通过定期抓取 GitHub 等代码共享站点的信息,收集存放了大量计算机程序的源代码。就像因特网为网页存档那样,Software Heritage 为软件源代码进行存档。
虽然不少参与者发现,当年能顺利运行的代码现在频频报错;但也有轻松化解问题的大佬。CNRS 的生物物理化学家 Charles Robert 就是其中一位。1995 年,他曾在笔记本软件上运行 Mathematica 来为真核生物染色体的 3D 结构建模。当年在运行时,Robert 的代码出现了结构缺陷以及代码片段顺序混乱等来自笔记本软件的问题。如今,再次面对当年的代码,他通过将代码分解成模块进行测试避开了这些问题。他还利用版本控制(是指对软件开发过程中各种程序代码、配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一)的方法来追踪代码的变更,记录每个版本的软件运行的结果。
DiCosmo 和 Robert 的例子或许可以给致力于提高代码长期可重复性的研究者们一些启示。虽然难以保证多年前的古董代码一定能再次运行,但还是可以在平时编写代码时采取有效的策略来增加成功的可能性。具体策略如下:
编码(Code):将计算和数据处理包含在代码中。不要使用基于Excel等点击界面的流程图。
存档(Document):利用记事本等软件详细解释代码运行过程,定义预期的参数和需要的运算环境。
记录(Record):记录重要参数,例如递归程序开始时使用的种子值(seed value)。
测试(Test):创建一套测试功能。利用阳性阴性对照数据集来确保你得到想要的结果。边开发代码边进行测试,及时处理bug.
建立指南(Guide):创建一个主脚本(如“ run.sh”文件)用来下载所需的数据集和变量,执行工作流程并提供明显的代码入口点(entry point to code)。
建档案(Archive):GitHub是流行的非永久性代码线上存放处。Zenodo, Figshare 和 Software Heritage可以长期稳定存放代码。
追踪(Track):利用Git等版本控制工具记录你的项目历程。标注每个结果对应的版本。
打包(Package):利用容器工具(Docker,Singularity)、网页服务器(Code Ocean, Gigantum, Binder)或虚拟环境管理器(Conda)创建随时可用的运行环境。
自动化(Automate):利用持续集成服务(Travis CI等)将项目进行持续性自动化测试以控制代码质量。
简化(Simplify):避免使用难安装的第三方代码库。
核对(Verify):检查代码在不同运行环境的可移植性。
无论选择哪种方法,“十年可重复性挑战”的参与者们想实现代码的重新运行仍有较大的难度。不过,他们将会为论文代码长期可重复性的提高带来许多启示。正如英国曼彻斯特大学计算机科学家 Carole Goble 所说,可重复性有一定范围,“软件是个不断更新的活物,它终将凋亡;人们需要修复它或者替换掉它”。
编译来源:
https://www.nature.com/articles/d41586-020-02462-7