Python 如何将RandomState与Sklearn RandomizedSearchCV一起在多核上使用

Python 如何将RandomState与Sklearn RandomizedSearchCV一起在多核上使用,python,numpy,scikit-learn,scipy,numpy-random,Python,Numpy,Scikit Learn,Scipy,Numpy Random,我对在多核上运行时使用np.random.RandomState和sklearn.model_selection.RandomizedSearchCV的正确方法感到困惑 我使用RandomState生成伪随机数,这样我的结果就可以重现。我给RandomizedSearchCV一个RandomState的实例,并将n_jobs=-1设置为使用所有六个核心 在多核上运行会引入异步元素。我预计这将导致在不同的运行中以不同的顺序从不同的内核请求伪随机数。因此,不同的运行应给出不同的结果,而不是显示再现性

我对在多核上运行时使用
np.random.RandomState
sklearn.model_selection.RandomizedSearchCV
的正确方法感到困惑

我使用
RandomState
生成伪随机数,这样我的结果就可以重现。我给
RandomizedSearchCV
一个
RandomState
的实例,并将
n_jobs=-1
设置为使用所有六个核心

在多核上运行会引入异步元素。我预计这将导致在不同的运行中以不同的顺序从不同的内核请求伪随机数。因此,不同的运行应给出不同的结果,而不是显示再现性

但事实上,结果是可重复的。对于给定的
n_iter
(即参数空间中的绘图数量),找到的最佳超参数值在每次运行中都是相同的。如果
n_jobs
是一个小于核心数的正数,我也会得到相同的值

具体而言,以下是代码:

import numpy as np
import scipy.stats as stats
from sklearn.datasets import load_iris
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import RandomizedSearchCV, StratifiedKFold, train_test_split

# Use RandomState for reproducibility.
random_state = np.random.RandomState(42)

# Get data. Split it into training and test sets.
iris = load_iris()
X, y = iris.data, iris.target
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.4, random_state=random_state, stratify=y)

# Prepare for hyper-parameter optimization.
n_iter = 1_000

base_clf = GradientBoostingClassifier(
    random_state=random_state, max_features='sqrt')

param_space = {'learning_rate': stats.uniform(0.05, 0.2),
               'n_estimators': [50, 100, 200],
               'subsample': stats.uniform(0.8, 0.2)}

# Generate data folds for cross validation.
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=random_state)

# Create the search classifier.
search_clf = RandomizedSearchCV(
    base_clf, param_space, n_iter=n_iter, scoring='f1_weighted', n_jobs=-1, 
    cv=skf, random_state=random_state, return_train_score=False)

# Optimize the hyper-parameters and print the best ones found.
search_clf.fit(X_train, y_train)
print('Best params={}'.format(search_clf.best_params_))
我有几个问题

  • 为什么尽管存在异步方面,我仍能得到可重复的结果

  • 的文档说明了
    random_state
    参数:“伪随机数生成器状态用于从可能值列表中随机均匀采样,而不是scipy.stats分布。”这是否意味着它不会影响参数空间中的分布?上面的代码是否足以确保再现性,或者我是否需要设置
    np.random.seed()
    ,或者编写类似以下内容:

    distn_learning_rate = stats.uniform(0.05, 0.2)  
    distn_learning_rate.random_state = random_state  
    distn_subsample = stats.uniform(0.8, 0.2)  
    distn_subsample.random_state = random_state  
    param_space = {'learning_rate': distn_learning_rate,  
                   'n_estimators': [50, 100, 200],  
                   'subsample': distn_subsample}  
    
    import numpy as np
    np.random.seed(42)
    
  • 总的来说,这是设置
    RandomizedSearchCV
    再现性的正确方法吗

  • 是否使用了
    RandomState
    的单个实例,或者我应该为
    train\u test\u split
    GradientBoostingClassifier
    StratifiedKFold
    RandomizedSearchCV
    使用单独的实例?另外,的文档说明种子是在
    RandomState
    初始化时设置的。这与设置种子如何交互

  • n_jobs
    设置为使用少于所有内核时,我仍然可以在所有内核上看到活动,尽管每个内核的使用级别随着内核数量的增加而增加,所用时间也会减少。这是否只是sklearn和/或macOS优化机器使用


  • 我使用的是macOS 10.14.2、Python 3.6.7、Numpy 1.15.4、Scipy 1.1.0和Sklearn 0.20.1。

    在使用。因此,只有一个
    random_状态
    就足以保证RandomizedSearchCV的再现性

    请注意,我说的是“随机搜索CV的再现性”。对于内部使用的估计器(
    base\u clf
    ),每个估计器都应该像您所做的那样携带自己的
    random\u state

    现在我们来讨论一下RandomState的一个实例,对于顺序的代码来说是非常好的。唯一需要担心的是多重处理何时开始。因此,让我们分析一下程序执行过程中发生的步骤

  • 使用种子设置
    RandomState
    对象。它现在有一个状态
  • train\u test\u split
    内部,使用了
    stratifiedshuffliesplit
    (因为您使用了
    stratify
    param),它将使用传递的
    RandomState
    对象在train和测试数据中分割和生成置换。因此,
    RandomState
    的内部状态现在已更改。但这是循序渐进的,没什么好担心的
  • 现在您可以在
    skf
    中设置此
    random_state
    对象。但在调用
    RandomizedSearchCV
    中的
    fit()
    之前,不会发生拆分。所以状态不变
  • 之后,当调用
    search\u clf.fit
    时:

  • 将使用
    随机状态
    一次生成所有参数组合(根据给定的
    n\u iter
    )。因此,多线程仍然没有发生,一切都很好
  • 被称为。有趣的是:

    out = parallel(delayed(_fit_and_score)(clone(base_estimator),
                                               X, y,
                                               train=train, test=test,
                                               parameters=parameters,
                                               **fit_and_score_kwargs)
                       for parameters, (train, test)
                       in product(candidate_params,
                                  cv.split(X, y, groups)))
    
  • 并行(延迟(_fit_和_score)之后的部分仍然是顺序的,由父线程处理

    • cv.split()
      将使用
      random_state
      (更改其状态)生成列车测试拆分
    • clone(estimator)
      将克隆估计器的所有参数,
      random\u状态也将克隆。因此,从
      cv.split
      对象中更改的
      RandomState
      状态将成为
      estimator
    • 上述两个步骤从父线程(无异步性)多次发生(拆分次数x参数组合次数)。每次克隆原始
      RandomState
      以服务于估计器。因此结果是可重复的
    • 因此,当实际的多线程部分启动时,不会使用原始的
      RandomState
      ,但每个估计器(线程)都有自己的
      RandomState
  • 希望这是有意义的,并回答您的问题。请学习以下设置:

    distn_learning_rate = stats.uniform(0.05, 0.2)  
    distn_learning_rate.random_state = random_state  
    distn_subsample = stats.uniform(0.8, 0.2)  
    distn_subsample.random_state = random_state  
    param_space = {'learning_rate': distn_learning_rate,  
                   'n_estimators': [50, 100, 200],  
                   'subsample': distn_subsample}  
    
    import numpy as np
    np.random.seed(42)
    
    使整个执行过程可复制,但您所做的也将如此

    我不能完全确定你的最后一个问题,因为我无法在我的系统上重现。我有4个内核,当我设置
    n_jobs=2
    3
    时,我只看到这些内核的百分比为100%,并且保持在20-30%左右。我的系统规格:

    System:
        python: 3.6.6 |Anaconda, Inc.| (default, Jun 28 2018, 17:14:51)  [GCC 7.2.0]
       machine: Linux-4.15.0-20-generic-x86_64-with-debian-buster-sid
    
    Python deps:
           pip: 18.1
    setuptools: 40.2.0
       sklearn: 0.20.1
         numpy: 1.15.4
         scipy: 1.1.0
        Cython: 0.29
        pandas: 0.23.4 
    

    在不使用所有cpu核心的方面:

    我有同样的问题,可以通过两件事来解决

    • 我写了自己的发行类,并意识到