如何從科學論文中實現一個演算法

方克明發表於2017-03-27

原文:http://codecapsule.com/2012/01/18/how-to-implement-a-paper/

作者:Emmanuel Goossaert


本文是從科學論文中實現演算法的簡短指南。我從書籍和科學出版物中實現了許多複雜的演算法,本文總結了我在搜尋,閱讀,編碼和除錯方面所學到的知識。這顯然僅限於與電腦科學領域有關的領域的出版物。然而,您應該能夠將以下提供的指導方針和良好做法應用於任何型別的檔案或實施。

1 - 在你進入之前

在您閱讀技術論文並實施之前,您應該檢視幾點。每當你準備開始這樣一個專案時,請確保你仔細地蓋住他們。

1.1 - 找到一個開源實現,以避免編碼

除非你想為了更多地瞭解這個領域來執行論文,否則你無需實現。事實上,你想要的不是對紙張進行編碼,而只是實現紙張的程式碼。所以在你開始任何事情之前,你應該花幾天的時間試圖在網際網路上找到一個開源的實現。只要考慮一下:你寧願失去兩天的時間尋找程式碼,還是浪費兩個月來實現一個已經可用的演算法?

1.2 - 找到更簡單的方法來實現你的目標

問問自己想要做什麼,如果更簡單的解決方案可以滿足您的需要。您可以使用其他技術 -即使結果只有您想要的80% -不需要實現該文件,並且您可以在隨後的兩天內執行可用的開源庫?有關更多資訊,請參閱我的文章20/80生產力規則

1.3 - 小心軟體專利

如果您在美國,請注意軟體專利。有些論文獲得專利,您可能會遇到在商業應用中使用它們的麻煩。

1.4 - 瞭解有關該領域的更多資訊

如果您正在閱讀關於在計算神經科學的背景下使用支援向量機(SVM)的文章,那麼您應該閱讀機器學習的簡短介紹以及可以替代SVM的不同型別的分類器,您應該閱讀關於計算神經科學的一般文章,以瞭解現在研究中正在做什麼。

1.5 - 保持動力

如果你從未執行過紙張和/或如果您是新的紙張領域,那麼閱讀可能非常困難。無論發生什麼,不要讓數學方程的數量和複雜性阻止你。此外,速度不是一個問題:即使你覺得你理解紙張比你想要的慢,只要繼續工作,你會看到你會慢慢地,穩定地瞭解論文中提出的概念,並通過所有困難一個接一個。

2 - 三種論文

選擇隨機紙並立即開始執行是不錯的選擇。那裡有很多論文,這意味著有很多垃圾。所有出版物可以分為三類:

2.1 - 開創性的論文

一些真正有趣的,寫得好的和原創的研究。大多數這些論文都是從頂級大學出來的,或者是在處理這個問題大約六到十年的小型大學的研究團隊中出來的。後來很容易發現:他們在論文中引用了自己的出版物,表明他們已經在這個問題上一段時間了,而且他們的新工作基於出版物的成功記錄。此外,這些開創性的論文一般都是在現場最好的期刊上發表的。

2.2 - 模板紙

一些研究小組正在跟隨開創性團隊的工作,提出改進建議,併發布其改進成果。這些論文中有許多缺乏正確的統計分析,錯誤地得出結論,改進真的在打破原始演算法。大多數時候,他們真的沒有帶來任何東西,除了不必要的額外的複雜性。但並不是所有的模仿者都是壞的。有些是好的,但很少見。

2.3 - 垃圾紙

一些研究人員真的不知道他們在做什麼和/或是邪惡的。他們只是試圖在他們所教授的學術機構中保持自己的地位和特權。所以他們需要資金,因此他們需要釋出,什麼東西。誠實的人會在結論中告訴你,他們失敗了,結果只有N%的時間是準確的(N是壞的價值)。但有些邪惡的人會說謊,他們的研究取得了很大的成功。經過一段時間閱讀出版物後,很容易發現垃圾紙並將其弄髒。

3 - 如何閱讀科學論文

很多已經寫了這個話題,所以我不會寫太多。一個好的起點是:如何閱讀 SrinivasanKeshav 的論文。以下是我在閱讀科學出版物時發現有用的幾點。

3.1 - 找到正確的檔案

你想要實現的是一個原始的論文,一個開始整個領域的論文。有時候,如果你覺得它給一個好的但不成熟的開創性論文帶來了真正的改進和一致性,那麼你可以選擇一張模板紙。

所以讓我們說你有一張紙作為你的起點。您需要在其周圍進行一些研究。為此,該策略是尋找相關出版物,以及本文末尾“參考”部分列出的出版物。去Google學術搜尋,搜尋標題和作者。你發現的任何一篇論文比原來的論文做得更好嗎?如果是的話,那就先把你正在看的紙扔掉,然後保持你發現的新的紙張。GoogleScholar的另一個很酷的功能是您可以找到引用給定論文的論文。這真的很棒,因為你所要做的就是按照一篇論文到下一篇文章的引用鏈,你會發現最近的論文。從起點尋找好的紙張是所有關於尋找的論文被引用由當前的紙張,以及論文引用當前的紙張。及時移動,您應該找到高質量,符合您需求的紙張。

重要提示:在這個簡單的探索和推算階段,您不應該閱讀並充分了解論文。這個搜尋正確的文章應該只是通過抄寫檔案,並用你的本能來檢測垃圾(這帶有經驗)。

3.2 - 不要在螢幕上閱讀

在硬紙上列印出版物,並閱讀論文版本。另外,不要減小大小,以便在每個頁面上列印更多。是的,你會儲存三張紙,但你會失去時間,因為你會很快讀這些小字元。良好的閱讀字型大小在11到13分之間。

3.3 - 良好的時間和位置

半夜不要看紙,在大腦仍然新鮮的一天的時刻做。另外,找到一個安靜的區域,並使用良好的照明。當我閱讀時,我有一個檯燈直接指向檔案。

3.4 - 標記和筆記

用標記突出顯示重要資訊,並在閱讀時在頭部彈出的任何想法的邊緣記筆記。

3.5 - 瞭解所有條款的定義

當你習慣於閱讀大多數新聞文章和小說時,你的大腦訓練有素,通過使用上下文作為扣除裝置來填充你不知道的單詞的意思。閱讀科學出版物是一個不同的練習,最大的錯誤之一是假設一個詞的錯誤含義。例如在這句話中“這種分割方法的結果仍然遭受模糊人造物”的困擾。這兩個詞,“分割”和“文物”,具有英文的一般含義,但在計算機視覺領域也具有特定的意義。如果您不知道這些詞在本文中具有特定的意義,那麼在閱讀時不注意,您的大腦將填寫一般含義,您可能會丟失一些非常重要的資訊。因此,您必須(i)避免對字詞的假設,並且每當有疑問的時候,在出版物的領域的上下文中查詢這個詞,並且(ii)在一張紙上寫上一個關於您之前不瞭解的出版物特有的所有概念和詞彙的詞彙表。如果您首次遇到諸如“逼真點”和“分段仿射變換”等概念,那麼您應該查詢其精確的定義並將其寫入您的詞彙表。概念是支援語言的大腦快捷方式,可以讓您更快地瞭解作者的意圖。如果您首次遇到諸如“逼真點”和“分段仿射變換”等概念,那麼您應該查詢其精確的定義並將其寫入您的詞彙表。概念是支援語言的大腦快捷方式,可以讓您更快地瞭解作者的意圖。如果您首次遇到諸如“逼真點”和“分段仿射變換”等概念,那麼您應該查詢其精確的定義並將其寫入您的詞彙表。概念是支援語言的大腦快捷方式,可以讓您更快地瞭解作者的意圖。

3.6 - 在結論中尋找統計分析

如果作者僅從其演算法中提供一條曲線,並從另一種演算法中提取一條曲線,並說“看,它的準確度高20%”,那麼你知道你正在閱讀垃圾。您想要閱讀的內容是:“在N個例項的一組測試中,我們的演算法顯示了使用雙樣本t檢驗的p值為5%的顯著改進。”使用統計分析顯示,作者,並且是一個很好的證明,結果可以信任泛化(除非作者謊稱使他們的結果看起來更性感,這可以永遠發生)。

3.7 - 確保結論證明本文正在做您所需要的

假設你想要一個可以在圖片中找到任何臉的演算法。本文作者在結論中說,他們的模型是使用來自80個不同人(10x 80 = 800張圖片)的10個姿勢進行訓練的,訓練組的面部檢測準確率為98%,但只有70%與測試集(圖片在訓練期間未使用)。這是什麼意思?這意味著顯然,該演算法具有適當的概括性問題。在訓練集上使用(無用)時表現良好,在現實世界中使用時表現更差。在這一點你應該得出結論,也許這篇文章對你所需要的不夠好。

3.8 - 注意作者使用的輸入資料

如果要使用網路攝像頭執行人臉檢測,並且作者已經使用高清晰度攝像機拍攝的照片,那麼在您的情況下,演算法將不會像作者那樣執行。確保該演算法是在與您的資料相似的資料上進行測試的,否則您將最終獲得在實際設定中完全無法使用的良好實現。

3.9 - 作者是人類

作者是人類,因此他們犯錯誤。不要以為作者是絕對正確的,如果一個方程真的很難理解或跟隨,你應該問自己,作者是否在那裡犯了錯誤。這可能只是文中的打字錯誤,或數學錯誤。無論是哪種情況,最好的方法就是自己推出方程式,並嘗試驗證其結果。

3.10 - 瞭解變數和運算子

在出版物實施過程中的主要任務是將論文中的數學方程式轉化為程式碼和資料。這意味著在跳入程式碼之前,您必須瞭解這些方程的100%的方程和過程。例如,“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++實現之間的結果進行除錯。這將在下面的“除錯”部分進一步發展。

4.3 - 事先清除執行問題

您一定會在您的原型中造成軟體設計錯誤,這是一件好事,因為您將能夠確定過程或資料的難點。當您編寫C++版本時,您將瞭解如何更好地構建軟體,並且您將生成比沒有原型設計步驟更簡潔,更穩定的程式碼(這是Frederick提出的“扔掉系統”的想法布魯克斯在神話人月)。

4.4 - 驗證文中提出的結果

仔細閱讀本文的“實驗”部分,並嘗試通過使用與作者使用的測試資料儘可能相似的方式儘可能接近地重現實驗條件。這增加了您重寫作者獲得的結果的機會。不使用類似的條件可能會導致您執行的行為,您可能會將其視為錯誤,而您只是不提供正確的資料。一旦您可以根據類似的資料重現結果,那麼您可以開始在不同型別的資料上進行測試。

5 - 選擇正確的語言和圖書館

在這個階段,您必須清楚地瞭解出版物中提供的演算法和概念,您必須擁有一個執行的原型,以說服演算法實際上是處理您希望在生產中使用的輸入資料。現在是進入下一步的時候,其中包括使用您希望在生產中使用的語言和框架來實施出版物。

5.1 - 預先存在的系統

許多時候,生產語言和圖書館都是由現有的系統所決定的。例如,您有一套用於圖片中的照明歸一化的演算法,在Java編碼的庫中,並且您想從釋出中新增新的演算法。在這種情況下,顯然,您不會在C++中編寫這個新演算法,而是在Java中編寫。

5.2 - 預測未來使用的實施

在這種情況下,沒有預先存在的系統強加一種語言,那麼語言的選擇應該基於該演算法的預測使用。例如,如果您認為在四到六個月內,您的應用程式的可能埠將完成到iPhone,那麼您應該選擇C/ C ++ over Java,因為它將是將程式碼輕鬆整合到目標中的唯一方法-C應用程式,無需從頭開始。

5.3 - 完全或部分解決演算法的可用庫

不同語言的可用庫也可以定向生產語言的選擇。我們假設您希望實現的演算法使用眾所周知的代數技術,如主成分分析(PCA)和奇異值分解(SVD)。那麼你可以從頭開始編寫PCA和SVD,如果有一個錯誤可能會導致一個星期的除錯結束,或者你可以重新使用已經實現這些技術的庫,並使用慣例和Matrix編寫實現程式碼這個圖書館的類。理想情況下,您應該能夠將您的實現分解為子任務,並嘗試查詢儘可能實現儘可能多的這些子任務的庫。如果您發現僅適用於某種語言的完美圖書庫,那麼您應該選擇該語言。也,請注意,庫的選擇應該是重新使用現有程式碼和最小化依賴關係之間的權衡。是的,為實現所需的每個子任務編寫程式碼是很好的,但是如果需要建立20個不同的庫的依賴關係,那麼這可能不是很實用,甚至可能會危及實現的未來穩定性。

6 - 實施

以下是我在實施出版物方面的經驗的一些提示

6.1 - 選擇正確的精度

您應該仔細選擇您用於計算的型別。通常使用double而不是float更好。記憶體使用量可以更大,但是計算的精度會大大提高,一般值得。此外,您應該瞭解32位和64位系統之間的區別。無論何時,建立自己的型別來封裝底層型別(float或double,32位或64位),並在程式碼中使用此型別。這可以通過定義是C/ C ++或Java中的類來完成。

6.2 - 記錄所有內容

儘管過度文件可能會大大減緩專案的真實性,但在執行復雜技術檔案的情況下,您需要對所有內容進行評論。即使您是唯一從事專案工作的人員,您應該記錄檔案,課程和方法。選擇一個常規,如Doxygen或reStructuredText,並堅持下去。在開發過程中,會有一段時間你會忘記一些類的工作原理,或者你如何實現一些方法,你會感謝你自己編寫程式碼!

6.3 - 在程式碼中新增對本文的引用

對於您實施的論文中的每個方程,您需要新增一個引用文章(作者和年份)的評論以及段落號或方程數。這樣,當稍後重新讀取程式碼時,您將能夠將程式碼直接連線到紙張中的精確位置。這些評論應該是:

// See Cootes et al. 2001 Equation 2.3
// See Matthews and Baker
2004 Section 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 - 在第一次通過期間不要優化

留下所有優化以備以後。由於您絕對不能確定程式碼中哪一部分需要優化。每次看到可能的優化時,新增一個註釋並解釋如何實現優化,如:

// OPTIMIZE HERE:computing the matrix one column at a time
// and multiplying them directly could save memory

這樣一來,您可以在程式碼中找到可以進行優化的所有位置,並獲得有關如何進行優化的新提示。一旦您的實施完成,您將可以通過執行Profiler(如Valgrind)或您使用的程式語言中的任何可用性來找到要優化的位置。

6.6 - 規劃建立API?

如果您計劃使用當前程式碼作為隨時間增長的API的基礎,那麼您應該瞭解建立實際可用介面的技術。為此,我將推薦“編譯圖書館”技術,由JoshuaBloch在他的演講中總結,何設計一個好的API以及它為什麼重要

7 - 除錯

實現一個新的演算法就像烹飪你從未吃過的菜。即使它味道好,你永遠不會知道這是什麼味道。現在我們很幸運,因為與烹飪不同,軟體開發有一些有用的技巧來增加我們在實現中的信心。

7.1 - 將結果與其他實現進行比較

清除錯誤的一個好方法是將程式碼的結果與同一演算法的現有實現的結果進行比較。因為我假設你正確地完成了上面提到的“但是在跳躍之前”的所有任務,你沒有找到任何可用的演算法實現(否則你會使用它而不是實現這個論文!)。因此,您在此階段唯一的其他實現方式是您之前程式設計的原型。

因此,該思想是在演算法的每個步驟中比較原型和生產實現的結果。如果結果不同,那麼兩個實現之一就會出錯,你必須找到哪些和為什麼。精度可以改變(原型可以給你x= 1.8966和生產程式碼x= 1.8965),比較當然應該考慮到這一點。

7.2 - 與閱讀論文的人交談

一旦兩個實現(原型和生產)的所有步驟都得到完全相同的結果,您可以獲得一些信心,您的程式碼是無bug的。然而,您仍然有一個風險,您錯誤地理解了該論文。在這種情況下,這兩個實現將為每個步驟提供相同的結果,您將認為您的實現是好的,而這只是證明兩個實現同樣是錯誤的。不幸的是,我沒有辦法知道這種問題。您最好的選擇是找到已經閱讀論文的人,並詢問有關您不確定的演算法部分的問題。你甚至可以試圖詢問作者,但是你得到答案的機會很低。

7.3 - 視覺化您的變數

在開發過程中,始終要注意演算法使用的變數的內容。我不是在說只是列印矩陣和資料中的所有值,而是發現視覺化技巧適應於實現中的任何變數。例如,如果假設矩陣表示影象的漸變,則在編碼和除錯期間,您應該有一個視窗彈出並顯示漸變影象,而不僅僅是影象矩陣中的數字值。這樣,您將將實際的影象與您正在處理的資料相關聯,您將能夠檢測何時出現與其中一個變數有關的問題,這反過來又會指示可能的錯誤。發明的視覺化技巧包括影象,散點圖,圖形或任何不僅僅是愚蠢的列表1,

7.4 - 測試資料集

生成資料來實驗您的實現可能非常耗時。無論何時,都可以嘗試查詢資料庫(面部資料庫,文字提取資料庫等)或用於生成此類資料的工具。如果沒有,則不要手動生成1000個樣本。在20行中編寫一個快速資料生成器,並完成它。

結論

在本文中,我提出了實施科學出版物的良好做法。記住,這些只是基於我的個人經驗,而且不應該盲目追隨。閱讀和編寫時,請務必注意,並用您的判斷力確定上述指導方針適合您的專案。也許一些做法會傷害你的專案超過它會幫助它,這取決於你找出來。現在去實現一些很酷的演算法!

相關文章