在scikit-learn中将RandomizedSearchCV(或GridSearchCV)与LeaveOneGroupOut交叉验证相结合

4

我喜欢使用scikit的LOGO (leave one group out) 作为交叉验证方法,结合学习曲线来使用。这在我处理的大多数情况下非常有效,但我只能(高效地)使用那些在这些情况中最关键的两个参数(基于经验):最大特征数和估计器数量。以下是我的代码示例:

    Fscorer = make_scorer(f1_score, average = 'micro')
    gp = training_data["GP"].values
    logo = LeaveOneGroupOut()
    from sklearn.ensemble import RandomForestClassifier
    RF_clf100 = RandomForestClassifier (n_estimators=100, n_jobs=-1, random_state = 49)
    RF_clf200 = RandomForestClassifier (n_estimators=200, n_jobs=-1, random_state = 49)
    RF_clf300 = RandomForestClassifier (n_estimators=300, n_jobs=-1, random_state = 49)
    RF_clf400 = RandomForestClassifier (n_estimators=400, n_jobs=-1, random_state = 49)
    RF_clf500 = RandomForestClassifier (n_estimators=500, n_jobs=-1, random_state = 49)
    RF_clf600 = RandomForestClassifier (n_estimators=600, n_jobs=-1, random_state = 49)

    param_name = "max_features"
    param_range = param_range = [5, 10, 15, 20, 25, 30]


    plt.figure()
    plt.suptitle('n_estimators = 100', fontsize=14, fontweight='bold')
    _, test_scores = validation_curve(RF_clf100, X, y, cv=logo.split(X, y, groups=gp),
                                      param_name=param_name, param_range=param_range,
                                      scoring=Fscorer, n_jobs=-1)
    test_scores_mean = np.mean(test_scores, axis=1)
    plt.plot(param_range, test_scores_mean)
    plt.xlabel(param_name)
    plt.xlim(min(param_range), max(param_range))
    plt.ylabel("F1")
    plt.ylim(0.47, 0.57)
    plt.legend(loc="best")
    plt.show()


    plt.figure()
    plt.suptitle('n_estimators = 200', fontsize=14, fontweight='bold')
    _, test_scores = validation_curve(RF_clf200, X, y, cv=logo.split(X, y, groups=gp),
                                      param_name=param_name, param_range=param_range,
                                      scoring=Fscorer, n_jobs=-1)
    test_scores_mean = np.mean(test_scores, axis=1)
    plt.plot(param_range, test_scores_mean)
    plt.xlabel(param_name)
    plt.xlim(min(param_range), max(param_range))
    plt.ylabel("F1")
    plt.ylim(0.47, 0.57)
    plt.legend(loc="best")
    plt.show()
    ...
    ...

我真正希望的是将LOGO与网格搜索或随机搜索相结合,以进行更全面的参数空间搜索。

目前我的代码如下:

param_dist = {"n_estimators": [100, 200, 300, 400, 500, 600],
              "max_features": sp_randint(5, 30),
              "max_depth": sp_randint(2, 18),
              "criterion": ['entropy', 'gini'],
              "min_samples_leaf": sp_randint(2, 17)}

clf = RandomForestClassifier(random_state = 49)

n_iter_search = 45
random_search = RandomizedSearchCV(clf, param_distributions=param_dist,
                                   n_iter=n_iter_search,
                                   scoring=Fscorer, cv=8,
                                   n_jobs=-1)
random_search.fit(X, y)

当我用cv=logo.split(X, y, groups=gp)替换cv = 8时,我收到了以下错误信息:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-0092e11ffbf4> in <module>()
---> 35 random_search.fit(X, y)


/Applications/anaconda/lib/python2.7/site-packages/sklearn/model_selection/_search.pyc in fit(self, X, y, groups)
   1183                                           self.n_iter,
   1184                                           random_state=self.random_state)
-> 1185         return self._fit(X, y, groups, sampled_params)

/Applications/anaconda/lib/python2.7/site-packages/sklearn/model_selection/_search.pyc in _fit(self, X, y, groups, parameter_iterable)
    540 
    541         X, y, groups = indexable(X, y, groups)
--> 542         n_splits = cv.get_n_splits(X, y, groups)
    543         if self.verbose > 0 and isinstance(parameter_iterable, Sized):
    544             n_candidates = len(parameter_iterable)

/Applications/anaconda/lib/python2.7/site-packages/sklearn/model_selection/_split.pyc in get_n_splits(self, X, y, groups)
   1489             Returns the number of splitting iterations in the cross-validator.
   1490         """
-> 1491         return len(self.cv)  # Both iterables and old-cv objects support len
   1492 
   1493     def split(self, X=None, y=None, groups=None):

TypeError: object of type 'generator' has no len()

任何建议关于(1)正在发生的事情,更重要的是(2)我如何使其工作(将RandomizedSearchCV与LeaveOneGroupOut组合)?
* 2017年2月8日更新 *
使用@Vivek Kumar的建议random_search.fit(X, y, wells)cv=logo,它可以工作。
1个回答

4
您不应该将logo.split()传递给RandomizedSearchCV,而是只需将像logo这样的cv对象传递给它。RandomizedSearchCV在内部调用split()来生成训练测试索引。
您可以将您的gp组传递到fit()调用中的RandomizedSearchCV或GridSearchCV对象中。
不要这样做:
random_search.fit(X, y)

请执行以下操作:

random_search.fit(X, y, gp)

编辑:你还可以将gp作为字典传递给GridSearchCV或RandomizedSearchCV的构造函数中的fit_params参数。


我不确定我理解了。我在哪里传递 cv.get_n_splits - MyCarta
@MyCarta 抱歉,我说的是 logo.split(),而不是 cv.get_n_splits。我已经编辑了我的答案以消除混淆。 - Vivek Kumar
@Vivek Kumar 好的,那就更清楚了。你是说没有解决方法吗? - MyCarta
是的。除非可以将组直接提供到LOGO的构造函数中,否则无法直接在GridSearchCV中使用它。还有其他可行的解决方法。 - Vivek Kumar
@MyCarta 对不起,我很无知,但是你可以将你的分组传递给gridsearchcv或randomizedsearchcv的fit()方法。 - Vivek Kumar
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接