如何在scikit-learn中生成自定义交叉验证生成器?



我有一个不平衡的数据集,所以我有一个过采样策略,我只在训练数据时应用。我想使用像GridSearchCVcross_val_score这样的 scikit-learn 类来探索或交叉验证我的估计器(例如 SVC)上的一些参数。但是,我看到您要么传递 cv 折叠的数量,要么传递标准的交叉验证生成器。

我想创建一个自定义的 cv 生成器,以便我获得并分层 5 倍并仅对我的训练数据进行过采样(4 倍),并让 scikit-learn 查看我的估计器的参数网格并使用剩余的折叠进行评分以进行验证。

交叉验证生成器返回一个长度n_folds的迭代对象,其每个元素都是 numpy 一维数组的 2 元组,(train_index, test_index)包含该交叉验证运行的测试和训练集的索引。

因此,对于 10 倍交叉验证,自定义交叉验证生成器需要包含 10 个元素,每个元素包含一个包含两个元素的元组:

  • 该运行的训练子集的索引数组,涵盖 90% 的数据
  • 该运行的测试子集的索引数组,涵盖 10% 的数据

我正在研究一个类似的问题,其中我为数据的不同折叠创建了整数标签。我的数据集存储在 Pandas 数据帧myDf中,该数据帧具有交叉验证标签的列cvLabel。 我构造自定义交叉验证生成器myCViterator如下所示:

myCViterator = []
for i in range(nFolds):
trainIndices = myDf[ myDf['cvLabel']!=i ].index.values.astype(int)
testIndices =  myDf[ myDf['cvLabel']==i ].index.values.astype(int)
myCViterator.append( (trainIndices, testIndices) )

我遇到了类似的问题,这个快速的技巧对我有用:

class UpsampleStratifiedKFold:
def __init__(self, n_splits=3):
self.n_splits = n_splits
def split(self, X, y, groups=None):
for rx, tx in StratifiedKFold(n_splits=self.n_splits).split(X,y):
nix = np.where(y[rx]==0)[0]
pix = np.where(y[rx]==1)[0]
pixu = np.random.choice(pix, size=nix.shape[0], replace=True)
ix = np.append(nix, pixu)
rxm = rx[ix]
yield rxm, tx
def get_n_splits(self, X, y, groups=None):
return self.n_splits

这会对少数类进行采样(替换)以获得平衡的(k-1)倍训练集,但使第k个测试集不平衡。 这似乎适用于需要 CV 生成器的sklearn.model_selection.GridSearchCV和其他类似类。

Scikit-Learn为此提供了一种解决方法,他们的标签k折叠迭代器:

LabelKFold是 k 折叠的变体,可确保相同的标签不在测试和训练集中。例如,如果您从不同主题获得数据,并且希望通过对不同主题进行测试和培训来避免过度拟合(即学习人员的特定特征),这是必要的。

要在过度采样的情况下使用此迭代器,首先,您可以在数据帧中创建一列(例如cv_label),用于存储每行的索引值。

df['cv_label'] = df.index

然后,您可以应用过采样,确保在过采样中也复制cv_label列。此列将包含过采样数据的重复值。您可以从这些标签创建单独的系列或列表,以便以后处理:

cv_labels = df['cv_label']

请注意,在运行交叉验证器/分类器之前,需要从数据帧中删除此列。

将数据分为特征(不包括cv_label)和标签后,创建LabelKFold迭代器并运行所需的交叉验证函数:

clf = svm.SVC(C=1)
lkf = LabelKFold(cv_labels, n_folds=5)
predicted = cross_validation.cross_val_predict(clf, features, labels, cv=lkf)
class own_custom_CrossValidator:#like those in source sklearn/model_selection/_split.py 
def init(self):#coordinates,meter 
pass # self.coordinates = coordinates # self.meter = meter 
def split(self,X,y=None,groups=None):
#for compatibility with #cross_val_predict,cross_val_score 
for i in range(0,len(X)): yield tuple((np.array(list(range(0,len(X))))

相关内容

  • 没有找到相关文章

最新更新