程式碼老矣,尚能跑否?

語言: CN / TW / HK

之後他黑進了軍方網路裡,險些引發了一場全球性的熱核戰爭。Rougier的需求就沒這麼刺激了。他只想從自己的Mac桌上型電腦往一臺老古董電腦上傳一個文字檔案——1977年的出品的Apple II。這是蘋果公司的第一部消費產品。

Rougier是法國國家資訊與自動化研究所(INRIA)的計算神經科學家兼程式設計師。傳這個檔案是他自己提出的計算挑戰的最後一步:十年程式碼復現大挑戰(Ten Years Reproducibility Challenge, https://rescience.github.io/ten-years/)。2019年,他和法國國家科學研究中心(CNRS)的理論生物物理學家Konrad Hinsen共同發起了這個挑戰,要求找一份老程式碼並重新執行,從而對至少已發表了十年的以計算為主的論文進行復現。原本的計劃是參與者在波爾多6月舉辦的研討會上討論心得,但是因為COVID-19被迫延期(目前暫定延期到2021年6月)。

雙胞胎計劃的插圖

雖然計算在科學中起到了越來越關鍵的作用,但科學文章很少會包含計算用的程式碼,Rougier說。即使有包含,也很難由其他人執行,甚至連原作者過一段時間後,都可能在執行時遇到問題。程式語言在發展,執行程式碼的計算機環境也是一樣。今天可以順利執行的程式碼明天可能就會出問題。

2015年,Rougier和Hinsen創辦了《 ReScience C 》。這份期刊刊載的是研究人員如何基於原始論文和自行編寫的開原始碼來複現其他人的計算方法。評審人再研究程式碼以確認它是否能用。但即使在這種理想化的場景——作者有意願讓程式碼復現、評審人純熟於計算領域、程式碼也是新寫的——整個流程依然有很多難點。

十年程式碼大挑戰的目標是“找出十年前哪些寫作和釋出程式碼的技術好到如今依然可用”,Hinsen說。挑戰的時間設在了2020年1月1日這個Python 2“退場”的時間點。這個在科學領域非常流行的語言在出現20年之後決定終止支援。(2008年出現的Python 3仍然在繼續開發,但是這兩者之間的區別比較大,用其中一種寫成的程式碼在另一個環境下可能無法執行。)

“在軟體的世界裡,十年是很長很長很長的時間。”Victoria Stodden說。她在伊利諾伊大學厄巴納-香檳分校研究計算的可復現性。作出這一論斷後,她說這個挑戰本質上是鼓勵研究者探索程式碼復現的極限,能不能在一個“對軟體世界來說幾乎無窮長的時間”裡復現。

一共有35個挑戰者。在他們提出要復現的43篇文章裡,其中28篇提交了復現報告。《 ReScience C 》從今年初開始刊載他們的工作。使用的程式語言從C和R到Mathematica和Pascal;一位挑戰者復現的不是程式碼,而是用系統生物學標記語言(SBML)編碼的分子模型。

雖然是在數碼世界,但挑戰者的經歷和現實世界的考古一樣可以借古喻今,提出未來複現程式碼的最佳策略。其中一個共同點是,科學家想要復現程式碼就必須優化文件。“2002年,我覺得我所有東西都能記一輩子,”威斯康星大學麥迪遜分校的生物統計學家Karl Broman說,“之後我才意識到,過不了一個月就會忘了。”

重現科研

Rougier的參賽作品重現的是整個挑戰裡最老的程式碼[1],他在16歲時為Apple II寫的影象放大器,文章發表於一份已經停刊的法國業餘愛好者雜誌《 Tremplin Micro 》(挑戰裡最老的科學程式碼是一份28年前的繪製水質資料的Pascal程式,之後會在《 ReScience C 》上發表)。32年之後,Rougier已經記不清程式碼是怎麼運作的了,它用的還是長得像咒語一樣的AppleSoft BASIC程式碼——“挺奇怪的,畢竟是我本人寫的”。但他成功地在網上找到了這份程式碼,並用網頁版本的Apple II模擬器成功執行。這一步比較簡單,他說,在實際的Apple II上執行才是真正的困難。

硬體並不是問題——Rougier在辦公室有一臺Apple II,是他同事清理辦公室的時候撿回來的。“年輕人會問‘這是個啥?’”他說,“然後你就得解釋‘這是臺電腦’。老人看到就會說‘哦,我對這機器有印象’。”但是因為Apple II比USB和網際網路還要早——而現代的電腦也沒法直接和老式硬碟連線——Rougier就需要一些自制的硬體,外加上一盒老式軟盤,才能讓電腦讀取程式碼。他在亞馬遜上找到了這些東西,1993年造的“全新”品。寫入三次保證所有位元都穩定之後,他確認了這些軟盤可用。

INRIA研究中心的電腦科學家Bruno Levy評審了Rougier寫的內容。Levy也有一臺Apple II,還在推特上釋出了一小段影片。在老式鍵盤的一聲“咔噠”之後,他呼叫了程式碼併成功執行,緩緩顯示出一行純綠色的“我們重現科研!”。

過時的硬體,已死的語言

當法國國家科學研究中心的生物物理化學家Charles Robert聽說這一挑戰的時候,他決定用這個機會回顧一個他很多年都沒再思考的研究課題。“這個挑戰給了我臨門一腳,讓我再向那個方向努力一把。”他說。

1995年,Robert用運行了商業軟體Mathematica的計算筆記本為真核染色體的三維結構做了建模。Robert在MacBook上有Mathematica,但是為了好玩,他花了100歐元(約合800人民幣)買了一臺Raspberry Pi,這是個愛好者玩的微控制器,上面安裝了Linux系統,並預安裝了Mathematica 12。

Robert執行程式碼時基本沒什麼問題,但是暴露出了計算筆記本可能會引發的難點[2],例如缺乏程式碼結構,而程式碼段也可能不按順序執行。到了今天,Robert通過將程式碼拆成模組並寫了程式碼測試。他還使用了版本控制來追蹤程式碼的修改,並記錄了哪個版本的軟體產生了什麼樣的結果。“當我讀到老程式碼的時候,偶爾會起雞皮疙瘩,然後思考現在能如何做得更好,”他說,“不過,我還覺得整個過程讓我複習了從那之後學的一些知識。”

成功完成挑戰的Robert並非孤例:至今為止發表的13份重現論文中只有2篇失敗了。其中之一是Hinsen寫的,1990年代初他用來系統性儲存程式碼的磁帶讓他栽了跟頭[3]。“這就是做了備份卻沒能在十年後檢查備份是否能讀的下場。”他說,“之前你有這套很好的磁帶,還有備份,但是現在沒有讀取裝置了。”(Hinsen還發表了一篇成功完成的文章[4])其他沒能成功完成挑戰的參與者歸因於時間不足,特別是在疫情之下。

挑戰者遇到的另一個普遍問題是過時的計算環境。現在在義大利國家研究委員會的大氣汙染研究所任職的計算物理學家Sabino Maggi曾經使用程式語言Fortran為一種叫做Josephson結的超導裝置進行了建模,並用微軟的Visual Basic處理了結果。在那之後,Fortran的改變不多,因此Maggi只微調了一些就成功編譯了程式碼。Visual Basic則造成了更大的麻煩。

“Visual Basic,”Maggi在文章[5]裡寫道,“是一門已死的語言,已經被Visual Basic.NET取代很久了,而兩者之間只有名字一樣。”為了執行程式碼,他不得不在Mac筆記本上重構了一個十年前的Windows虛擬機器。他用網上找到的安裝盤裝了微軟DOS6.22和Windows3.11(都是1994年前後的軟體)以及Visual Basic。“即使是很久以前的軟體,使用模擬器安裝版權軟體仍然可能有合法性的問題。”Maggi承認。不過,因為他當時做科研的時候有合法的證書,他說他覺得“至少道德上有資格”使用。

但是該用哪個版本的Visual Basic?微軟在幾年內釋出了好幾個版本的Visual Basic,並且不都是向前相容的。Maggi已經記不起1996年他用的是哪個版本的了,而地下室的一次漏水摧毀了他早年記錄這些細節的筆記本。“我得從頭開始了。”他說。

在Mac上執行1994年Windows的模擬器以執行微軟的Visual Basic。來源:Sabino Maggi

INRIA的研究工程師Ludovic Courtès重現了一份2006年的研究,內容是比較不同的資料壓縮策略,程式碼是用C語言[6]寫的。但是程式設計師所使用的應用程式介面(API)變了,因此他的程式沒辦法用現代的軟體庫編譯。“所有的東西都在進化——當然了,只有論文裡用到的那段軟體除外。”他說。最後,他不得不將五六個程式庫回退到了老版本——他稱為“降級的連鎖反應”。“這坑有點深。”他說。

今天,研究者可以使用Docker容器[7]和Conda虛擬環境[8]對計算環境進行包裝以便於重用。但是幾個挑戰者選擇了另一種方式。Courtes說這“很可能代表了重現科研論文的‘黃金標準’”:一個叫做Guix的Linux軟體包管理系統。它保證環境可以連每個位元都完全重現,並且對於程式碼連結時的版本完全透明。“整個環境,事實上整篇論文都可以從原始碼開始檢視並連結。”他說。Hinsen將它稱為“可能是目前為止重現科研最好用的東西。”

需要文件

在INRIA和巴黎大學的電腦科學家Roberto DiCosmo嘗試重現[9]的論文中,他提出了另一個挑戰者常見的難題:尋找自己到底把程式碼放哪了。DiCosmo挑戰的是1998年的一篇論文,其中描述了一個叫做OcamlP3l的並行程式系統。他搜遍了硬碟和備份,還請1998年的合作者們也搜了一遍,但是什麼都沒找到。之後他搜了自己2015年建立的一個服務Software Heritage。“找到了,不可思議。”他說。

Software Heritage定期爬Github一類的程式碼分享站,和Internet Archive備份網頁一樣備份原始碼。開發者也可以要求該服務備份自己的庫,而挑戰的規則也要求挑戰者這樣做:DiCosmo並不是一開始就去Software Heritage上搜索的,因為他開發OcamlP3l的時候Software Heritage還沒出現。不過,不知道是誰把他的程式碼發到了一個叫Gitorious的庫上。Gitorious現在已經消失了,但在那之前被Software Heritage備份,上面的OcamlP3l也就一起被收錄進去。

當然了,找到程式碼不意味著就知道該怎麼用。比如說,Broman的文章裡就提到,他在重現2003年一篇論文[10]的時候因為缺乏文件和“古怪的”檔案結構而花了很大力氣才搞明白到底該執行哪個程式碼。“結果到頭來我得花功夫去讀當初的那篇論文。”他寫道。

“(在結構良好的程式裡)文件比程式碼長並不是罕見的事情。”在加州大學伯克利分校重點研究計算可重現性的Karthik Ram說,“有了足夠詳細的文件,再更廣泛地描述分析方法,資料來源,資料和程式碼的元資料,這些都是很關鍵的。”

愛丁堡大學的神經科學家Melanie Stefan利用這次挑戰評估了她用SBML寫的計算模型的可重現性。雖然程式碼很好找,但是她找不到之前使用的引數了(例如分子濃度)。資料歸一化時的關鍵細節也沒有詳細記錄。結果,Stefan無法重現一部分研究。“你做科研時候差不多是顯而易見的事不再那麼顯而易見了——對10-12年之後的你來說。誰能想到!”她自嘲。

可重現性的光譜

Stefan的經歷驅使她給實驗室訂下了文件上的規章——例如,模型中必須附上這樣的說明:“想重現圖5的話,需要按以下步驟執行。”

但是寫這些資源需要時間,Stodden說。清理程式碼並補充文件,撰寫測試,整理資料集,重現計算環境——“這些工作量都不出成果”。研究者沒什麼動力去做這些事,她補充說,而科學界關於可重現的論文應當長什麼樣也沒什麼共識。讓問題進一步複雜化的是計算系統還在繼續進化,因此難以預測哪種策略能一直有效。

可重現性是一條光譜,曼徹斯特大學的電腦科學家、研究可重現性的Carole Goble說。從科學家復現自己的研究,到同行評審人試執行程式碼以證明其有效,再到研究者將發表的演算法應用在新資料上。類似地,研究者為了保證可重現性所做的事情也能夠成一條光譜(見下“可重現性檢查表”),但是這張表可能會很長。Goble說,把原始碼釋出出去,這樣至少未來其他人可以瀏覽並按需改寫——Goble管它叫“讀程式碼的重現手段”。“軟體是有生命的,”她說,“而有生命的東西終將腐朽,因此需要不斷修理,最終就得換掉。”

可重現性檢查表

雖然以下手段不可能百分之百保證計算可重現性,但是可以增大成功率。

程式碼- 如果你的計算過程是在圖形介面上點來點去,例如Excel,是不可重現的。將你的計算和資料操作寫成程式碼。

文件- 使用註釋、計算筆記本和README檔案來解釋程式的運作方式,並將預期的引數和所需的計算環境也定義好。

記錄- 記錄關鍵引數,例如隨機數生成器的種子。這類記錄可以用來重現程式碼,發現漏洞並追蹤意料之外的結果。

測試- 寫一套測試函式。使用正向和負向的控制組資料集來確保你能獲得預期的結果,並在開發過程中不斷執行這些測試以便在程式設計出錯時立刻發現。

指南- 寫一個主指令碼(例如run.sh檔案)來下載所需要的資料集和變數,執行計算流程併為你的程式碼提供一個顯而易見的入口。

存檔- GitHub是一個流行但是非永久性的線上程式碼庫。使用Zenodo、Figshare和Software Heritage這樣的存檔服務來保證長期的穩定性。

追蹤- 使用Git一類的版本控制工具記錄專案歷史。記錄產生各種結果的分別是哪個版本。

打包- 使用容器化的工具(例如Docker和Singularity)、網上服務(Code Ocean、Gigantum、Binder)或是虛擬環境管理器(Conda)設定可以即時使用的計算環境。

自動化- 使用持續整合服務(例如Travis CI)來自動、定期、在各種計算環境下測試程式碼。

簡化- 避免罕見或難以安裝的第三方程式碼庫,以簡化重用程式碼的難度。

驗證- 在不同的計算環境下執行你的程式碼,以確認其可移植性。

一個不怎麼符合直覺的事實是,很多挑戰者都發現使用更老的語言寫成的程式碼反而更易於複用。新語言的應用程式介面會頻繁更新,而它們所依賴的第三方庫則導致程式碼更易損壞。從這個意義上講,今年初Python 2.7的退役為科學家提供了一個機會,Rougier和Hinsen說。Python 2.7“讓我們有了一個保證不會再變化的高階程式語言。”Rougier寫道[1]。

無論研究者使用什麼樣的程式語言和可復現策略,實際驗證一遍都是明智之舉,謝菲爾德大學的研究軟體工程師Anna Krystalli說。Krystalli負責舉辦一個叫ReproHacks的研討會,讓研究者提交已經發表的論文、程式碼和資料,然後要求其他參與者重現其結果。她說,大多數情況下是重現不出來的:作者沒能提供一些他們看起來顯而易見而其他人卻不知道的關鍵細節。“無論我們在做什麼,如果不實際用一用,擺弄擺弄的話就不可能知道是否真的可以重現。”Krystalli說,“實際上,這比人們所想象的要難得多。”