scikit-learn 并行,资源管理和配置
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_threads
、OpenBLAS_NUM_threads
和BLIS_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=8
。HistGradientBoostingClassifier
的每个实例将产生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_THREADS
、MKL NUM_THREADS
、OPENBLAS NUM_THREADS
或BLIS 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
当此环境变量设置为非零值时,将跳过需要网络访问的测试。