SKIP 4 — 過渡到 scikit-image 2.0#

作者:

Juan Nunez-Iglesias <juan.nunez-iglesias@monash.edu>

作者:

Lars Grüter

狀態:

草稿

類型:

標準追蹤

建立時間:

2022-04-08

已解決:

<null>

決議:

<null>

生效版本:

摘要#

scikit-image 正準備發布 1.0 版本。這被視為清理 API 的機會,包括不相容的回溯變更。其中一些變更涉及在不變更函式簽名的情況下變更傳回值,這通常只能透過新增一個無用的關鍵字引數(例如 new_return_style=True)來完成,其預設值會在多個版本中變更。結果仍然是回溯不相容的變更,但經過較長的時間段才完成。

儘管 scikit-image 處於 beta 版和 0.x 系列版本中,但它的使用範圍非常廣泛,任何回溯不相容的變更都可能造成破壞。鑑於 SKIP-3 被拒絕,本文提出了一種建立新 API 的替代途徑。新途徑包括以下步驟

  • 任何預定在 v0.20 和 v0.21 中進行的擱置棄用都會完成(v0.19 中棄用訊息建議的新 API 會成為唯一的 API)。

  • 這將作為 1.0 版本發布。

  • 此時,main 分支會將套件和匯入名稱變更為 skimage2,並且 API 可以自由發展。

以下將說明 API 變更的進一步動機,並且在很大程度上與 SKIP-3 中重複。

動機和範圍#

scikit-image 在過去 12 年多的時間裡有機地成長,其功能是由來自不同背景的廣大貢獻者社群新增的。這導致 API 的各個部分不一致:例如,skimage.transform.warp 反轉了座標順序,因此 (45, 32) 的轉換實際上會將 NumPy 陣列中的值沿著第 0 軸移動 32,沿著第 1 軸移動 45,但僅在 2D 中。在 3D 中,(45, 32, 77) 的轉換會將每個軸中的值移動相應位置的數字。

此外,隨著我們的使用者群的成長,某些早期 API 選擇變得比有幫助更令人困惑。例如,scikit-image 會自動將影像轉換為各種資料類型,並在此過程中重新縮放它們。範圍 [0, 255] 中的 uint8 影像會自動轉換為 [0, 1] 中的 float64 影像。這最初可能看起來合理,但為了保持一致性,[0, 65535] 中的 uint16 影像會重新縮放為 [0, 1] 浮點數,而 [0, 4095] 中具有 12 位元範圍的 uint16 影像(在顯微鏡檢視中很常見)會重新縮放為 [0, 0.0625]。這些無聲的轉換已導致許多使用者的困惑。

變更此慣例將需要向幾乎所有 scikit-image 函式新增一個 preserve_range= 關鍵字引數,其預設值會在 4 個版本中從 False 變更為 True。最終,無論我們使棄用曲線多麼溫和,此變更都會回溯不相容。

其他主要函式,例如 skimage.measure.regionprops,可以使用 API 調整,例如傳回將標籤對應到屬性的字典,而不是列表。

鑑於累積了太多潛在的 API 變更,這些變更已變得太繁重且雜亂,無法使用標準棄用週期來修復,主要是因為它們涉及變更相同輸入的函式輸出,因此在過渡到 2.0 版本時進行所有這些變更是有意義的。

雖然語意版本控制 [6] 從技術上允許主要版本升級進行 API 變更,但我們必須承認 (1) 大量專案依賴 scikit-image,因此會受到回溯不相容變更的影響,並且 (2) 在科學 Python 社群中,對相依性設定上限版本界限還不是常見的作法,因此不太可能有人在其相依性列表中使用 scikit-image<1.*scikit-image<2.*。這表示發布具有重大 API 變更的 scikit-image 2.0 版本將會破壞大量使用者。此外,如此廣泛的變更將使大量的 StackOverflow 和其他使用者指南失效。最後,發布具有大量變更的新版本會阻止使用者逐步移轉到新的 API:必須整體移轉舊的程式碼庫,因為無法同時依賴這兩個版本的 API。這將對許多使用者構成巨大的進入障礙。

鑑於以上所述,此 SKIP 建議我們發布一個新的套件,我們可以在其中應用我們從十多年的開發中所學到的一切,而不會破壞我們現有的使用者群。

詳細描述#

本文的目的並非列出 skimage2 的所有建議 API 變更,其中許多變更尚未決定。事實上,如果接受此 SKIP,2.0 過渡的範圍和雄心可能會擴大。此 SKIP 反而提出了一種管理過渡的機制,而不會破壞使用者的程式碼。追蹤建議變更的 meta-issue 可以在 GitHub 上找到,scikit-image/scikit-image#5439 [7]。下面簡要包含一些範例以供說明

  • 當必須強制轉換為浮點數時,停止重新縮放輸入陣列。

  • 停止在不同的環境中交換座標軸順序,例如繪圖或扭曲。

  • 允許自動傳回非 NumPy 類型,只要它們可以使用 numpy.asarray 強制轉換為 NumPy。

  • 協調不同函式中的類似參數以具有相同的名稱;例如,我們目前在不同的函式中有 random_seedrandom_stateseedsample_seed,都是指相同的內容。

  • measure.regionprops 變更為傳回字典而不是列表。

  • 將具有相同目的的函式(例如 watershedslicfelzenschwalb)合併到一個通用命名空間中。這會讓新使用者更容易找出他們應該針對特定任務嘗試哪些函式。這也會有助於社群圍繞通用 API 成長,而現在 scikit-image API 對於每個函式來說基本上是唯一的。

為了以最少的用戶干擾進行此轉換,此 SKIP 建議發布一個新的函式庫 skimage2,它將取代現有的函式庫,但僅在使用者明確選擇加入的情況下。此外,透過發布新的函式庫,使用者可以同時依賴 scikit-image (1.0) 和 skimage2,讓使用者可以逐步移轉其程式碼。

實作#

提案的詳細資訊如下

  • scikit-image 0.19 之後將是 scikit-image 1.0。每個棄用訊息都將從 1.0 中移除,並且 API 將被視為 scikit-image 1.0 API。

  • 1.0 之後,main 分支將變更為 (a) 將匯入名稱變更為 skimage2,(b) 將套件名稱變更為 skimage2,以及 (c) 將版本號碼變更為 2.0-dev。

  • PyPI 上不會有版本為 2.0 的「scikit-image」套件。使用 pip install scikit-image 的使用者將始終取得套件的 1.x 版本。若要安裝 scikit-image 2.0,使用者需要 pip install skimage2conda install skimage2 或類似的指令。

  • 在就新的 API 達成共識後,將會發布 skimage2。

  • 在 skimage2 發布之後,會發布 scikit-image 1.1。此版本與 1.0 相同(包括錯誤修復),但會建議使用者 (a) 升級到 skimage2,或 (b) 將套件固定到 scikit-image<1.1 以避免警告。

  • scikit-image 1.0.x 和 1.1.x 版本將在一段未指定的時間內,根據錯誤的嚴重性和所涉及的工作量,接收重要的錯誤修復。

向下相容性#

此提案在函式庫中的許多地方破壞了向下相容性。然而,它是在一個新的命名空間中進行的,因此此提案不會引起使用者對向下相容性的擔憂。也就是說,作者將嘗試將不相容的變更限制在那些可能大幅改善整體使用者體驗的變更。預期將 skimage 程式碼移植到 skimage2 將是一個簡單的過程,我們將在 skimage2 發佈時發布一份使用者指南,說明如何進行轉換。使用者將透過 scikit-image 1.1 中的警告等方式收到關於這些資源的通知。

替代方案#

在同一個套件中使用語義版本控制發布新的 API#

這是 SKIP-3,在與社群討論後被否決。

在多個版本中持續棄用#

這種轉換可以隨著多個版本逐步進行。例如,對於自動轉換和重新縮放浮點數輸入的函式,我們可以新增一個 preserve_range 關鍵字參數,該參數最初預設為 False,但 False 的預設值將被棄用,使用者會收到警告,要求切換到 True。切換後,我們可以(選擇性地)棄用該參數,在經過另外兩個版本發布後,達到相同的結果:scikit-image 不再自動重新縮放數據,API 中不再有不必要的關鍵字參數。

當然,這種操作必須在所有上述建議的變更中同時進行。

最終,核心團隊認為,這種方法對 scikit-image 開發人員和下游函式庫的開發人員產生更多的工作,但好處令人懷疑:最終,較新版本的 scikit-image 仍然會與舊版本不相容,儘管時間尺度較長。

包含兩個版本的單一套件#

由於匯入名稱正在變更,因此可以建立一個單一套件,其中包含 skimageskimage2 命名空間,至少在一段時間內一起發布。這個選項很有吸引力,但這意味著需要長期維護 1.0 命名空間,我們可能沒有足夠的維護時間,或者 1.0 命名空間的棄用週期會很長,這最終會導致許多不滿的使用者收到他們使用 scikit-image 時的棄用訊息。

不進行建議的 API 變更#

另一種可能性是完全拒絕不相容的 API 變更,除非在極端情況下。核心團隊認為,這本質上等同於將函式庫固定在 0.19 版本。

以「scikit-image2」作為新的套件名稱#

作者承認,新名稱的選擇應謹慎,以盡可能減少對 scikit-image 使用者群體和社群的干擾。然而,為了保護沒有設定版本上限的使用者,避免意外升級到新的 API,套件名稱 scikit-image 必須變更。變更匯入名稱 skimage 也同樣有利,因為它允許在同一個環境中使用兩個 API。

本文建議僅使用 skimage2 作為 scikit-image 的 API 2.0 版本的新名稱,包括匯入名稱和在 PyPI、conda-forge 和其他地方的名稱。支持這個建議的論點如下:

  • 專案只引入一個新名稱,從而使相關名稱的數量盡可能少。

  • 透過此變更,匯入名稱和套件名稱一致。

  • 使用者可能會混淆他們應該安裝 scikit-image2 還是 scikit-image-2。人們認為 skimage2 可以避免這種混淆。

  • 知道 skimage 是什麼的使用者,如果在某處的安裝說明中看到 skimage2,很可能會推斷出它是該套件的較新版本。

  • 使用者不太可能知道新的 API 2.0,而不知道新的套件名稱。建議發布的 scikit-image 1.1 版本可能會在安裝和更新過程中將使用者指向 skimage2,從而清楚地傳達後繼者的名稱。

以下是反對將套件命名為 skimage2 的論點:

  • 根據「最小驚訝原則」,scikit-image2 可能被認為是套件名稱最不令人驚訝的演變。

  • 它打破了包括 scikit-image 在內的其他 scikit 所遵循的慣例。(有人指出,這個慣例在一段時間內並非如此,而且在名稱中引入版本號已經是一個先例。)

前面的「相關工作」章節描述了其他專案如何處理類似的問題。

討論#

此 SKIP 是 SKIP-3 討論的結果。有關此 SKIP 動機的更多背景資訊,請參閱該文件的「決議」章節。

決議#

參考文獻和腳註#

所有 SKIP 都應聲明為專用於公眾領域,並使用 CC0 授權 [1],如下面的 Copyright 所示,並鼓勵使用 CC0+BY [2] 來註明出處。