常見問題#

在這裡,我們嘗試回答郵件列表中經常出現的一些問題。

關於專案#

專案名稱是什麼(很多人都搞錯了)?#

scikit-learn,而不是 scikit 或 SciKit,也不是 sci-kit learn。也不是先前使用的 scikits.learn 或 scikits-learn。

專案名稱如何發音?#

sy-kit learn。sci 代表 science!

為什麼是 scikit?#

有多個 scikit,它們是圍繞 SciPy 建立的科學工具箱。除了 scikit-learn 之外,另一個流行的工具箱是 scikit-image

你們支援 PyPy 嗎?#

由於維護人員資源有限且使用者數量少,因此不正式支援將 scikit-learn 與 PyPy(具有內建即時編譯器的替代 Python 實作)一起使用。

我如何取得授權,將 scikit-learn 中的圖片用於我的工作?#

可以透過 scikit-learn 儲存庫中包含的圖片和 scikit-learn 文件中產生的圖片,以 BSD 3 條款授權用於您的工作。強烈鼓勵並感謝引用 scikit-learn。請參閱 引用 scikit-learn

實作決策#

為什麼沒有支援深度學習或強化學習?未來會有這樣的支援嗎?#

深度學習和強化學習都需要豐富的詞彙來定義架構,其中深度學習還需要 GPU 進行高效運算。但是,這兩者都不符合 scikit-learn 的設計限制。因此,深度學習和強化學習目前不在 scikit-learn 尋求實現的範圍內。

您可以在你們會加入 GPU 支援嗎?中找到有關加入 GPU 支援的更多資訊。

請注意,scikit-learn 目前在 sklearn.neural_network 中實作了一個簡單的多層感知器。我們只會接受此模組的錯誤修復。如果您想實作更複雜的深度學習模型,請轉向熱門的深度學習框架,例如 tensorflowkeraspytorch

你們會將圖形模型或序列預測加入 scikit-learn 嗎?#

在可預見的未來不會。scikit-learn 嘗試為機器學習中的基本任務提供統一的 API,並使用 pipeline 和網格搜尋等元演算法將所有內容連結在一起。結構化學習所需的概念、API、演算法和專業知識與 scikit-learn 可以提供的不同。如果我們開始進行任意的結構化學習,我們就需要重新設計整個套件,並且專案很可能會因為自身的負擔而崩潰。

有兩個專案具有與 scikit-learn 相似的 API,可以進行結構化預測

  • pystruct 處理一般的結構化學習(專注於具有近似推論的任意圖形結構上的 SSVM;將樣本的概念定義為圖形結構的實例)。

  • seqlearn 僅處理序列(專注於精確推論;具有 HMM,但主要為了完整性;將特徵向量視為樣本,並使用偏移編碼來表示特徵向量之間的依賴關係)。

為什麼你們從 scikit-learn 中移除 HMM?#

請參閱 你們會將圖形模型或序列預測加入 scikit-learn 嗎?

你們會加入 GPU 支援嗎?#

預設加入 GPU 支援會引入大量特定於硬體的軟體依賴關係,並且需要重新實作現有的演算法。這會使普通使用者更難安裝 scikit-learn,並且開發人員更難維護程式碼。

但是,自 2023 年以來,如果輸入資料是以 PyTorch 或 CuPy 陣列的形式提供,並且 scikit-learn 已設定為接受此類輸入(如 Array API 支援(實驗性)中所述),則已經有一份有限但不斷成長的scikit-learn 估計器清單可以在 GPU 上執行。此 Array API 支援允許 scikit-learn 在 GPU 上執行,而不會在主要套件中引入大量且特定於硬體的軟體依賴關係。

大多數依賴 NumPy 進行計算密集型運算的估計器都可以考慮 Array API 支援,因此也可以考慮 GPU 支援。

但是,並非所有 scikit-learn 估計器都適合透過 Array API 在 GPU 上有效執行,這有基本演算法上的原因。例如,目前在 scikit-learn 中使用 Cython 實作的基於樹狀結構的模型基本上不是基於陣列的演算法。其他演算法(例如 k 平均值或 k 最近鄰居)依賴基於陣列的演算法,但也以 Cython 實作。使用 Cython 手動交錯連續陣列運算,以避免將效能降低的記憶體存取引入到大型中間陣列:這種低階演算法重寫稱為「核心融合」,並且在可預見的未來無法透過 Array API 表示。

要為無法使用 Array API 有效實作的估計器加入有效的 GPU 支援,需要為 scikit-learn 設計並採用更靈活的擴充系統。在以下 GitHub 問題(正在討論中)中正在考慮這種可能性

與其他工具相比,為什麼類別變數需要在 scikit-learn 中進行預處理?#

scikit-learn 大部分假設資料是以 NumPy 陣列或 SciPy 稀疏矩陣的形式存在,且為單一數值資料類型 (dtype)。目前,這些資料結構並未明確表示類別變數。因此,與 R 的 data.framespandas.DataFrame 不同,我們需要將類別特徵明確轉換為數值,如 編碼類別特徵 中所討論。另請參閱 使用混合資料類型 Column Transformer,了解如何處理異質 (例如類別和數值) 資料的範例。

請注意,最近 HistGradientBoostingClassifierHistGradientBoostingRegressor 已透過 categorical_features="from_dtype" 選項,原生支援類別特徵。此選項依賴於根據 pandas.CategoricalDtypepolars.datatypes.Categorical 資料類型來推斷資料的哪些欄位是類別型的。

scikit-learn 是否原生支援各種資料框架類型?#

Scikit-learn 對 pandas.DataFramepolars.DataFrame 的支援有限。Scikit-learn 的估算器可以接受這兩種資料框架類型作為輸入,而 scikit-learn 的轉換器可以使用 set_output API 輸出資料框架。如需更多詳細資訊,請參閱 介紹 set_output API

然而,scikit-learn 估算器的內部計算依賴於數值運算,這些運算在同質資料結構 (如 NumPy 陣列或 SciPy 稀疏矩陣) 上執行效率更高。因此,大多數 scikit-learn 估算器會在內部將資料框架輸入轉換為這些同質資料結構。同樣地,資料框架輸出也是從這些同質資料結構產生。

另請注意,ColumnTransformer 可以透過將依名稱或資料類型選擇的資料框架欄位的同質子集映射到專用的 scikit-learn 轉換器,來方便地處理異質 pandas 資料框架。因此,在處理異質資料框架時,ColumnTransformer 通常會用在 scikit-learn 管線的第一步 (如需更多詳細資訊,請參閱 管線:串接估算器)。

另請參閱 使用混合資料類型 Column Transformer,了解如何處理異質 (例如類別和數值) 資料的範例。

您是否計畫在管線中實作目標 y 的轉換?#

目前,轉換僅適用於管線中的特徵 X。關於無法在管線中轉換 y 的問題,一直存在著長期的討論。請追蹤 GitHub issue #4143。同時,您可以參考 TransformedTargetRegressorpipegraphimbalanced-learn。請注意,scikit-learn 已解決在訓練之前對 y 應用可逆轉換,並在預測後反轉轉換的情況。scikit-learn 計畫解決在訓練時轉換 y,但在測試時不轉換,以用於重新取樣和類似用途的情況,例如 imbalanced-learn。一般來說,這些使用案例可以使用自訂元估算器 (meta estimator) 而不是 Pipeline 來解決。

為什麼線性模型有這麼多不同的估算器?#

通常,每種模型類型都有一個分類器和一個回歸器,例如 GradientBoostingClassifierGradientBoostingRegressor。兩者都有相似的選項,且都有 loss 參數,這在回歸案例中特別有用,因為它可以估計條件均值以及條件分位數。

對於線性模型,有許多非常接近的估算器類別。讓我們看看:

維護者觀點:它們原則上都做相同的事情,並且僅因施加的懲罰而有所不同。然而,這對基礎最佳化問題的解決方式有很大的影響。最終,這相當於使用來自線性代數的不同方法和技巧。一個特例是 SGDRegressor,它包含所有 4 個先前的模型,並且由於最佳化程序而有所不同。另一個副作用是不同的估算器偏好不同的資料佈局 (X 為 C-連續或 F-連續、稀疏 csr 或 csc)。看似簡單的線性模型的這種複雜性是導致不同懲罰有不同估算器類別的原因。

使用者觀點:首先,目前的設計靈感來自科學文獻,其中具有不同正規化/懲罰的線性回歸模型被賦予不同的名稱,例如嶺回歸。擁有具有相應名稱的不同模型類別,可以讓使用者更容易找到這些回歸模型。其次,如果將上述所有 5 個線性模型統一為一個類別,則會存在具有大量選項的參數,例如 solver 參數。此外,不同的參數之間會存在許多排他性的相互作用。例如,參數 solverprecomputeselection 的可能選項會取決於懲罰參數 alphal1_ratio 的選定值。

貢獻#

我如何為 scikit-learn 做出貢獻?#

請參閱 貢獻。在想要新增新演算法之前 (這通常是一項重大且耗時的工作),建議從 已知問題 開始。請勿直接聯絡 scikit-learn 的貢獻者以討論為 scikit-learn 做出貢獻的事宜。

為什麼我的提取請求沒有受到任何關注?#

scikit-learn 的審閱流程需要大量時間,貢獻者不應因提取請求缺乏活動或審閱而感到沮喪。我們非常重視第一次就把事情做對,因為維護和後續變更的成本很高。我們很少發布任何「實驗性」程式碼,因此我們的所有貢獻都會立即被大量使用,並且最初應具有最高品質。

除此之外,scikit-learn 的審閱頻寬有限;許多審閱者和核心開發人員都是在他們自己的時間內開發 scikit-learn。如果您的提取請求的審閱來得很慢,很可能是因為審閱者很忙。我們請求您的諒解,並要求您不要僅因此原因而關閉您的提取請求或停止您的工作。

新演算法的納入標準是什麼?#

我們只考慮納入成熟的演算法。一個經驗法則是,至少在發表後 3 年、被引用 200 次以上,並且具有廣泛的使用和實用性。對於廣泛使用的方法提供明確改進的技術 (例如,增強的資料結構或更有效率的近似技術) 也會被考慮納入。

從符合上述標準的演算法或技術中,只會接受那些符合 scikit-learn 目前 API 的演算法,也就是具有 fitpredict/transform 介面,並且通常具有 numpy 陣列或稀疏矩陣的輸入/輸出。

貢獻者應以研究論文和/或其他類似套件中的實作來支持擬議新增內容的重要性,透過常見的使用案例/應用來證明其用途,並以基準測試和/或繪圖來證實效能改進 (如果有的話)。預期擬議的演算法至少在某些方面應該優於 scikit-learn 中已實作的方法。

如果符合以下條件,將加速現有模型的新演算法納入會比較容易:

  • 它不會引入新的超參數(因為這會使函式庫更具未來性),

  • 清楚記錄貢獻何時能提高速度以及何時不能提高速度很容易,例如,「當 n_features >> n_samples 時」,

  • 基準測試清楚地顯示速度的提升。

此外,請注意,您的實作不必在 scikit-learn 中才能與 scikit-learn 工具一起使用。您可以透過 scikit-learn 相容的方式實作您最喜歡的演算法,將其上傳到 GitHub 並告知我們。我們很樂意將其列在相關專案下。如果您已經在 GitHub 上有一個遵循 scikit-learn API 的套件,您可能也會有興趣看看scikit-learn-contrib

為什麼您對 scikit-learn 中包含的演算法如此挑剔?#

程式碼伴隨著維護成本,我們需要平衡程式碼量和團隊規模(此外,複雜性會隨著功能的數量非線性地擴展)。該套件依賴核心開發人員利用他們的空閒時間來修復錯誤、維護程式碼和審查貢獻。任何添加的演算法都需要開發人員將來的關注,屆時原始作者可能早已失去興趣。另請參閱新演算法的納入標準是什麼?。如需關於開放原始碼軟體中長期維護問題的精彩閱讀,請參閱道路與橋樑的執行摘要

使用 scikit-learn#

取得 scikit-learn 使用協助的最佳方法是什麼?#

  • 一般機器學習問題:使用Cross Validated,並加上 [machine-learning] 標籤。

  • scikit-learn 使用問題:使用Stack Overflow,並加上 [scikit-learn][python] 標籤。您也可以使用郵寄清單

請務必包含一個最小的可重製程式碼片段(理想情況下短於 10 行),以強調您在玩具資料集上的問題(例如來自sklearn.datasets 或使用 numpy.random 的函數隨機產生,並加上固定的隨機種子)。請刪除任何不必要於重製您的問題的程式碼行。

只需將您的程式碼片段複製貼上到已安裝 scikit-learn 的 Python shell 中,即可重製該問題。別忘了包含 import 語句。如需更多關於編寫良好重製程式碼片段的指引,請參閱:https://stackoverflow.com/help/mcve

如果您的問題引發您不了解的例外狀況(即使在 Google 搜尋後),請務必包含執行重製腳本時獲得的完整追溯。

如需錯誤報告或功能要求,請使用 GitHub 上的議題追蹤器

警告

請勿直接透過電子郵件聯繫任何作者尋求協助、報告錯誤或任何其他與 scikit-learn 相關的問題。

我應該如何儲存、匯出或部署估算器以供生產使用?#

請參閱模型持久化

如何建立 Bunch 物件?#

Bunch 物件有時會用作函數和方法的輸出。它們擴展了字典,使值可以通過鍵 bunch["value_key"] 或屬性 bunch.value_key 存取。

它們不應該用作輸入。因此,除非您正在擴展 scikit-learn 的 API,否則您幾乎不需要建立Bunch 物件。

如何將我自己的資料集載入 scikit-learn 可用的格式?#

一般而言,scikit-learn 適用於任何以 numpy 陣列或 scipy 稀疏矩陣形式儲存的數值資料。其他可轉換為數值陣列的類型(例如pandas.DataFrame)也是可以接受的。

如需更多關於將資料檔案載入這些可用資料結構的資訊,請參閱載入外部資料集

我該如何處理字串資料(或樹狀結構、圖形...)?#

scikit-learn 估算器假設您將向它們提供實值特徵向量。這個假設幾乎硬式編碼在整個函式庫中。但是,您可以透過幾種方式向估算器提供非數值輸入。

如果您有文字文件,您可以使用詞頻特徵;請參閱文字特徵提取以了解內建的文字向量化器。如需從任何種類的資料中提取更廣泛的特徵,請參閱從字典載入特徵特徵雜湊

另一個常見的情況是當您有非數值資料,並且在這些資料上有自訂距離(或相似度)度量。範例包括具有編輯距離(又稱 Levenshtein 距離)的字串,例如 DNA 或 RNA 序列。這些可以編碼為數字,但這樣做既麻煩又容易出錯。處理任意資料上的距離度量可以通過兩種方式完成。

首先,許多估算器採用預先計算的距離/相似度矩陣,因此如果資料集不是太大,您可以計算所有輸入配對的距離。如果資料集很大,您可以使用只有一個「特徵」的特徵向量,該「特徵」是指向單獨資料結構的索引,並提供一個自訂度量函數,該函數會在此資料結構中查找實際資料。例如,若要將dbscan 與 Levenshtein 距離一起使用

>>> import numpy as np
>>> from leven import levenshtein  
>>> from sklearn.cluster import dbscan
>>> data = ["ACCTCCTAGAAG", "ACCTACTAGAAGTT", "GAATATTAGGCCGA"]
>>> def lev_metric(x, y):
...     i, j = int(x[0]), int(y[0])  # extract indices
...     return levenshtein(data[i], data[j])
...
>>> X = np.arange(len(data)).reshape(-1, 1)
>>> X
array([[0],
       [1],
       [2]])
>>> # We need to specify algorithm='brute' as the default assumes
>>> # a continuous feature space.
>>> dbscan(X, metric=lev_metric, eps=5, min_samples=2, algorithm='brute')  
(array([0, 1]), array([ 0,  0, -1]))

請注意,上面的範例使用了第三方編輯距離套件 leven。對於樹狀結構核心、圖形核心等,可以使用類似的技巧,但需要小心。

為什麼我在 OSX 或 Linux 下使用 n_jobs > 1 時有時會發生當機/凍結?#

一些 scikit-learn 工具,例如 GridSearchCVcross_val_score,在內部依靠 Python 的 multiprocessing 模組,透過傳遞 n_jobs > 1 作為參數,將執行平行化到多個 Python 處理程序中。

問題在於,Python multiprocessing 會進行 fork 系統呼叫,但出於效能考量,並未在之後執行 exec 系統呼叫。許多函式庫(例如 OSX 下的 Accelerate 或 vecLib 的某些版本)、(MKL 的某些版本)、GCC 的 OpenMP 執行階段、nvidia 的 Cuda(以及可能許多其他的函式庫)管理自己的內部執行緒池。在呼叫 fork 時,子處理程序中的執行緒池狀態會損壞:執行緒池認為它有很多執行緒,而實際上只有主執行緒的狀態被 fork 了。可以更改函式庫以使其檢測何時發生 fork 並在這種情況下重新初始化執行緒池:我們對 OpenBLAS 進行了此操作(自 0.2.10 版起已合併到主分支中),並且我們為 GCC 的 OpenMP 執行階段貢獻了一個修補程式(尚未審查)。

但最終真正的罪魁禍首是 Python 的 multiprocessing,它執行 fork 而不執行 exec,以減少為平行運算啟動和使用新的 Python 處理程序的開銷。不幸的是,這違反了 POSIX 標準,因此一些軟體編輯器(例如 Apple)拒絕將 Accelerate 和 vecLib 中缺少 fork 安全性視為錯誤。

在 Python 3.4+ 中,現在可以將 multiprocessing 配置為使用 "forkserver""spawn" 啟動方法(而不是預設的 "fork")來管理處理程序池。為了在使用 scikit-learn 時解決此問題,您可以將 JOBLIB_START_METHOD 環境變數設定為 "forkserver"。但是,使用者應該知道,使用 "forkserver" 方法會阻止 joblib.Parallel 呼叫在 shell 工作階段中互動定義的函數。

如果您有自訂程式碼直接使用 multiprocessing,而不是透過 joblib 來使用,您可以為您的程式全域啟用 "forkserver" 模式。請在您的主程式碼中插入以下指示:

import multiprocessing

# other imports, custom code, load data, define model...

if __name__ == "__main__":
    multiprocessing.set_start_method("forkserver")

    # call scikit-learn utils with n_jobs > 1 here

您可以在 multiprocessing 文件中找到關於新啟動方法的更多預設值。

為什麼我的工作使用的核心比用 n_jobs 指定的還多?#

這是因為 n_jobs 只控制使用 joblib 進行平行化的常式的工作數量,但平行程式碼可能來自其他來源。

  • 某些常式可能會使用 OpenMP(針對以 C 或 Cython 撰寫的程式碼)進行平行化。

  • scikit-learn 很大程度上依賴於 numpy,而 numpy 又可能依賴於 MKL、OpenBLAS 或 BLIS 等數值函式庫,這些函式庫可以提供平行實作。

欲瞭解更多詳細資訊,請參閱我們的關於平行化的說明

我該如何為整個執行設定 random_state#

請參閱控制隨機性