Group/Cluster 使用 Sklearn 的 K-Fold CV
Group/Cluster K-Fold CV with Sklearn
我需要在某些模型上做 K 折 CV,但我需要确保验证(测试)数据集按组和 t
年数聚集在一起。 GroupKFold
很接近,但它仍然拆分了验证集(见第二折)。
例如,如果我有一组 2000-2008 年的数据,我想将其 K 折成 3 组。适当的集合是:验证:2000-2002,训练:2003-2008; V:2003-2005, T:2000-2002 & 2006-2008;和 V:2006-2008,T:2000-2005)。
有没有办法使用 K-Fold CV 对数据进行分组和聚类,其中验证集按 t
年聚类?
from sklearn.model_selection import GroupKFold
X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 8.8, 9, 10, 0.1, 0.2, 2.2]
y = ["a", "b", "b", "b", "c", "c", "c", "d", "d", "d", "a", "b", "b"]
groups = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4]
gkf = GroupKFold(n_splits=2)
for train_index, test_index in gkf.split(X, y, groups=groups):
print("Train:", train_index, "Validation:",test_index)
输出:
Train: [ 0 1 2 3 4 5 10 11 12] Validation: [6 7 8 9]
Train: [3 4 5 6 7 8 9] Validation: [ 0 1 2 10 11 12]
Train: [ 0 1 2 6 7 8 9 10 11 12] Validation: [3 4 5]
期望产出(假设每组 2 年):
Train: [ 7 8 9 10 11 12 ] Validation: [0 1 2 3 4 5 6]
Train: [0 1 2 10 11 12 ] Validation: [ 3 4 5 6 7 8 9 ]
Train: [ 0 1 2 3 4 5 ] Validation: [6 7 8 9 10 11 12]
虽然,测试和训练子集不是连续的,并且可以 select 更多年来分组。
希望我理解正确。
来自 scikits model_selection
的 LeaveOneGroupOut 方法可能有帮助:
假设您将组标签 0 分配给 2000-2002 年的所有数据点,将标签 1 分配给 2003 年至 2005 年之间的所有数据点,将标签 2 分配给 2006-2008 年的数据。
然后您可以使用以下方法创建训练和测试拆分,其中三个测试拆分是从三个组中的一个创建的:
from sklearn.model_selection import LeaveOneGroupOut
import numpy as np
groups=[1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3]
X=np.random.random(len(groups))
y=np.random.randint(0,4,len(groups))
logo = LeaveOneGroupOut()
print("n_splits=", logo.get_n_splits(X,y,groups))
for train_index, test_index in logo.split(X, y, groups):
print("train_idx:", train_index, "test_idx:", test_index)
输出:
n_splits= 3
train_idx: [ 4 5 6 7 8 9 10 11 12 13 14 15 16 17] test_idx: [0 1 2 3]
train_idx: [ 0 1 2 3 10 11 12 13 14 15 16 17] test_idx: [4 5 6 7 8 9]
train_idx: [0 1 2 3 4 5 6 7 8 9] test_idx: [10 11 12 13 14 15 16 17]
编辑
我想我现在终于明白你想要什么了。对不起,我花了这么长时间。
我不认为你想要的拆分方法已经在 sklearn 中实现了。但是我们可以轻松地扩展 BaseCrossValidator 方法。
import numpy as np
from sklearn.model_selection import BaseCrossValidator
from sklearn.utils.validation import check_array
class GroupOfGroups(BaseCrossValidator):
def __init__(self, group_of_groups):
"""
:param group_of_groups: list with length n_splits. Each entry in the list is a list with group ids from
set(groups). In each of the n_splits splits, the groups given in the current group_of_groups sublist are used
for validation.
"""
self.group_of_groups = group_of_groups
def get_n_splits(self, X=None, y=None, groups=None):
return len(self.group_of_groups)
def _iter_test_masks(self, X=None, y=None, groups=None):
if groups is None:
raise ValueError("The 'groups' parameter should not be None.")
groups=check_array(groups, copy=True, ensure_2d=False, dtype=None)
for g in self.group_of_groups:
test_index = np.zeros(len(groups), dtype=np.bool)
for g_id in g:
test_index[groups == g_id] = True
yield test_index
用法很简单。和以前一样,我们定义 X,y
和 groups
。此外,我们定义了一个列表列表(组的组),它定义了哪些组应该在哪个测试折叠中一起使用。
所以 g_of_g=[[1,2],[2,3],[3,4]]
意味着第 1 组和第 2 组在第一次折叠时用作测试集,而其余的第 3 组和第 4 组用于训练。在折叠 2 中,来自第 2 组和第 3 组的数据用作测试集等
我对命名不太满意 "GroupOfGroups" 所以也许你会找到更好的。
现在我们可以测试这个交叉验证器了:
X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 8.8, 9, 10, 0.1, 0.2, 2.2]
y = ["a", "b", "b", "b", "c", "c", "c", "d", "d", "d", "a", "b", "b"]
groups = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4]
g_of_g = [[1,2],[2,3],[3,4]]
gg = GroupOfGroups(g_of_g)
print("n_splits=", gg.get_n_splits(X,y,groups))
for train_index, test_index in gg.split(X, y, groups):
print("train_idx:", train_index, "test_idx:", test_index)
输出:
n_splits= 3
train_idx: [ 6 7 8 9 10 11 12] test_idx: [0 1 2 3 4 5]
train_idx: [ 0 1 2 10 11 12] test_idx: [3 4 5 6 7 8 9]
train_idx: [0 1 2 3 4 5] test_idx: [ 6 7 8 9 10 11 12]
请记住,我没有包括很多检查,也没有进行彻底的测试。因此,请仔细验证这是否适合您。
我需要在某些模型上做 K 折 CV,但我需要确保验证(测试)数据集按组和 t
年数聚集在一起。 GroupKFold
很接近,但它仍然拆分了验证集(见第二折)。
例如,如果我有一组 2000-2008 年的数据,我想将其 K 折成 3 组。适当的集合是:验证:2000-2002,训练:2003-2008; V:2003-2005, T:2000-2002 & 2006-2008;和 V:2006-2008,T:2000-2005)。
有没有办法使用 K-Fold CV 对数据进行分组和聚类,其中验证集按 t
年聚类?
from sklearn.model_selection import GroupKFold
X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 8.8, 9, 10, 0.1, 0.2, 2.2]
y = ["a", "b", "b", "b", "c", "c", "c", "d", "d", "d", "a", "b", "b"]
groups = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4]
gkf = GroupKFold(n_splits=2)
for train_index, test_index in gkf.split(X, y, groups=groups):
print("Train:", train_index, "Validation:",test_index)
输出:
Train: [ 0 1 2 3 4 5 10 11 12] Validation: [6 7 8 9]
Train: [3 4 5 6 7 8 9] Validation: [ 0 1 2 10 11 12]
Train: [ 0 1 2 6 7 8 9 10 11 12] Validation: [3 4 5]
期望产出(假设每组 2 年):
Train: [ 7 8 9 10 11 12 ] Validation: [0 1 2 3 4 5 6]
Train: [0 1 2 10 11 12 ] Validation: [ 3 4 5 6 7 8 9 ]
Train: [ 0 1 2 3 4 5 ] Validation: [6 7 8 9 10 11 12]
虽然,测试和训练子集不是连续的,并且可以 select 更多年来分组。
希望我理解正确。
来自 scikits model_selection
的 LeaveOneGroupOut 方法可能有帮助:
假设您将组标签 0 分配给 2000-2002 年的所有数据点,将标签 1 分配给 2003 年至 2005 年之间的所有数据点,将标签 2 分配给 2006-2008 年的数据。 然后您可以使用以下方法创建训练和测试拆分,其中三个测试拆分是从三个组中的一个创建的:
from sklearn.model_selection import LeaveOneGroupOut
import numpy as np
groups=[1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,3,3]
X=np.random.random(len(groups))
y=np.random.randint(0,4,len(groups))
logo = LeaveOneGroupOut()
print("n_splits=", logo.get_n_splits(X,y,groups))
for train_index, test_index in logo.split(X, y, groups):
print("train_idx:", train_index, "test_idx:", test_index)
输出:
n_splits= 3
train_idx: [ 4 5 6 7 8 9 10 11 12 13 14 15 16 17] test_idx: [0 1 2 3]
train_idx: [ 0 1 2 3 10 11 12 13 14 15 16 17] test_idx: [4 5 6 7 8 9]
train_idx: [0 1 2 3 4 5 6 7 8 9] test_idx: [10 11 12 13 14 15 16 17]
编辑
我想我现在终于明白你想要什么了。对不起,我花了这么长时间。
我不认为你想要的拆分方法已经在 sklearn 中实现了。但是我们可以轻松地扩展 BaseCrossValidator 方法。
import numpy as np
from sklearn.model_selection import BaseCrossValidator
from sklearn.utils.validation import check_array
class GroupOfGroups(BaseCrossValidator):
def __init__(self, group_of_groups):
"""
:param group_of_groups: list with length n_splits. Each entry in the list is a list with group ids from
set(groups). In each of the n_splits splits, the groups given in the current group_of_groups sublist are used
for validation.
"""
self.group_of_groups = group_of_groups
def get_n_splits(self, X=None, y=None, groups=None):
return len(self.group_of_groups)
def _iter_test_masks(self, X=None, y=None, groups=None):
if groups is None:
raise ValueError("The 'groups' parameter should not be None.")
groups=check_array(groups, copy=True, ensure_2d=False, dtype=None)
for g in self.group_of_groups:
test_index = np.zeros(len(groups), dtype=np.bool)
for g_id in g:
test_index[groups == g_id] = True
yield test_index
用法很简单。和以前一样,我们定义 X,y
和 groups
。此外,我们定义了一个列表列表(组的组),它定义了哪些组应该在哪个测试折叠中一起使用。
所以 g_of_g=[[1,2],[2,3],[3,4]]
意味着第 1 组和第 2 组在第一次折叠时用作测试集,而其余的第 3 组和第 4 组用于训练。在折叠 2 中,来自第 2 组和第 3 组的数据用作测试集等
我对命名不太满意 "GroupOfGroups" 所以也许你会找到更好的。
现在我们可以测试这个交叉验证器了:
X = [0.1, 0.2, 2.2, 2.4, 2.3, 4.55, 5.8, 8.8, 9, 10, 0.1, 0.2, 2.2]
y = ["a", "b", "b", "b", "c", "c", "c", "d", "d", "d", "a", "b", "b"]
groups = [1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4]
g_of_g = [[1,2],[2,3],[3,4]]
gg = GroupOfGroups(g_of_g)
print("n_splits=", gg.get_n_splits(X,y,groups))
for train_index, test_index in gg.split(X, y, groups):
print("train_idx:", train_index, "test_idx:", test_index)
输出:
n_splits= 3
train_idx: [ 6 7 8 9 10 11 12] test_idx: [0 1 2 3 4 5]
train_idx: [ 0 1 2 10 11 12] test_idx: [3 4 5 6 7 8 9]
train_idx: [0 1 2 3 4 5] test_idx: [ 6 7 8 9 10 11 12]
请记住,我没有包括很多检查,也没有进行彻底的测试。因此,请仔细验证这是否适合您。