我对在多核上运行时将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_))
我有几个问题。
尽管存在异步方面的原因,为什么我仍可以获得可重现的结果?
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}
总体来说,这是设置RandomizedSearchCV
的可重复性的正确方法吗?
是使用RandomState
的单个实例,还是应该为train_test_split
,GradientBoostingClassifier
,StratifiedKFold
和RandomizedSearchCV
使用单独的实例?另外,np.random.seed
的文档说,种子{{1}初始化时已设置。这与RandomState
设置种子如何相互作用?
将RandomizedSearchCV
设置为使用少于所有内核时,尽管每个内核的使用级别增加,并且随着内核数量的增加,经过的时间减少,但我仍然看到所有内核上的活动。这仅仅是sklearn和/或macOS优化机器使用率吗?
我正在使用macOS 10.14.2,Python 3.6.7,Numpy 1.15.4,Scipy 1.1.0和Sklearn 0.20.1。
答案 0 :(得分:1)
使用ParameterSampler object传递给多线程功能之前,将生成候选参数。因此,仅一个random_state
就足以实现RandomizedSearchCV的可重复性。
请注意,我说的是"reproducibility of RandomizedSearchCV"
。对于其中使用的估算器(此处为base_clf
),每个估算器都应像您所做的那样携带自己的random_state
。
现在谈论a single instance of RandomState
,对于顺序代码来说是完全可以的。唯一需要担心的是何时启动多进程。因此,让我们分析程序执行期间发生的步骤。
RandomState
对象。它现在处于状态。train_test_split
内使用StratifiedShuffleSplit
(因为您使用过stratify
参数),它将使用传递的RandomState
对象在训练和测试中拆分并生成排列数据。因此RandomState
的内部状态现在已更改。但是它的顺序性并没有什么可担心的。random_state
中设置了此skf
对象。但是在fit()
中的RandomizedSearchCV
被调用之前,不会发生分裂。因此状态不变。此后,当调用search_clf.fit
时,the following happens:
_run_search()
被执行,它将使用random_state
一次生成所有参数组合(根据给定的n_iters
)。因此,仍然没有多线程发生,一切都很好。 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)))
parallel(delayed(_fit_and_score)
之后的部分仍然是顺序的,由父线程处理。
cv.split()
将使用random_state
(更改其状态)来生成列车测试成绩clone(estimator)
将克隆估计器的所有参数(也random_state
)。因此,RandomState
对象中cv.split
的已更改状态成为estimator
中的基本状态RandomState
来为估算器服务。因此结果是可重复的。RandomState
,但是每个估计器(线程)将拥有自己的RandomState
副本希望这是有道理的,并回答您的问题。 Scikit学习explicitly requests the user可以像这样设置:
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
答案 1 :(得分:0)
关于它没有使用您所有的 cpu 内核:
我遇到了同样的问题,可以通过做两件事来解决它。
我已经编写了自己的分发类,并意识到由于一个问题,它非常慢。加快速度有帮助。
我将 findOne
设置为诸如 pre_dispatch
之类的合理值。我认为问题在于它在开始将内容安装到其他内核之前准备了所有数据。