如何實現科技論文裏面的算法

這是一篇關於如何實現科研論文中算法的簡要指南。作者曾實現過很多書本上和科研論文中的複雜算法,在這篇文章中作者總結他在研究,閱讀,編碼和調試時積累 的大量經驗。很顯然,這篇文章主要集中在和計算機科學相關的研究領域中。 然而,你也可以在其他任何領域的論文中使用下面提及的準則。

1. – 開始之前

在你開始閱讀一篇論文和實現它之前,有幾個你需要注意的地方。並請確保每次你要開始類似的項目之前,都仔細的注意過這幾個方面。


1.1 – 看看是不是已經有開源軟件實現過

除非你是爲了純粹的學習目的而去實現一篇文論中的算法, 否則你不必一定要實現它。實際上,你所需要的不是自己去實現它的過程,而是已經實現了這個算法的代碼。所以在你開始編碼之前,你應該花一些時間去找找網上 是不是已經有實現了這個算法的開源軟件。想想你是願意花兩天時間去找到已經完成的代碼,還是浪費兩個月的時間去實現一個別人已經實現的了算法呢?   

1.2 – 用最簡單的方法達到你的目的

你要先搞清楚你想要達到的目標是什麼,以及是不是有簡單的方法已經可以達到你的目標。 或許你可以嘗試使用另外的技術,即使它只能達到你80%的目標,但它不需要你去實現一篇論文。然後你可以再花幾天的時間去嘗試是不是可以用開源軟件運行起 來。關於這個更多的詳情,請參考我的另外一篇文章 The 20 / 80 Productivity Rule.

1.3 – 注意軟件的專利

如果你再美國, 你需要注意軟件的專利問題。有一些論文是註冊了專利的,你可能因爲在商業軟件中使用了它的算法而惹上麻煩。

1.4 – 關注更多該領域的論文

如果你正在研究一篇關於在計算神經學領域中使用支持向量機(SVM)的論文的話,那你應該再讀一些關於機器學習以及其他可替代支持向量機(SVM)的分類算法的介紹。同時,你還可以讀一些關於計算神經學領域的文章,看看學術界正在研究什麼.

1.5 – 保持積極性

如果你還沒有實現過一篇論文或你正在研究一個新領域內的論文,這樣的閱讀研究是很困難的。不管發生什麼,不要讓那些複雜的數學公式嚇到你。而且,不要去擔 心進度的問題,即使你感覺理解這篇論文要比你預計的要慢很多, 堅持做下去你會發現你慢慢的就理解了這篇論文中所要表述的概念。 

其它翻譯版本(1)

2 – 三種類型的論文

隨便選一篇論文,馬上開始實現絕對不是一個好注意. 世界上有很多很多的論文,這也意味着有很多很多的垃圾論文。所以發表的論文可以分成三類。

2.1 – 創新型論文

這是一些非常有趣的,寫的很好的,原創性的論文。大部分這類論文來自世界頂尖大學或者是已經研究某個領域很久的小學校的研究團隊。後者很好區分,他們往往 都在自己論文中引用自己之前的研究成果,已展示他們對這個問題的研究已經很長時間了, 而且他們最新的研究工作都是基於已經證實了的論文之上。 同時,這類型的論文基本都在該領域內最好的期刊雜誌上發表。

2.2-模仿型論文

一些研究羣體僅僅只是跟隨哪些有突破性創新的團體,他們的目的就是提高已經發表的論文(算法),並且發表他們提高後的結果。很多這樣的論文缺少合適的統計 分析和錯誤的結論,這樣的提高其實是破壞了原有的算法。很多時候,他們這樣做並沒有帶來任何東西,除了帶來不必要與而外的複雜性。但也並不是所有的模仿是 不好的,有些還是不錯的,但卻很稀少。

2.3-垃圾型論文

一些研究者並不知道他們做的事情是非常不好的。他們僅僅是爲了維持他們在自己學術領域中的狀態與特權。所以他們需要資金,並且他們需要出版任何他們需要的 東西。比較誠實的研究者會告訴你,在結論中他們失敗了,在結果中只有N%的時間(N是有比較差的值)。但一些心懷不軌的研究員會說謊話,他們會說他們的研 究取得了巨大的成功。在閱讀出版物一段時間以後,很容易就能發現那些垃圾型論文,並拋棄他們。

3-如何閱讀科技論文

這個話題已經討論過很多次,因此不打算繼續寫很多關於這個問題的探討。一個好的出發點可以參見這篇文章:How to Read a Paper ,下面的一些觀點是我在閱讀科技論文時候發現的有用的觀點。

3.1-找到正確的論文

原始論文就是你想實現的是什麼,描述的是這個領域的開端。有時候也可以選擇相似的論文,前提是你覺得這篇論文是能帶來算法上本質的提升並且有良好的一致性卻不成熟的創新的文章。

那麼讓我告訴你以該論文作爲出發點。你需要在該論文相關領域做些研究。對此,方法就是尋找相關聯的論文,即是那些在該論文引用部分裏面所提到的論文。再去Google Scholar依 據論文題目和作者搜索。你找到的論文是否比你現在看的論文所做的效果更好呢?如果是,那麼就去研究找到的新論文。Google Scholar還有個優點就是能標註論文的引用。這個非常棒,因爲你能從一篇論文的引用鏈接找到另外的論文,這樣可以找到某個研究領域最新的成果。總體來 說就是從當前論文的引用中尋找新的論文。通過這樣來回搜索,就會找到適合你的高質量的論文。

重點:在這個搜索論文過程中,你不需要從頭到尾仔細閱讀理解論文。只需要大體瀏覽論文並用你的直覺去判斷論文的價值。


3.2 – 不要在屏幕上閱讀

打印成紙質閱讀,打印時不要降低字體大小,如果你降低了字體大小可能節省了幾頁紙卻使你在閱讀時因爲眼睛更易疲勞而浪費了更多的時間,字體大小一般建議在11pt-13pt

3.3 –選擇恰當的閱讀時間和地點

不要在半夜閱讀,最好在大腦清醒時才閱讀.也應該找一個安靜、光線適中的地方。我通常都在臺燈下閱讀。

3.4 – 注意標記和備註

標記重要信息,並在重要信息旁寫下閱讀時腦袋中浮現的各種想法。


3.5 知道所有術語的定義

通常你閱讀大多數文章和小說時,對於生詞你的大腦會象計算機一樣根據上下文自動得出意思。但是科技出版物完全不同,其中最多的錯誤就是單詞意思的錯誤假設。比如說句子“The results of this segmentation approach still suffer from blurring artifacts”中“segmentation”和“artifacts”通 常有着“分割”和“手工品”的意思,但是在計算機視覺領域有着特殊的意思。如果你閱讀這篇論文時沒有注意這個,你的大腦用了他們的通常意義去理解,你會漏 掉很重要的信息。因此你必須(1)避免假設單詞的意思,任何時候有疑問需查閱專業詞典(2)抄寫一張你不熟悉的專業生詞概念表做參考。如果你遇到概念比如“faducial points”和“piece-wise affine transform”,首先你應該查出他們的準確的專業含義,然後加在你的生詞表裏。概念是語言式的大腦快捷鍵可以幫助你更快理解作者的意圖。


3.6 – 關注結論中的統計分析

如果論文作者僅僅用曲線展示他的算法和其他算法的差異,並且說“你看,比原有算法準確了20%”,這種論文就一垃圾論文. 你應該閱讀的是那種在論文中說: “在N個實例的測試集中,我們的算法在兩個樣本測試中檢驗值提升了5%(這句真不好翻譯)” 統計結論更多的證據應該來自數據而不是作者的個人想法 (除非作者撒謊誇大他的結論).

3.7 – 請確定論文所展示的結論是你所需要的

假設你需要一個能夠從圖片中人臉識別的算法.作者在論文結論中說他的模型用80個人不同照片(10x 80 = 800張圖片)進行訓練, 對訓練集進行測試時人臉識別準確率達到98%,但是對測試集(測試的圖片沒有被用於訓練集)進行人臉識別時準確率只有70%。這樣的描述是什麼意思呢?它以意味着該算法仍然存在問題(這句真不好翻譯)——該算法當用於對訓練集進行識別時是非常不錯的(但種情況沒有任何實際意義),但是用於對測試集進行識別時就會非常糟糕。你也可以通過這點,來判斷一篇論文是否是你所需的


3.8 注意作者用的輸入數據

如果你想用網絡攝像頭做臉部識別,而作者的輸入數據用的是出自高清相機的相片,那麼算法結果可能會完全不同。必須保證你的輸入數據近似與算法測試數據,否則會造成算法完全無用在你的case。

3.9 作者也是人

人都會犯錯,不要認爲作者是神。特別是算式真的很難理解,你應該問問你自己是否作者寫錯了。這可能僅僅是論文的抄寫錯誤或一個數學計算錯誤。不管怎麼說,最好的方法是自己做做計算去驗證結果。


3.10-理解變量與運算符

實現算法的主要的工作量在於將紙面上的數學算式轉化爲代碼以及數據。這意味着在轉化完畢之前,你必須百分百理解那些數學式子到底是在做什麼。例如“C=A.B”可能有很多意義。A和B

可以是簡單的數字,而“.”是一個數字運算符號。這種情況下,C的結果可以是A和B的和。但是A和B也有可能是兩個矩陣,這種情況下,“.”又可以作爲一 個矩陣運算符存在。這種情況下,C是A和B兩個矩陣運算的結果矩陣。唉……其實還有個可能,A,B都是矩陣,然後“.”是一個矩陣加法運算符,這種情況 下,每一個C(i,j)都是A(i,j)和B(i,j)的和。變量和符號到底代表什麼意思(矩陣、向量、數字、座標,還是其他?),這值得你好好研究下。 (譯者注:其中關於矩陣的運算符號,我不清楚這幾個英文單詞都對應哪些符號……)

3.11-弄清數據處理流程

論文就是一堆方程的大雜燴。在你開始寫代碼之前,你需要了解如何把N根據要求變成N+1


4-模型

一旦讀懂了論文,接下來你應當設計一個模型。這是一個非常重要的步驟,通過這一步你可以避免大量的資源以及時間的浪費。要把一個算法用c,c++或者 java寫出來是非常費時間的。即使你認爲自己對論文有足夠的瞭解而且算法肯定會按照你所想的去工作,這裏仍然有個問題:如果他完全不動彈呢?所以如果你 想盡快將算法轉化爲代碼,還是先建立一個模型檢驗一下他能否工作吧。

4.1-模型建立方法

最好的方法當然是使用高級通用語言來建立,比如matlab,R,Octave或者SciPy/NumPy。像c++這樣的工程語言一般不太容易用於描述 一個數學方程並輸出結果以進行人工檢查。相反的,在matlab上這就很容易實現。你用c++兩三週纔可以幹完的事情或許matlab上面只需要兩天。


4.2-用模型進行調試

模型的另外一個應用是進行調試,特別是當你開發出你的c++版本以後,你可以通過你的matlab版本的輸出與c++版本進行對比。對該條的進一步描述請參看“調試”(譯者注:“7-調試”)章節。

4.3-模型可以精實你的程序

你肯定會在模型建立當中遇到一些問題。這是好事情,因爲你可以從中發現數據處理的難點在哪裏。當你編寫c++代碼的時候你就知道如何更好的構造程序,因而 使他更加的可靠簡潔。而如果你沒有經歷過模型構造這一步,你的代碼大概會走向相反方向(這一點可以從Frederick Brooks的《人月神話》中找到)。

4.4-檢驗文章中的試驗結果

請仔細閱讀“試驗”(譯者注:我沒有找到相關章節)這一章節,然後嘗試使用論文中給出的數據重複試驗。這將會使你可能看到作者文章中所描述的結果。如果不 能較好的重複文章中的試驗,你將在試驗中看到不一樣的結果,讓你疑心是否是文章有問題。而其實你只要較好的重複試驗並輸入文章中的試驗數據你就可以看到文 章所描述的結果。然後你就可以開始測試其他不同的數據了。


5 - 正確地選擇實現語言和庫

在這一階段,你必須已對論文提出的算法和概念有了一個清晰的認識,而且必須能有一個可運行原型程序,以確認算法能夠在你預想的輸入數據下,正常的工作。好的,進入下一步,確定你要使用哪種語言和框架來實現論文的算法。

5.1 - 現有體系

大多數情況,現有體現已經指定了實現語言和庫。實際的例子:你有一套Java實現的計算圖像照度歸一化的算法庫,而你想要將論文的新算法添加進庫。這種情況,很顯然,你要使用Java來實現新算法,而不是C++。


5.2 - 前瞻算法的應用場景

在沒有現有體系限制你選擇語言的情況下,則要基於對該算法在將來的使用場景的前瞻來選擇實現語言。比如,如果你堅信在四到六個月後,你的應用程序將會實現 面向iPhone的接口,那你就應該更傾向於選擇C/C++,而不是Java,因爲如此可以很簡單的將算法代碼集成的Objective-C的應用中,而 不需要重頭來過。

5.3 - 參考能夠解決或部分解決算法問題的現有庫

不同語言實現的現有庫也會影響我們對實現語言的選擇。讓我們來想象一下,你想要實現的算法使用了諸如主成分分析(PCA)以及單值分解(SVD)等經典的 代數技巧。那你是重頭編碼實現PCA和SVD算法,遇到bug,可能還要停滯一週來調試自己的代碼?還是使用現有的,已經實現了這些功能的庫,使用他們提 供的常規接口和矩陣類來實現自己的編碼?理論上講,你應該將你的實現工作分解爲若干個子任務,然後嘗試儘可能多的尋找能夠解決這些子任務問題的現有庫。如 果你發現有一套完美解決你的問題的庫,且僅有某一語言版本提供,那麼,就使用這種語言。同時也要注意,在選擇庫的時候,需要在代碼的重用及最小依賴之間做 出權衡。沒錯,如果有能夠解決你所有子任務問題的代碼是再好不過了,但是如果這需要依賴超過20各不同的庫,那實在是不太實用,甚至危及你的實現代碼將來 的穩定性。


6 – 實現

實現算法的一些個人經驗

6.1 – 選擇正確的精度

通常最好用double代替float,雖然使用double內存會佔用更多,但卻改善了精度,這是值得的.另外,你需要注意32bit和64bit操作 系統之間的不同處.如果可以,自己創建一個封裝(封裝了float或者double,32-bit或64-bit)類型,然後在代碼中使用封裝了的數據類 型,在c/c++中可以使用define實現,java中可以使用class來實現.

6.2 – 爲代碼做好註釋

雖然文檔維護會顯著降低項目進度,但在實現一個複雜的技術論文時,你最好做好文檔.即使項目只有你一個人,你也應該對你的文件、類、方法進行註釋說明.選 擇一種轉換方法如Doxygen或reStructuredText,並堅持使用它.在以後的開發中,你可能會忘記某個class是如何使用的或者某個方 法的實現方式,你就會覺得當初的文檔太有用了.


6.3 - 在你的代碼中添加引用

對於來自你所要實現的論文的每一條方程,你需要添加一段註釋引用這篇論文(作者和時間)以及段落號碼或方程號碼。這樣,以後重讀代碼時,你可以直接把代碼和論文中的準確位置聯繫起來。這些註釋就像:
// 參見 Cootes et al., 2001, 方程 2.3
// 參見 Matthews and Baker, 2004, 章節 4.1.2

6.4 - 在變量名中避免數學符號

我們假設一些數量在算法中是一個矩陣表示爲A。然後,算法要求矩陣在兩個維度上的梯度,表示位 dA = (dA/dx, dA/dy)。那麼變量名不應當爲"dA_dx"和"dA_dy",而是"gradient_x"和"gradient_y"。相似地,如果一個方程系統 要求收斂判定,那麼變量不應爲"prev_da_dx"和"dA_dx",而是"error_previous"和"error_current"。把變 量永遠命名爲它們所表示的物理數量,而不是論文作者使用的任何字母符號(例如"gradient_x"而不是"dA_dx"),並且永遠要把更具體的表示 寫在左邊(例如"gradient_x"而不是"x_gradient")。


6.5-不要一開始就優化

請把優化這種事情往後面放放。否則你絕對不可能確定你的代碼到底是哪裏出了問題。而每當你進行優化,你最好添加一些註釋來進行解釋,例如:

//優化:一次計算矩陣一列以減少內存開銷

通過這種方式,不論是什麼語言,你都可以很快的從你的代碼中發現到底是哪處優化導致了問題。

6.6-打算創建一個api?

如果你打算把你的代碼構建成一個api,你需要注意的是如何如何創建接口。對於這一點,我建議你使用Joshua Bloch How to Design a Good API and Why it Matters書中的“面向函數庫編程”(譯者注:原文“coding against the library”真的不知道怎麼翻譯……)技術。


7 - 調試

實現一個新算法如同燒一道你之前從未吃過的菜。即使它味道不錯,你也不知道這是不是它原本的味道。和燒菜不同的是,軟件開發有一些有用的訣竅來增添我們在實施中的信心,所以我們還算運氣不錯。

7.1 - 和其他實現比較結果

一個淘出bug的好方法就是把你代碼的結果和同一個算法的已有的實現的結果進行比較。假設你正確完成了上面“着手之前”章節提及的所有任務,你會發現該算 法還沒有任何可行的實現(否則你就該直接用它,不必去實現那篇論文)。所以,你眼下的另一種實現只能是你以前編寫的原型。


這個想法就是在算法的每一步比較原型和產品實現的結果。如果結果不同,那麼兩者其一出了錯,你得找到哪個出了錯以及出錯的原因。精確度可能變化(原型給出x=1.8966但產品卻給出x=1.8965),比較時也要將此考慮進來。

7.2 - 和讀過論文的人談談

一旦兩個實現(原型和產品)的所有步驟給出相同的結果,你就可以在一定程度上相信你的代碼沒有bug。但是,還存在你對論文理解錯誤的風險。在這種情形 下,兩個實現都會在每一步給出相同的結果,你會認爲你的實現還不錯,然而這隻能證明兩個實現同樣都是錯的。不幸的是,我也不知道該如何察覺到這種錯誤。最 好的選擇是找一個讀過這篇論文的人,並問他關於算法中你不確定的部分的問題。你甚至可以設法詢問作者,但你得到回答的機率非常低。


7.3 - 可視化你的變量

開發時,你應該留意算法中用到的變量的內容。我不是說僅僅打印出你所有矩陣和數據的值就可以了,而是找到適合你實現中的任何變量的可視化訣竅。例如,如果 一個矩陣用來表示一張圖片的梯度,那麼在編碼和調試過程中,你應當有一個彈出窗口,展示出那張梯度圖,而不只是圖片矩陣中的數值。這樣,你就會把真實的圖 片和你要處理的數據聯繫起來,並且在變量出現問題時,你可以察覺到,這可能將是一個bug。創造性的可視化訣竅包括圖像、散點圖、曲線圖,但絕不是一張包 含1000個數字的愚蠢列表,然後你必須再依靠這張表在腦海中呈現出圖像。


7.4 - 測試數據集

生成數據來試驗你的實現可能非常耗費時間。無論何時,只要你能,就設法去找到數據庫(人臉數據庫、原文摘錄數據庫等等)或者生成這種數據的工具。如果沒有,那麼不要把時間犧牲在手動生成1000個樣例數據上。編寫一個20行以內的快速數據生成器,再用它幹活。

總結

這篇文章中,我已經展示瞭如何實現科學出版物的良好實踐。記住,這些僅僅基於我的經驗,不能一字一字地盲目效仿。當你閱讀和編碼時,永遠要小心,並用你的判斷力來決定上面哪條準則適合你的項目。也許有些做法對你項目的傷害會比好處更多,這就靠你去發現了。

現在,去實現一些很酷的算法吧!


發佈了28 篇原創文章 · 獲贊 15 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章