圖1 Jupter專案整體架構
[https://docs.jupyter.org/en/latest/projects/architecture/content-architecture.html]
Jupyter Notebook是一套基於web的互動式開發環境。使用者可以線上開發和分享包含程式碼和輸出的互動式檔案,支援實時程式碼,數學方程,視覺化和 markdown等。用途包括:資料清理和轉換,數值模擬,統計建模,機器學習等等。
Jupyter Notebook內部通過核心維護狀態並執行程式碼片段,瀏覽器顯示程式碼片段和其執行的結果。Jupyter Notebook提供了一個使用者互動式的開發環境,使用者可以通過執行一部分程式碼片段,並觀察執行結果。這種互動式設計,使得Jupyter Notebook非常適合資料科學和機器學習的開發工作。
注意本文的程式碼和指令碼,均基於Jupyter Notebook v6.5.2穩定版本。
圖2 Jupter Notebook工作方式
[https://docs.jupyter.org/en/latest/projects/architecture/content-architecture.html]
Jupyter的主要工作單元是Jupyter Server和Kernel。其中Jupyter Server用來提供基於Web的介面和API服務,Kernel用來執行程式碼片段。瀏覽器通過Http和Websockets的方式和Jupyter Server進行互動,Jupyter Server和kernel之間,通過ZeroMQ進行資料通訊。
Jupyter Server採用經典的MVC模式,使用了tornado作為web伺服器,用來提供地址對映和控制器邏輯,使用jinja2來提供模板檢視功能。
Jupyter Notebook(v6.5.2)專案的主要模組結構如下:
模組 | 說明 |
---|---|
notebook | Notebook功能模組。 |
terminal | 終端模組。為Jupyter提供控制檯互動能力。 |
view | 檔案視覺化模組。比如pdf檔案的顯示。 |
tree | 工作區目錄樹 |
nbconvert | 格式轉換模組,可以把Jupyter Notebook轉換成html,pdf等格式 |
kernel | Jupyter Notebook 核心 |
services | Jupyter Notebook REST API模組 |
i18n | Jupyter Notebook多語言資源 |
前置條件
Python和pip
不同的Jupyter Notebook對Python有不同的版本要求。我們安裝的最新的穩定版本v6.5.2的Jupyter Notebook,要求Python的最低版本為3.6。注意這個Python的版本,不同於核心的Python版本。對於Jupyter核心來說,支援的Python版本和Jupyter Notebook依賴的Python版本沒有關係。
在Linux系統下安裝Jupyter Notebook
使用pip安裝Jupyter notebook非常簡單。如果伺服器同時擁有Python2和Python3的pip,注意需要使用pip3來替換命令中的pip。
# 更新pip
pip install --upgrade pip
# 安裝jupyter
pip install jupyter
# 檢查安裝的jupyter
jupyter --version
//輸出 notebook : 6.5.2
Jupyter提供了大量的啟動引數,用來設定Jupyter Server。我們可以在啟動Jupyter服務時,通過命令列引數的方式設定當前啟動的服務,但更普遍的方式是使用Jupyter的組態檔。
# 生成組態檔
jupyter notebook --generate-config
// 預設生成的組態檔位置:
/root/.jupyter/jupyter_notebook_config.py
# 修改Jupyter組態檔...
# 啟動jupyter
jupyter notebook
Jupyter直接使用一個Python檔案來設定Jupyter服務,所有的設定項均通過Python程式碼來完成。常用的設定項及其說明如下:
名稱 | 預設值 | 說明 |
---|---|---|
c.NotebookApp.allow_root | False | 為了安全,Jupyter預設不允許使用root使用者啟動。如果需要以root使用者的身份啟動Jupyter,需要開啟此設定 |
c.NotebookApp.allow_origin | '' | 當需要Jupyter內嵌到iframe時,可以設定為「*「來避免跨origin的限制 |
c.NotebookApp.ip | localhost | 當需要通過外網地址來存取Jupyter服務時,需要設定一個有效的伺服器IP地址。 |
c.NotebookApp.port | 8888 | Jupyter server對外伺服器埠 |
c.NotebookApp.notebook_dir | / | Jupyter的工作空間,預設可以存取伺服器上當前使用者的所有檔案系統 |
c.NotebookApp.open_browser | True | 啟動服務後是否立即通過瀏覽器開啟服務地址 |
c.NotebookApp.default_url | /tree | Jupyter服務的預設地址 |
c.NotebookApp.extra_static_paths | [] | 擴充套件靜態檔案目錄 |
c.NotebookApp.extra_template_paths | [] | 擴充套件模板檔案目錄 |
c.KernelSpecManager.allowed_kernelspecs | set() | 預設允許使用所有的kernel |
c.NotebookApp.nbserver_extensions | {} | 允許載入的Jupyter Server擴充套件 |
啟動Jupyter 後,在瀏覽器內輸入 http://伺服器地址:埠/,Jupyter會預設重定向到.default_url指定的工作區目錄樹地址,預設是工作區目錄樹的介面。
如果在存取的過程中,使用了預設的token作為其認證方式,那麼在首次開啟時,需要輸入Jupyter Notebook的token值,這個值可以在啟動Jupyter時的控制檯輸出中找到,或者使用Jupyter命令來查詢
# 查詢執行的jupyter notebook
jupyter notebook list
//返回結果中包含了http://x.x.x.x:8899?token=ABC 的資訊,其中的ABC就是我們需要的token
圖3 Jupter Notebook的預設工作區目錄樹頁面
Jupyter Notebook通過Jupyter Server提供基於Web的平臺無關的工作方式,這使得跨平臺開發和共同作業,程式碼分享等能力變得比傳統IDE更加容易。
在Jupyter 工作區管理介面,使用者可以靈活地以類似檔案系統的方式管理工作區的資料。可以建立檔案和資料夾,編輯檔案和資料夾,可以上傳和下載檔案。通過選擇一個Jupyter核心,可以建立一個Notebook檔案。
圖4 通過Jupyter核心建立一個Notebook
使用Python3核心建立一個Notebook後,我們得到一個xxx.ipynb(IPython Notebook)檔案。這個檔案是一個json格式的文字檔案,其中包含了我們在Notebook中編寫的程式碼和文字內容,也包含了介面上沒有顯示的後設資料資訊。通過在工作區目錄介面選擇一個notebook檔案,點選編輯,我們可以檢視到ipynb檔案的原始內容。
圖5 ipynb檔案的原始內容
我們可以像使用其它IDE類似的方式來使用Notebook,在使用Notebook上,我們主要關注下Jupyter核心和單元格。
核心是執行單元格程式碼的核心程序,不同的核心,決定了我們在單元格中能夠編寫哪些語言的程式碼,以及對應了指定的程式語言的哪個版本等資訊。
單元格是整個Notebook的核心組成部分,我們編寫的程式碼和文字,通過一些列Notebook單元格來組成。Notebook提供了Code,Markdown, Raw NBConvert, Heading四種型別的單元格。
•Code單元格。用來編寫核心指定語言的程式程式碼
•Markdown單元格。使用Markdown編輯器的語法來編輯富文字
•Raw NBConvert單元格。原始的文字,不會被當作程式碼或markdown被解釋執行
•Heading單元格。Heading是Mardown的一個子集,對應了Markdown中的標題編寫語法
Jupyter Notebook使用了機器學習中檢查點的概念,在我們修改Notebook的過程中,Jupyter會自動儲存我們的修改,我們也可以通過【檔案】->【儲存】來手動儲存檢查點。檢查點檔案包含了我們編寫的Notebook內容,以及執行程式碼單元格之後的輸出。我們可以在工作空間的「.ipynb_checkpoints」資料夾下,找到這些檢查點檔案。
圖6 使用Jupyter單元格來編寫互動式程式碼
相較於使用傳統的IDE編寫的程式碼,基於Web服務的Jupyter Notebook在程式碼分享上擁有著天然的優勢。
在Jupyter Notebook中,我們可以通過兩種不同的方式分享我們創作的nootbook。
傳統的技術檔案或者說明書,通過靜態的文字,配合圖片和視訊,來描述和講解特定的技術或功能。有了Jupyter Notebook後,我們仍然可以使用Notebook來編寫類似傳統的技術檔案。在此基礎上,我們可以加入更生動的程式碼互動單元格,使用者通過檢視檔案說明,並與檔案中提供的程式碼進行互動,可以更生動地介紹產品中的功能和技術。每個Jupyter Notebook的ipynb檔案,都對應了一個獨立的存取地址: http://x.x.x.x:8899/notebooks/my_notebook.ipynb ,通過分享此檔案的地址,其他使用者可以方便地使用包含了富文字和可執行的程式碼的互動式Notebook檔案。
我們通過逐步執行檔案中的所有單元格,得到一個包含了我們編寫的說明和程式碼,以及程式碼執行的輸出結果的完整檔案。之後點選【檔案】-> 【另存為】,選擇一種合適的檔案格式。我們可以把檔案匯出為一份靜態檔案,通過共用此靜態檔案,我們實現了Notebook檔案的離線分享。
Jupyter Notebook提供了一些列魔法函數來增強Jupyter Code單元格的功能,通過魔法函數,我們能夠執行javascript指令碼,html程式碼,執行另一個可執行程式等許多額外的功能。
我們可以在Jupyter程式碼單元格中使用 %lsmagic命令來檢視所有的魔法函數,如果要閱讀詳細的魔法函數的使用說明,可以參考: https://ipython.readthedocs.io/en/stable/interactive/magics.html
魔法函數分為行魔法函數,單元格魔法函數和對談魔法函數。顧名思義,行魔法函數只對當前行起作用,而單元格魔法函數則作用於整個單元格,對談魔法函數則作用於整個對談期間。
一些常用的魔法函數:
指令 | 說明 |
---|---|
%matplotlib | 設定matplot繪圖的顯示模式 |
%%javascript | 單元格內的程式碼被識別為javascript程式碼 |
%%html | 單元格內的程式碼被識別為html程式碼 |
%run | 執行外部指令碼檔案 |
%pwd | 獲取當前工作的目錄位置(非工作空間目錄位置) |
%writefile | 以檔案形式儲存當前單元格程式碼 |
%timeit | 獲取本行程式碼的執行時間 |
%debug | 啟用偵錯模式 |
Jupyter Notebook使用i18n目錄下的資源來進行多語言翻譯。在Jupyter Notebook啟動時,會載入i18n目錄下的多語言資源。之後根據http請求指定的語言,為響應資料提供對應的多語言翻譯。如果沒有對應的翻譯,則保留原始的多語言標籤值(英文)。如果調整了多語言翻譯,需要重新啟動Jupyter Notebook才能使用新的語言套件。
Jupyter Notebook的翻譯資源主要分佈在三個po檔案中:
•nbjs.po - js檔案中的多語言資料
•nbui.po - UI介面中的多語言資料
•notebook.po - notebook中的多語言資料
原始的po檔案,需要通過pybabel工具,把po檔案編譯成mo檔案,之後部署在$notebook/i18n/${LANG}/LC_MESSAGES/目錄下($notebook是notebook的安裝目錄),才能在Jupyter Notebook中作為多語言的資源包來使用。
# 使用pybabel編譯多語言po檔案
pybabel compile -D notebook -f -l ${LANG} -i ${LANG}/LC_MESSAGES/notebook.po -o ${LANG}/LC_MESSAGES/notebook.mo
pybabel compile -D nbui -f -l ${LANG} -i ${LANG}/LC_MESSAGES/nbui.po -o ${LANG}/LC_MESSAGES/nbui.mo
pybabel compile -D nbjs -f -l ${LANG} -i ${LANG}/LC_MESSAGES/nbjs.po -o ${LANG}/LC_MESSAGES/nbjs.mo
圖7 使用了中文語言套件後的中文Notebook介面
核心(kernel)是獨立於jupyter服務和介面之外的用來執行Jupyter程式碼單元格的程序,Jupyter預設提供了ipykernel核心來支援Python開發語言。Jupyter社群提供了jupyterC, IJava,xeus-cling, xeus-sql等眾多其它程式語言的核心,用來支援C/C++, Java, SQL等程式語言。
ipykernel預設使用系統環境下的Python來提供服務。我們可以使用ipykernel安裝多個Python kernel來提供Python2.x, Python3.x等多個Python核心環境。
安裝kernel後,kernel的資訊被儲存在kernel.json檔案中,我們可以在 /usr/local/share/jupyter/kernels 目錄,找到Jupyter安裝的所有kernel以及對應的kernel.json檔案。
kernel可以直接繼承自安裝kernel的Python指令,也可以使用Python虛擬環境。
# 1.直接繼承自Python指令的kernel安裝
# 安裝ipykernel
pip install ipykernel
# 安裝kernel
python -m ipykernel install --name tensorflow2 --display-name "tensorflow2"
# 2. 在Python虛擬環境下的kernel安裝
# 啟用虛擬環境
source activate myenv
# 安裝ipykernel
pip install ipykernel
# 安裝kernel
python -m ipykernel install --name myenv --display-name "Python3 (myenv)"
如果需要檢視當前的kernel列表,以及刪除已經安裝的kernel,可以使用如下的Jupyter命令:
# 檢視已經安裝的kernel列表
jupyter kernelspec list
# 刪除列表中指定的kernel
jupyter kernelspec remove kernelname
Jupyter提供了REST API介面來和Jupyter server進行互動。藉助REST API的能力,我們可以以程式設計的方式和Jupyter Server進行互動,靈活地管理Jupyter Server。另外REST API為現代化的軟體開發提供了一個優秀的能力:自動化。
藉助Jupyter Notebook REST API,可以實現檔案的上傳和下載,檢查點管理,對談管理,核心管理,終端管理等一些列管理能力。完整的Jupyter REST API介面列表可以參考: https://jupyter-server.readthedocs.io/en/latest/developers/rest-api.html
要使用REST API,需要在請求中攜帶認證資訊。Jupyter支援直接把token作為query string的方式來認證,也可以使用標準的Http Authorization頭資訊來完成認證。使用Authorization頭來認證的格式如下:
Authrozation: token 527a9f1430ccfed995ebcf15517583a2547c2469bc3c47a6
圖8 使用Postman來呼叫Jupyter REST API介面
Jupyter提供了靈活強大的能力,用以支援線上的互動式檔案和程式碼的編寫。但Jupyter專案自身沒有提供精細化的安全管理體系,用以支援多使用者下靈活地使用Jupyter Notebook的功能。對於檔案安全,Jupyter依賴於啟動服務的linux使用者,合理地設定啟動Jupyter的使用者的許可權,才能保證使用Jupyter的使用者,不會對系統或專案造成破壞。Jupyter工作空間的設定,僅起到了方便Jupyter使用者管理必要檔案的易用性,不能阻擋使用者存取和管理工作空間外的檔案系統。另外,配合使用Python虛擬環境,可以防止Jupyter Notebook提供的 pip install ,pip uninstall功能,對現有專案環境造成破壞。
在多人共同作業方面,JupyterHub專案提供了多人共同作業Jupyter Notebook和Jupyter lab開發的能力。使用JupyterHub,不同職能的使用者可以在自己獨立的空間內進行Notebook的編寫工作,不同使用者間也可以方便地分享各自的Notebook。
Jupyter Notebook前端擴充套件(front end extension)是使用Javascript語言編寫的非同步模組,可以用來繪製Jupyter介面的儀表盤,Notebook,工具列等,。定義一個前端擴充套件必須要實現一個load_ipython_extension方法,當前端控制元件被載入時,Jupyter client會呼叫load_ipython_extension方法。
Jupyter Notebook前端擴充套件能力目前還不是一個穩定的版本,不保證程式碼能夠向後相容。Jupyter的JS API目前也沒有官方的檔案,需要通過原始碼或者實際載入的JS來檢視Jupyter前端指令碼的成員和方法。
我們實現一個簡單的前端擴充套件指令碼,在jupyter前端的工具條中,新增一個自定義工具,當點選自定義工具時,彈出提示資訊。
define([
'base/js/namespace'
], function(
Jupyter
) {
function load_ipython_extension() {
var handler = function () {
alert('歡迎使用前端擴充套件!');
};
var action = {
icon: 'fa-comment-o',
help : '前端擴充套件',
help_index : 'zz',
handler : handler
};
var prefix = 'my_extension';
var action_name = 'show-alert';
var full_action_name = Jupyter.actions.register(action, action_name, prefix); // returns 'my_extension:show-alert'
Jupyter.toolbar.add_buttons_group([full_action_name]);
}
return {
load_ipython_extension: load_ipython_extension
};
});
完前端擴充套件程式碼後,我們把指令碼儲存到main.js檔案,放置在/opt/my_extension目錄下。接下來我們使用jupyter nbextension工具來安裝和啟用前端擴充套件
# 安裝前端擴充套件
jupyter nbextension install /opt/my_extension
# 啟用前端擴充套件
jupyter nbextension enable my_extension/main
# 禁用前端擴充套件
jupyter nbextension disable my_extension/main
# 檢視前端擴充套件列表
jupyter nbextension list
# 解除安裝前端擴充套件
jupyter nbextension uninstall my_extension
圖9 在Notebook工具條中加入的前端擴充套件
Jupyter伺服器端擴充套件(server extension)是使用Python語言編寫的模組,可以用來處理傳送到Jupyter Server的Http請求。使用Jupyter伺服器端擴充套件,可以更改現有Jupyter請求的資料和行為,也可以為jupyter Server定義新的服務處理程式。
定義一個伺服器端擴充套件模組要實現一個load_jupyter_server_extension方法,其中包含一個型別為notebook.notebookapp.NotebookApp的引數serverapp,serverapp的詳細屬性和方法可以通過Jupyter Notebook原始碼中的notebookapp.py檔案來檢視。當伺服器端擴充套件被載入時,Jupyter Server會呼叫load_jupyter_server_extension方法。在load_jupyter_server_extension方法中,我們可以通過呼叫serverapp的web_app屬性的add_handlers方法來註冊處理程式,用來處理特定的伺服器端請求。處理程式類需要繼承自Jupyter的IPythonHandler類。在處理程式的方法中,可以使用Jupyter提供的@web.authenticated裝飾器來為方法增加身份認證保護。
通過伺服器端擴充套件,還可以與前端擴充套件聯動,實現一個功能豐富的Jupyter Notebook前端控制元件。
# 定義一個處理程式
from tornado import (
gen, web,
)
from notebook.base.handlers import IPythonHandler
class HelloWorldHandler(IPythonHandler):
@web.authenticated
@gen.coroutine
def get(self):
self.finish(f'Hello, world!')
# 實現load_jupyter_server_extension方法並註冊處理程式
def load_jupyter_server_extension(serverapp):
handlers = [
('/myextension/hello', HelloWorldHandler)
]
serverapp.web_app.add_handlers('.*$', handlers)
完成伺服器端擴充套件程式碼後,我們把程式碼儲存為__init__.py檔案,要在Jupyter Notebook中使用處理程式,我們還需要進行伺服器端擴充套件的安裝和啟用。不同於前端擴充套件,伺服器端擴充套件不能直接使用指令來安裝,需要我們手動編寫安裝程式。此外,Jupyter提供了自動啟用伺服器端擴充套件和前端擴充套件的方法,需要我們在指令碼的根目錄提供啟用擴充套件的組態檔。
jupyter-config/
├── jupyter_notebook_config.d/
│ └── my_server_extension.json
└── nbconfig/
└── notebook.d/
└── my_front_extension.json
setup.py
加入了自動啟用擴充套件的設定,我們的伺服器端擴充套件目錄結構如下:
hello-extension/
├── __init__.py
jupyter-config/
├── jupyter_notebook_config.d/
└── hello_extension.json
hello_extension.json檔案的內容為:
{
"ServerApp": {
"jpserver_extensions": {
"hello_extension": true
}
}
}
接下來我們通過安裝程式,安裝伺服器端擴充套件的資訊儲存在/root/.jupyter/jupyter_notebook_config.json檔案中。在安裝完成後,我們可以通過jupyter serverextesion工具來股那裡伺服器端擴充套件
# 啟用伺服器端擴充套件
jupyter serverextension enable hello_extension
# 禁用伺服器端擴充套件
jupyter serverextension disable hello_extension
# 伺服器端擴充套件直接解除安裝的方法,需要我們通過pip uninstall 解除安裝安裝程式,
# 再通過手工修改/root/.jupyter/jupyter_notebook_config.json檔案刪除擴充套件資訊來完成解除安裝
圖10 在瀏覽器中測試安裝的伺服器端擴充套件程式
Jupyter沒有提供標準的介面客製化的能力,但我們可以手工調整jupyter生成的模板檢視檔案和樣式檔案,達到整條調整jupyter notebook的介面的能力。
Jupyter Notebook模板檔案的位置為:$notebook/templates,樣式和指令碼客製化推薦的方案是使用/.jupyter/custom/custom.css和/.jupyter/custom/custom.js檔案。我們可以直接在此基礎上對檔案進行修改,還可以通過extra_template_paths和extra_static_paths來引入其它位置的模板和其它靜態檔案。
圖11 通過直接調整模板檔案加入的介面客製化按鈕
小部件(Widgets)是Jupyter互動式視覺化資料呈現部件。Jupyter Widgets同時包含了存取後端資料和前端呈現的能力,可以用於在Jupyter Notebook上生動地展示伺服器端的資料和資料變化。
在v6.5.2穩定版本上,我們目前只能使用系統提供的小部件,還不能開發自定義小部件。在Jupyter notebook7.x版本中,開始提供了小部件的自定義開發能力。
# 確保安裝了ipywidgets和traitlets
pip install --upgrade traitlets
pip install --upgrade ipywidgets
# 安裝和啟用小部件
jupyter nbextension install --py widgetsnbextension
jupyter nbextension enable --py widgetsnbextension
在安裝和啟用了小部件後,我們可以在notebook中直接使用系統提供的小部件。
圖12 在Notebook中使用小部件
完整的小部件列表和使用方式可以參考: https://ipywidgets.readthedocs.io/en/7.x/examples/Widget List.html
Jupyter Notebook以其豐富的功能,簡單易用,強大的互動能力和擴充套件能力,成為資料科學和機器學習開發中的神器。目前,Jupyter Notebook支援超過40種程式語言,被應用於Google Colab, Kubeflow, 華為雲,kaggle等多個知名專案中,大量機器學習和資料科學的論文中使用到了Jupyter。Jupyter在資料視覺化,提升工作效率,改善使用者體驗和豐富檔案功能方面顯現了巨大的威力。除此之外,Jupyter還提供的靈活強大的擴充套件能力,更是為Jupyter的深層次使用提供了更廣闊的想象空間。如果你還沒有開始接觸Jupyter,那麼就從現在開始吧。