【问题标题】:How to use RandomState with Sklearn RandomizedSearchCV on multiple cores如何在多核上使用 RandomState 和 Sklearn RandomizedSearchCV
【发布时间】:2019-05-12 14:34:34
【问题描述】:

我对在多核上运行时使用 np.random.RandomStatesklearn.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_))

我有几个问题。

  1. 尽管有异步方面,为什么我得到可重现的结果?

  2. RandomizedSearchCV 的文档提到了 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}  
    
  3. 总的来说,这是设置RandomizedSearchCV 以实现可重复性的正确方法吗?

  4. 是使用RandomState 的单个实例,还是应该为train_test_splitGradientBoostingClassifierStratifiedKFoldRandomizedSearchCV 使用单独的实例?此外,np.random.seed 的文档说在初始化RandomState 时设置了种子。这与 RandomizedSearchCV 设置种子有何交互作用?

  5. n_jobs 设置为使用少于所有内核时,我仍然可以看到所有内核上的活动,尽管每个内核的使用水平会随着内核数量的增加而增加,并且经过的时间会减少。这只是 sklearn 和/或 macOS 优化机器使用吗?

我正在使用 macOS 10.14.2、Python 3.6.7、Numpy 1.15.4、Scipy 1.1.0 和 Sklearn 0.20.1。

【问题讨论】:

    标签: python numpy scikit-learn scipy numpy-random


    【解决方案1】:

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

    我遇到了同样的问题,可以通过两件事解决它。

    • 我编写了自己的分发类,并意识到由于一个问题它非常慢。加快速度会有所帮助。

    • 我将pre_dispatch 设置为pre_dispatch=10*os.cpu_count() 之类的合理值。我认为问题在于它会在开始将所有数据适合其他内核之前准备好所有数据。

    【讨论】:

      【解决方案2】:

      候选参数是在使用ParameterSampler object 传递给多线程功能之前生成的。因此,只有一个 random_state 就足以实现 RandomizedSearchCV 的重现性。

      注意我说的是"reproducibility of RandomizedSearchCV"。对于其中使用的估算器(此处为base_clf),每个估算器都应像您所做的那样携带自己的random_state

      现在谈论a single instance of RandomState,对于顺序代码来说非常好。唯一需要担心的情​​况是多处理何时启动。所以让我们分析一下程序执行期间发生的步骤。

      1. 您使用种子设置了RandomState 对象。它现在有一个状态。
      2. train_test_split 内部,使用了StratifiedShuffleSplit(因为您使用了stratify 参数),它将使用传递的RandomState 对象在训练和测试数据中拆分和生成排列。所以RandomState的内部状态现在改变了。但它是连续的,无需担心。
      3. 现在您将这个random_state 对象设置在skf 中。但是在调用RandomizedSearchCV 中的fit() 之前不会发生拆分。所以状态是不变的。
      4. 之后,当search_clf.fit被调用时,the following happens

        1. 执行_run_search(),它将使用random_state一次生成所有参数组合(根据给定的n_iters)。所以仍然没有发生多线程,一切都很好。
        2. evaluate_candidates() 被调用。有趣的是:

          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)))
          
        3. parallel(delayed(_fit_and_score) 之后的部分仍然是顺序的,由父线程处理。

          • cv.split() 将使用 random_state(更改其状态)生成训练测试拆分
          • clone(estimator) 将克隆估计器的所有参数(random_state 也是)。所以RandomStatecv.split对象的改变状态变成estimator的基本状态
          • 上述两个步骤从父线程多次发生(拆分次数 x 参数组合次数)(没有异步性)。并且每次克隆原始的RandomState 以服务于估计器。所以结果是可重现的。
          • 所以在实际的多线程部分启动时,原来的RandomState没有被使用,但是每个估计器(线程)都会有自己的RandomState的副本

      希望这是有道理的,并回答您的问题。 Scikit-learn explicitly requests the user 设置如下:

      import numpy as np
      np.random.seed(42)
      

      使整个执行可重现,但你正在做的事情也会做。

      我不完全确定您的最后一个问题,因为我无法在我的系统上重现该问题。我有 4 个核心,当我设置 n_jobs=23 时,我只看到那些核心数量为 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 
      

      【讨论】:

      • 关于最后一个问题,我在您提到的FAQ page 上找到了关于np.random.seed 的答案。
      • @hsafer 好的。伟大的。谢谢你告诉我。
      • 我有一个问题:执行 randomisedsearchcv 后,所有使用的核心 (n_jobs = -1) 都没有在 macOS 上发布。查看活动监视器,我可以看到它们都仍然处于活动状态。有人知道吗?
      猜你喜欢
      • 2015-03-26
      • 2016-06-02
      • 2019-08-18
      • 2017-03-06
      • 2019-06-26
      • 2017-02-19
      • 2020-10-23
      • 2018-03-25
      • 2019-11-28
      相关资源
      最近更新 更多