2015-08-24 46 views
26

Jupyter(iPython)筆記本被當成是一種很好的工具,用於原型代碼和交互式地完成各種機器學習內容。但是,當我使用它,我難免會碰到以下:將jupyter筆記本轉換爲python腳本的最佳實踐

  • 筆記本很快變得過於複雜和混亂的維持和進一步提高筆記本,我必須做出Python腳本出來的;
  • 當談到產品代碼(例如需要每天重新運行的代碼)時,筆記本再次不是最好的格式。

假設我在jupyter中開發了一個完整的機器學習管道,其中包括從各種來源獲取原始數據,清理數據,特徵工程和訓練模型。現在,通過高效可讀的代碼來製作腳本的最佳邏輯是什麼?我用它對付幾個方面至今:

  1. 簡單地轉換.ipynb到的.py和,只有輕微的變化,硬編碼從筆記本的所有管道爲一個Python腳本。

    • '+':快速
    • ' - ':髒,不靈活,也不方便維護
  2. 做一個簡單的腳本有許多功能(大約爲每一個1個功能或兩個單元格),嘗試將管道的各個功能分爲幾個階段,並相應地命名它們。然後通過​​指定所有參數和全局常量。

    • '+':使用更靈活;更可讀的代碼(如果您正確地將流水線邏輯轉換爲函數)
    • ' - ':通常情況下,管道不能拆分成邏輯上完成的部分,可能會變成函數,而代碼中沒有任何怪癖。所有這些函數通常只需要在腳本中調用一次,而不是在循環,映射等內部多次調用。此外,每個函數通常會接收之前調用的所有函數的輸出,因此必須將許多參數傳遞給每個函數功能。
  3. 與點(2)相同的東西,但現在將所有函數包裝在類中。現在,所有全局常量以及每個方法的輸出都可以存儲爲類屬性。

    • 「+」:你不需要很多參數傳遞給每個方法 - 所有已存儲在屬性中前面的輸出
    • 「 - 」:一個任務的總體邏輯仍然沒有捕捉到 - 它是數據和機器學習的渠道,而不僅僅是階級。該類的唯一目標是創建,逐個調用所有方法,然後將其刪除。最重要的是,類實現起來相當長。
  4. 將筆記本轉換爲具有多個腳本的python模塊。我沒有嘗試過,但我懷疑這是處理這個問題的最長的方法。

我想,這個整體設置在數據科學家中是非常普遍的,但是令人驚訝的是我找不到任何有用的建議。

請朋友們分享您的想法和經驗。你有沒有遇到過這個問題?你是如何處理它的?

+0

你只是想從Notebook文件中提取python代碼嗎? – kxxoling

+0

我做了幾次 - 這只是我在第(1)點提到的方式。它有一些缺點。首先,非靈活的硬代碼。其次,通常你不想逐個執行筆記本中的每個單元格(可能會有很多純粹的可視化或僅僅是過多的單元格),所以在導出後仍然需要清理代碼 - 相同的工作量,就好像您只是手動複製粘貼的單元格一樣。 – kurtosis

+0

您可以編寫腳本來提取所有Python代碼,而不是手動提取。這是你想要的嗎? – kxxoling

回答

8

我們有類似的問題。但是,我們正在使用幾個筆記本電腦進行原型設計,畢竟這應該成爲幾個Python腳本。

我們的方法是,我們放下代碼,在這些筆記本上重複接縫。我們把它放到python模塊中,它由每個筆記本導入並用於生產。我們連續不斷地改進這個模塊,並添加我們在原型設計過程中發現的測試。

筆記本然後變得相當像配置腳本(我們只是簡單地複製到最終生成的python文件中)以及幾個原型驗證和驗證,我們在生產中不需要這些驗證和驗證。

最重要的是,我們不怕重構的:)

+0

這聽起來很不錯:它似乎不可能直接在筆記本中進行重構 - 無論是ipython/jupyter,zeppelin,spark-notebook等。這是一個表演者。因此,將筆記本描述爲「配置」是我認爲它是演示工具的另一個方面 - 但不是開發工具。 – javadba

3

生命的救星:爲你寫你的筆記本,逐步重構你的代碼的功能,編寫一些最小assert測試和文檔字符串。

之後,從筆記本到腳本的重構是很自然的。不僅如此,即使您沒有計劃將其變成其他任何東西,它在編寫長筆記本時也會讓您的生活更輕鬆。

與「最小」測試和文檔字符串單元格內容的基本例如:

def zip_count(f): 
    """Given zip filename, returns number of files inside. 

    str -> int""" 
    from contextlib import closing 
    with closing(zipfile.ZipFile(f)) as archive: 
     num_files = len(archive.infolist()) 
    return num_files 

zip_filename = 'data/myfile.zip' 

# Make sure `myfile` always has three files 
assert zip_count(zip_filename) == 3 
# And total zip size is under 2 MB 
assert os.path.getsize(zip_filename)/1024**2 < 2 

print(zip_count(zip_filename)) 

一旦你出口它袒露.py文件,你的代碼可能不會被尚未組織成類。但值得努力的是,將筆記本重新構建到具有一組文檔功能的地步,每個筆記都有一組簡單的assert語句,可以很容易地將其移動到tests.py中,以便使用pytest,unittest或您有什麼測試。如果有意義的話,那麼將這些函數綁定到你的類的方法是非常簡單的。

如果一切順利,你需要做的就是編寫你的if __name__ == '__main__':及其「鉤子」:如果你正在編寫腳本被終端調用,你會想要handle command-line arguments,如果你是寫一個模塊,你會想到its API with the __init__.py file

這一切都取決於預期的用例是什麼,當然:將筆記本轉換爲小腳本與將其轉換爲全面的模塊或包裝。

下面是筆記本到腳本的工作流程一些想法:

  1. 導出Jupyter筆記本到Python文件(的.py)通過GUI。
  2. 刪除不執行實際工作的「助手」行:print陳述,繪圖等
  3. 如果需要,將您的邏輯捆綁到類中。唯一需要的額外重構工作應該是編寫您的類文檔字符串和屬性。
  4. if __name__ == '__main__'寫下你的腳本的入口。
  5. 分隔你的每個函數/方法的assert聲明,並充實tests.py中最小的測試套件。