8.3. 并行, 資源管理和配置?

8.3.1. 并行計算

一些scikit-learn的評估器與實用程序可以通過多核CPU進行并行計算,這要歸功于下面的組件:

  • 通過 joblib 庫. 可以通過 n_jobs 參數控制程序的進程或線程數量.
  • 通過OpenMP, 使用 C 或者 Cython 代碼.

此外,如果numpy安裝了特定的數字庫(如MKL、OpenBLAS或BLIS),scikit learn內部使用的一些numpy例程也可以并行化。

我們將在下面的小節中描述這三個場景。

8.3.1.1. 基于Joblib的并行計算

當底層實現使用joblib時,可以通過n_jobs參數來控制并行生成的worker(線程或進程)的數量。

注意 在估計器中并行化發生的位置(和方式)目前文獻記錄不多。請幫助我們改進我們的文檔并解決 issue 14228!

Joblib能夠同時支持多處理和多線程。joblib選擇生成線程還是進程取決于它使用的后端。Scikit learn通常依賴于loky后端,這是joblib的默認后端。Loky是一個多處理后端。

在進行多進程計算時,為了避免在每個進程中復制內存(對于大數據集來說這是不合理的),joblib將創建一個memmap當數據大于1MB時,所有進程都可以共享。

在某些特定情況下(當并行運行的代碼釋放GIL時),scikit learn將向joblib指示多線程后端是更可取的。

作為用戶,您可以通過使用上下文管理器來控制joblib將使用的后端(無論scikit learn推薦什么):

from joblib import parallel_backend

with parallel_backend('threading', n_jobs=2):
    # Your scikit-learn code here

有關更多詳細信息,請參閱joblib’s docs

實際上,并行性運算是否有助于改進運行時取決于許多因素。實驗通常是一個好主意,而不是假設增加并行計算的數量總是一件好事。在某些情況下,并行運行某些估計器或函數的多個副本會對性能造成極大的損害(請參閱下面的超額訂閱)。

8.3.1.2. 基于OpenMP的并行運算

OpenMP使用Cython或C編寫的代碼實現并行化,完全依賴多線程。默認情況下(除非joblib試圖避免超額使用)將使用盡可能多的線程。

您可以通過OMP-NUM-threads環境變量控制使用的線程的確切數量:

OMP_NUM_THREADS=4 python my_script.py

8.3.1.3. 基于特定數字庫的numpy并行化例程

Scikit learn在很大程度上依賴于NumPy和SciPy,后者在內部調用在MKL、OpenBLAS或BLIS等庫中實現的多線程線性代數例程。

OpenBLAS、MKL或BLIS庫使用的線程數可以通過MKL_NUM_threadsOpenBLAS_NUM_threadsBLIS_NUM_threads環境變量設置。

請注意,scikit learn無法直接控制這些實現。Scikit-learn完全依賴于Numpy和Scipy

注意 在撰寫本文時(2019年),NumPy和SciPy軟件包在pypi.org網站(通過pip安裝)。conda forge通道上安裝的conda包與OpenBLAS鏈接,而從anaconda.org 默認通道上安裝的conda包與MKL鏈接。

8.3.1.4. 過度消耗: 生成太多線程

通常建議避免使用比計算機上CPU數量多得多的進程或線程。當程序同時運行太多線程時,會發生過度消耗。

假設你有一臺有8個CPU的機器。考慮一種情況,在histgradientboostingcrifier(與OpenMP并行)上運行GridSearchCV(與joblib并行)和n_jobs=8HistGradientBoostingClassifier的每個實例將產生8個線程(因為您有8個CPU)。總共有8*8=64個線程,這會導致物理CPU資源的超額訂閱和調度開銷。

使用嵌套在joblib調用中的MKL,OpenBLAS或BLIS的并行化例程,可以以完全相同的方式產生超額訂閱。

joblib>=0.14開始,當使用oky后端(這是默認值)時,joblib將告訴其子進程,以限制它們可以使用的線程數,從而避免超額訂閱。在實踐中,joblib使用的啟發式方法是通過相應的環境變量告訴進程使用max_threads=n_cpu//n_jobs。回到上面的例子,由于GridSearchCV的joblib后端是loky,因此每個進程只能使用1個線程而不是8個線程,從而減少了訂閱過多的問題。

注意:

  • 手動設置其中一個環境變量(OMP NUM_THREADSMKL NUM_THREADSOPENBLAS NUM_THREADSBLIS NUM_THREADS)將優先于joblib嘗試執行的操作。線程總數將為n_jobs*_NUM_threads。請注意,設置此限制還將影響主進程中的計算,該進程只使用NUM線程。Joblib公開一個上下文管理器,以便更好地控制其工作線程的數量(請參閱下面鏈接的Joblib文檔)。

  • Joblib當前無法避免多線程上下文中的超額訂閱。它只能使用“loky”后端(它生成進程)。

您將在joblib文檔中找到有關joblib緩解超額訂閱的其他詳細信息。

8.3.2. 配置開關

8.3.2.1. python 運行時

sklearn.set_config 參數控制下列行為:

  • assume_finite(假設有限)

    用于跳過驗證,這可以加快計算速度,但如果數據包含NaN,則可能導致分段錯誤。

  • working_memory(工作內存)

    一些算法使用的臨時數組的最佳大小。

8.3.2.2. 環境變量

在導入scikit-learn之前應該先設置這些變量。

  • SKLEARN_SITE_JOBLIB

    當此環境變量設置為非零值時,scikit-learn使用站點joblib而不是其供應商版本。因此,必須安裝joblib才能運行scikit-learn。請注意,使用網站joblib需要您自擔風險:scikit-learn和joblib的版本必須兼容。當前,支持joblib 0.11+。此外,joblib.Memory的轉儲可能不兼容,您可能會丟失一些緩存并必須重新下載一些數據集。

    *從版本0.21開始不推薦使用:*從版本0.21開始,此參數無效,已刪除供應商的joblib,并且始終使用站點joblib。

  • SKLEARN_ASSUME_FINITE

    assume_finite參數 設置默認值 sklearn.set_config.

  • SKLEARN_WORKING_MEMORY

    working_memory參數 設置默認值 sklearn.set_config.

  • SKLEARN_SEED

    為了運行可重復性,在運行測試時設置全局隨機數生成器的種子。

  • SKLEARN_SKIP_NETWORK_TESTS

    當此環境變量設置為非零值時,將跳過需要網絡訪問的測試。