【问题标题】:Parallelize RandomizedSearchCV to restrict number CPUs used并行化 RandomizedSearchCV 以限制使用的 CPU 数量
【发布时间】:2022-02-22 00:22:50
【问题描述】:

当我使用 sklearn RandomizedSearchCV 拟合模型时,我试图限制 CPU 的使用数量,但不知何故我一直在使用所有 CPU。根据Python scikit learn n_jobs 的回答,我看到在 scikit-learn 中,我们可以使用n_jobs 来控制所使用的 CPU 核心数。

n_jobs 是一个整数,指定同时运行的worker的最大数量。如果给定 1,则根本不使用 joblib 并行度,这对于调试很有用。如果设置为 -1,则使用所有 CPU。
对于低于-1 的n_jobs,使用(n_cpus + 1 + n_jobs)。例如n_jobs=-2,除了一个以外的所有 CPU 都被使用。

但是当将n_jobs 设置为 -5 时,所有 CPU 仍会继续以 100% 运行。我查看了joblib 库以使用Paralleldelayed。但是我所有的 CPU 仍然在继续使用。这是我尝试过的:

from sklearn.model_selection import RandomizedSearchCV
from joblib import Parallel,delayed

def rscv_l(model, param_grid, X_train, y_train):
    rs_model = RandomizedSearchCV(model, param_grid, n_iter=10,
                            n_jobs=-5, verbose=2, cv=5,
                            scoring='r2')
    rs_model.fit(X_train, y_train)         # the cpu usage problem comes here
    return rs_model

# Here my attempt to parallelize and set my function as iterable
results = Parallel( n_jobs = -5 )( delayed( rscv_l )( model,
                                                      param_grid,
                                                      X, y )
                                                  for X, y
                                             in zip( [X_train],
                                                     [y_train] ) ) 

出了什么问题?


更新: 看着How do you stop numpy from multithreading?,我想我可能遇到了多线程问题。当我检查 numpy 配置时,我发现:

blas_mkl_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['user/lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['user/include']
blas_opt_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['user/lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['user/include']
lapack_mkl_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['user/lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['user/include']
lapack_opt_info:
    libraries = ['mkl_rt', 'pthread']
    library_dirs = ['user/lib']
    define_macros = [('SCIPY_MKL_H', None), ('HAVE_CBLAS', None)]
    include_dirs = ['user/include']

但提出的解决方案仍然对我不起作用:

import os
os.environ["OMP_NUM_THREADS"]        = "4" # export OMP_NUM_THREADS=4
os.environ["OPENBLAS_NUM_THREADS"]   = "4" # export OPENBLAS_NUM_THREADS=4 
os.environ["MKL_NUM_THREADS"]        = "6" # export MKL_NUM_THREADS=6
os.environ["VECLIB_MAXIMUM_THREADS"] = "4" # export VECLIB_MAXIMUM_THREADS=4
os.environ["NUMEXPR_NUM_THREADS"]    = "6" # export NUMEXPR_NUM_THREADS=6

import numpy
from sklearn.model_selection import RandomizedSearchCV

这解决了我的问题: 感谢@user3666197 的回答,我决定限制整个脚本的cpu 数量,并简单地使用n_jobs 和一个正整数。这解决了我的 CPU 使用问题:

import os
n_jobs = 2  # The number of tasks to run in parallel
n_cpus = 2  # Number of CPUs assigned to this process

pid = os.getpid()
print("PID: %i" % pid)

# Control which CPUs are made available for this script
cpu_arg = ''.join([str(ci) + ',' for ci in list(range(n_cpus))])[:-1]
cmd = 'taskset -cp %s %i' % (cpu_arg, pid)
print("executing command '%s' ..." % cmd)
os.system(cmd)

# hyperparameter tunning
rs_model = RandomizedSearchCV(xgb, param_grid, n_iter=10,
                            n_jobs=n_jobs, verbose=2, cv= n_folds,
                            scoring='r2')

#model fitting
rs_model.fit(X_train,y_train)

【问题讨论】:

    标签: python multithreading scikit-learn parallel-processing joblib


    【解决方案1】:

    问: “怎么了?

    A :
    没有一件事情我们可以说它“出错”,代码执行生态系统是如此多分层,这并不像我们希望的那样微不足道,并且有几个(不同的,一些隐藏的)地方,配置决定多少 CPU 内核实际上将承担整个处理负载。

    情况也取决于版本和特定配置(Scikit、Numpy、Scipy 对所使用的数字包的各自编译选项具有相互依赖关系和底层依赖关系)


    实验
    证明或反驳刚刚假设的语法 (d) 效果:

    鉴于在 RandomizedSearchCV(...) 方法中的顶级 n_jobs 参数中解释负数的记录特征,提交完全相同的任务,但配置使其具有明确的允许数量(顶级)n_jobs = CPU_cores_allowed_to_load 并观察在整个处理流程中实际加载的内核数量和时间。

    结果:
    当且仅当加载了“允许”的 CPU 核心数量时,顶级调用才会正确地将参数设置“传播”到每个方法或与处理流程一起使用的程序

    如果您的观察证明设置没有“遵守”,我们只能审查所有垂直源代码的整个范围来决定,谁应该为这种不遵守规定的不遵守行为负责n_jobs 的顶层设置上限。虽然用于 CPU 内核关联映射的 O/S 工具可能会给我们一些机会“从外部”限制使用的此类内核的数量,但会出现一些其他不利影响(附加管理成本是对性能影响最小的) -热管理引入的 CPU 核心“跳跃”,被关联图所禁止,将在当代处理器上导致越来越低的时钟频率(因为核心在数值密集型处理中确实很热),从而延长了整体任务处理时间,因为系统中有“更冷”(因此更快)的 CPU 核心(那些被关联映射阻止使用的 CPU 核心),但这些 CPU 核心与关联映射不允许使用的 CPU 核心完全相同用于临时放置我们的任务处理(而由于达到热上限而重新分配处理流程的热任务有一些时间冷却并重新获得在不降低 CPU 时钟的情况下运行的机会-费率)

    顶级调用可能设置了一个n_jobs-参数,但任何较低级别的组件都可能“遵守”了这个值(不知道有多少其他同时工作的对等点做了同样的事情——就像在@987654330 @ 和类似的构造函数,更不用说其他固有部署的 GIL 规避多线程库 - 因为它们恰好缺乏任何相互协调以保持顶级集合 n_jobs-ceiling )

    def rscv_l( model, param_grid, X_train, y_train ):
        rs_model = RandomizedSearchCV( model,
                                       param_grid,
                                       n_iter  = 10,
                                       n_jobs  =  1, # DO NOT CANNIBALISE MORE
                                       verbose =  2, #        AS BEING RUN
                                       cv      =  5, #        IN CONFLICT
                                       scoring = 'r2'#        WITH OUTER-SETTINGS
                                       )             # ----vvv----------
        rs_model.fit( X_train, y_train )             # the cpu usage problem comes here
        return rs_model
    
    ################################################################
    #
    # Here my attempt to parallelize and set my function as iterable
    #
    results = Parallel( n_jobs = -5 # <------------- joblib spawns that many workers
                        )( delayed( rscv_l ) # <---# HERE, avoid
                                  ( model,         #       UNCOORDINATED
                                    param_grid,    #       CPU-CANNIBALISM
                                    X, y )         #       ref. above
                                for X, y in zip( [X_train],
                                                 [y_train] )
                           )
    

    如果有兴趣了解更多详情

    你可能也喜欢这个
    "How to find an optimum number of processes in GridSearchCV( ..., n_jobs = ... )?"
    这个
    "How does scikit-learn handle..."

    "How to find ideal number of parallel processes..."
    来自
    其他sources,涵盖了这个问题。

    【讨论】:

    • 非常感谢您的详细解答!在受到您的一些 cmets 的启发后,我意识到我并没有事先划定 CPU 的数量。所以我决定限制整个脚本的 cpu 数量,并简单地使用 n_jobs 和正整数。我仍然有点不确定它们是如何交互的,但是问题描述中的更新代码解决了我的 CPU 使用问题。
    • 很高兴看到您使用提示感到高兴。另请注意,常见的 ML 任务,如 .fit() 方法是内存密集型的(因此实际的 CPU 缓存层次结构大小、缓存行长度和物理、裸机、MEM-I/O - 通道决定 CPU 饥饿开始发生的位置,无论您的计算设备能够使用多少“空闲” CPU 内核 - 内存块和饥饿的 CPU 只需要等待空闲的 MEM-I/O-通道以在任何数据写入之后获取缓存未命中或维护 CPU-缓存/RAM 一致性的任何新数据)。无论如何,请随意接受答案+G/L
    猜你喜欢
    • 1970-01-01
    • 2021-11-22
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    • 2022-08-15
    • 1970-01-01
    • 2019-02-18
    相关资源
    最近更新 更多