我有一个基本的嵌套 CV 循环,其中外部循环执行内部模型调整步骤。我的期望是每次折叠都应该抽取不同的超参数值随机样本。然而,在下面的示例中,每次折叠最终都会采样相同的值。
导入并制作数据集:
from sklearn.model_selection import RandomizedSearchCV, KFold, cross_validate
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.base import clone
from scipy.stats import uniform
import numpy as np
X, y = make_classification(n_features=10, random_state=np.random.RandomState(0))
嵌套 CV 循环:
#Used for tuning the random forest:
rf_tuner = RandomizedSearchCV(
RandomForestClassifier(random_state=np.random.RandomState(0)),
param_distributions=dict(min_samples_split=uniform(0.1, 0.9)),
n_iter=5,
cv=KFold(n_splits=2, shuffle=False),
random_state=np.random.RandomState(0),
n_jobs=1,
)
#Nested CV
for trn_idx, tst_idx in KFold(3).split(X, y):
#'cloned' will now share the same RNG as 'rf_tuner'
cloned = clone(rf_tuner)
#This should be consuming the RNG of 'rf_tuner'
cloned.fit(X[trn_idx], y[trn_idx])
#Report hyperparameter values sampled in this fold
display(cloned.cv_results_['params'])
#<more code for nested CV, not shown>
输出:
Fold 1/3:
[{'min_samples_split': 0.593},
{'min_samples_split': 0.743},
{'min_samples_split': 0.642},
{'min_samples_split': 0.590},
{'min_samples_split': 0.481}]
Fold 2/3:
[{'min_samples_split': 0.593},
{'min_samples_split': 0.743},
{'min_samples_split': 0.642},
{'min_samples_split': 0.590},
{'min_samples_split': 0.481}]
Fold 3/3:
[{'min_samples_split': 0.593},
{'min_samples_split': 0.743},
{'min_samples_split': 0.642},
{'min_samples_split': 0.590},
{'min_samples_split': 0.481}]
我首先用
RandomizedSearchCV
实例化 RandomForestClassifier
。我将搜索的 random_state=
设置为随机状态实例 np.random.RandomState(0)
。
对于外循环的每一次传递,我
clone()
和 fit()
搜索对象 - cloned
因此应该使用 same RNG 作为原始,在每次传递时对其进行变异。每个循环应该产生不同的超参数值采样。然而,如上所示,每次传递采样的超参数是相同的。这表明每个循环都以相同的未修改的 RNG 开始,而不是突变的 RNG。
文档说估计器的克隆共享相同的随机状态实例:
[...] 调用b = clone(a)
将消耗a.fit
的 RNG,调用b
将消耗b.fit
的 RNG,因为它们是相同的a
如何解释折叠之间没有随机化?
clone
对每个非估计器参数(source)执行
deepcopy
,因此在 RandomState
的情况下,克隆都将具有 不同 RandomState
对象,全部从相同状态开始(如在 get_state()
)。所以你的例子是预期的。
我不知道这是否曾经有不同的行为,或者文档在这一点上是否总是错误的。