如何获得可重复但不同的GroupKFold实例

时间:2017-01-25 19:05:27

标签: python numpy scikit-learn

GroupKFold 来源中, random_state 设置为None

    def __init__(self, n_splits=3):
    super(GroupKFold, self).__init__(n_splits, shuffle=False,
                                     random_state=None)

因此,多次运行时(代码来自here

import numpy as np
from sklearn.model_selection import GroupKFold

for i in range(0,10):
    X = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
    y = np.array([1, 2, 3, 4])
    groups = np.array([0, 0, 2, 2])
    group_kfold = GroupKFold(n_splits=2)
    group_kfold.get_n_splits(X, y, groups)

    print(group_kfold)

    for train_index, test_index in group_kfold.split(X, y, groups):
        print("TRAIN:", train_index, "TEST:", test_index)
        X_train, X_test = X[train_index], X[test_index]
        y_train, y_test = y[train_index], y[test_index]
        print(X_train, X_test, y_train, y_test)
    print 
    print 

O / P

GroupKFold(n_splits=2)
('TRAIN:', array([0, 1]), 'TEST:', array([2, 3]))
(array([[1, 2],
       [3, 4]]), array([[5, 6],
       [7, 8]]), array([1, 2]), array([3, 4]))
('TRAIN:', array([2, 3]), 'TEST:', array([0, 1]))
(array([[5, 6],
       [7, 8]]), array([[1, 2],
       [3, 4]]), array([3, 4]), array([1, 2]))


GroupKFold(n_splits=2)
('TRAIN:', array([0, 1]), 'TEST:', array([2, 3]))
(array([[1, 2],
       [3, 4]]), array([[5, 6],
       [7, 8]]), array([1, 2]), array([3, 4]))
('TRAIN:', array([2, 3]), 'TEST:', array([0, 1]))
(array([[5, 6],
       [7, 8]]), array([[1, 2],
       [3, 4]]), array([3, 4]), array([1, 2]))
等等......

分裂是相同的。

如何为random_state设置GroupKFold,以便在几个不同的交叉验证试验中获得不同(但可重复)的拆分集?

例如,我想要

GroupKFold(n_splits=2, random_state=42)
('TRAIN:', array([0, 1]), 
  'TEST:', array([2, 3]))

('TRAIN:', array([2, 3]), 
'TEST:', array([0, 1]))


GroupKFold(n_splits=2, random_state=13)
('TRAIN:', array([0, 2]), 
 'TEST:', array([1, 3]))

('TRAIN:', array([1, 3]), 
'TEST:', array([0, 2]))

到目前为止,似乎策略可能是首先使用sklearn.utils.shuffle,如post中所述。然而,这实际上只是重新排列每个折叠的元素 - 它不会给我们新的分裂。

from sklearn.utils import shuffle
from sklearn.model_selection import GroupKFold
import numpy as np
import sys
import pdb

random_state = int(sys.argv[1])


X = np.arange(20).reshape((10,2))
y = np.arange(10)
groups = np.array([0,0,0,1,2,3,4,5,6,7])

def cv(X, y, groups, random_state):
    X_s, y_s, groups_s = shuffle(X,y, groups, random_state=random_state)
    cv_out = GroupKFold(n_splits=2)
    cv_out_splits = cv_out.split(X_s, y_s, groups_s)
    for train, test in cv_out_splits:
        print "---"
        print X_s[test]
        print y_s[test]
        print "test groups", groups_s[test]
        print "train groups", groups_s[train]
    pdb.set_trace()
print "***"
cv(X, y, groups, random_state)

输出:

>python sshuf.py 32

***
---
[[ 2  3]
 [ 4  5]
 [ 0  1]
 [ 8  9]
 [12 13]]
[1 2 0 4 6]
test groups [0 0 0 2 4]
train groups [7 6 1 3 5]
---
[[18 19]
 [16 17]
 [ 6  7]
 [10 11]
 [14 15]]
[9 8 3 5 7]
test groups [7 6 1 3 5]
train groups [0 0 0 2 4]

>python sshuf.py 234

***
---
[[12 13]
 [ 4  5]
 [ 0  1]
 [ 2  3]
 [ 8  9]]
[6 2 0 1 4]
test groups [4 0 0 0 2]
train groups [7 3 1 5 6]
---
[[18 19]
 [10 11]
 [ 6  7]
 [14 15]
 [16 17]]
[9 5 3 7 8]
test groups [7 3 1 5 6]
train groups [4 0 0 0 2]

7 个答案:

答案 0 :(得分:8)

  • KFold仅在shuffle=True时才被随机化。 Some datasets should not be shuffled.
  • GroupKFold根本没有随机化。因此random_state=None
  • GroupShuffleSplit可能更接近您所寻找的内容。

基于群组的分割者的比较:

  • GroupKFold中,测试集形成了所有数据的完整分区。
  • LeavePGroupsOut组合出所有可能的P组子集;对于P> 1,测试集将重叠。 1.由于这意味着P ** n_groups完全分裂,通常你需要一个小P,并且通常需要LeaveOneGroupOut,这与GroupKFoldk=1基本相同。
  • GroupShuffleSplit没有说明连续测试集之间的关系;每个列车/测试拆分都是独立完成的。

顺便说一下, Dmytro Lituiev has proposed an alternative GroupShuffleSplit algorithm更适合在指定test_size的测试集中获取正确数量的样本(不仅仅是正确数量的组)。

答案 1 :(得分:1)

子类和实现

random_state依赖_iter_test_masks( ... random_state = None )方法, 正如科幻小说super(...)来源中自我记录的那样。实例化中使用的random_state参数(.__init__()是 "只是"存储并留给用户的创造力,如果它将以test_mask代的任何定制方式使用或不会被使用(如在科学家来源评论中字面表达的那样):

(CIT:)

# Since subclasses must implement either _iter_test_masks or
# _iter_test_indices, neither can be abstract.

def _iter_test_masks(self, X=None, y=None, groups=None):
    """Generates boolean masks corresponding to test sets.

    By default, delegates to _iter_test_indices(X, y, groups)
    """
    for test_index in self._iter_test_indices(X, y, groups):
        test_mask = np.zeros(_num_samples(X), dtype=np.bool)
        test_mask[test_index] = True

    yield test_mask

定义一个依赖外部提供的进程random_state != None也应该执行一个公平的做法来保护 - 保存/存储RNG的实际当前状态(RNG_stateTUPLE = numpy.random.get_state()),设置从.__init__()调用接口,完成后,从保存的(numpy.random.set_state( RNG_stateTUPLE ))恢复RNG状态。

这样一种自定义过程既可以获得random_state值所需的依赖性,也可以获得可重复性。 的 Q.E.D。

答案 2 :(得分:1)

到目前为止,我的解决方案是简单地随机拆分组。这可能导致非常不平衡的群体(我认为GroupKFold旨在抵御),但希望每组的观察数量很少。

from sklearn.utils import shuffle
from sklearn.model_selection import GroupKFold
from numpy.random import RandomState
import numpy as np
import sys
import pdb

random_state = int(sys.argv[1])


X = np.arange(20).reshape((10,2))


y = np.arange(10)
groups = np.array([0,0,0,1,2,3,4,5,6,7])
for el in zip(range(len(y)),X,y,groups):
    print "ix, X, y, groups", el

def RandGroupKfold(groups, n_splits, random_state=None, shuffle_groups=False):

    ix = np.array(range(len(groups)))
    unique_groups = np.unique(groups)
    if shuffle_groups:
        prng = RandomState(random_state)
        prng.shuffle(unique_groups)
    splits = np.array_split(unique_groups, n_splits)
    train_test_indices = []

    for split in splits:
        mask = [el in split for el in groups]
        train = ix[np.invert(mask)]
        test = ix[mask]
        train_test_indices.append((train, test))
    return train_test_indices

splits = RandGroupKfold(groups, n_splits=3, random_state=random_state, shuffle_groups=True)

for train, test in splits:
    print "---"
    for el in zip(train, X[train], y[train], groups[train]):
        print "train ix, X, y, groups", el
    for el in zip(test, X[test], y[test], groups[test]):
        print "test ix, X, y, groups", el

数据:

ix, X, y, groups (0, array([0, 1]), 0, 0)
ix, X, y, groups (1, array([2, 3]), 1, 0)
ix, X, y, groups (2, array([4, 5]), 2, 0)
ix, X, y, groups (3, array([6, 7]), 3, 1)
ix, X, y, groups (4, array([8, 9]), 4, 2)
ix, X, y, groups (5, array([10, 11]), 5, 3)
ix, X, y, groups (6, array([12, 13]), 6, 4)
ix, X, y, groups (7, array([14, 15]), 7, 5)
ix, X, y, groups (8, array([16, 17]), 8, 6)
ix, X, y, groups (9, array([18, 19]), 9, 7)

随机状态为4

---
train ix, X, y, groups (0, array([0, 1]), 0, 0)
train ix, X, y, groups (1, array([2, 3]), 1, 0)
train ix, X, y, groups (2, array([4, 5]), 2, 0)
train ix, X, y, groups (3, array([6, 7]), 3, 1)
train ix, X, y, groups (4, array([8, 9]), 4, 2)
train ix, X, y, groups (7, array([14, 15]), 7, 5)
train ix, X, y, groups (8, array([16, 17]), 8, 6)
test ix, X, y, groups (5, array([10, 11]), 5, 3)
test ix, X, y, groups (6, array([12, 13]), 6, 4)
test ix, X, y, groups (9, array([18, 19]), 9, 7)
---
train ix, X, y, groups (4, array([8, 9]), 4, 2)
train ix, X, y, groups (5, array([10, 11]), 5, 3)
train ix, X, y, groups (6, array([12, 13]), 6, 4)
train ix, X, y, groups (8, array([16, 17]), 8, 6)
train ix, X, y, groups (9, array([18, 19]), 9, 7)
test ix, X, y, groups (0, array([0, 1]), 0, 0)
test ix, X, y, groups (1, array([2, 3]), 1, 0)
test ix, X, y, groups (2, array([4, 5]), 2, 0)
test ix, X, y, groups (3, array([6, 7]), 3, 1)
test ix, X, y, groups (7, array([14, 15]), 7, 5)
---
train ix, X, y, groups (0, array([0, 1]), 0, 0)
train ix, X, y, groups (1, array([2, 3]), 1, 0)
train ix, X, y, groups (2, array([4, 5]), 2, 0)
train ix, X, y, groups (3, array([6, 7]), 3, 1)
train ix, X, y, groups (5, array([10, 11]), 5, 3)
train ix, X, y, groups (6, array([12, 13]), 6, 4)
train ix, X, y, groups (7, array([14, 15]), 7, 5)
train ix, X, y, groups (9, array([18, 19]), 9, 7)
test ix, X, y, groups (4, array([8, 9]), 4, 2)
test ix, X, y, groups (8, array([16, 17]), 8, 6)

随机状态为5

---
train ix, X, y, groups (0, array([0, 1]), 0, 0)
train ix, X, y, groups (1, array([2, 3]), 1, 0)
train ix, X, y, groups (2, array([4, 5]), 2, 0)
train ix, X, y, groups (3, array([6, 7]), 3, 1)
train ix, X, y, groups (5, array([10, 11]), 5, 3)
train ix, X, y, groups (7, array([14, 15]), 7, 5)
train ix, X, y, groups (8, array([16, 17]), 8, 6)
test ix, X, y, groups (4, array([8, 9]), 4, 2)
test ix, X, y, groups (6, array([12, 13]), 6, 4)
test ix, X, y, groups (9, array([18, 19]), 9, 7)
---
train ix, X, y, groups (4, array([8, 9]), 4, 2)
train ix, X, y, groups (5, array([10, 11]), 5, 3)
train ix, X, y, groups (6, array([12, 13]), 6, 4)
train ix, X, y, groups (8, array([16, 17]), 8, 6)
train ix, X, y, groups (9, array([18, 19]), 9, 7)
test ix, X, y, groups (0, array([0, 1]), 0, 0)
test ix, X, y, groups (1, array([2, 3]), 1, 0)
test ix, X, y, groups (2, array([4, 5]), 2, 0)
test ix, X, y, groups (3, array([6, 7]), 3, 1)
test ix, X, y, groups (7, array([14, 15]), 7, 5)
---
train ix, X, y, groups (0, array([0, 1]), 0, 0)
train ix, X, y, groups (1, array([2, 3]), 1, 0)
train ix, X, y, groups (2, array([4, 5]), 2, 0)
train ix, X, y, groups (3, array([6, 7]), 3, 1)
train ix, X, y, groups (4, array([8, 9]), 4, 2)
train ix, X, y, groups (6, array([12, 13]), 6, 4)
train ix, X, y, groups (7, array([14, 15]), 7, 5)
train ix, X, y, groups (9, array([18, 19]), 9, 7)
test ix, X, y, groups (5, array([10, 11]), 5, 3)
test ix, X, y, groups (8, array([16, 17]), 8, 6)

答案 3 :(得分:1)

受到user0的answer(无法评论)的启发,但速度更快:

def RandomGroupKFold_split(groups, n, seed=None):  # noqa: N802
    """
    Random analogous of sklearn.model_selection.GroupKFold.split.

    :return: list of (train, test) indices
    """
    groups = pd.Series(groups)
    ix = np.arange(len(groups))
    unique = np.unique(groups)
    np.random.RandomState(seed).shuffle(unique)
    result = []
    for split in np.array_split(unique, n):
        mask = groups.isin(split)
        train, test = ix[~mask], ix[mask]
        result.append((train, test))

    return result

答案 4 :(得分:0)

我想组合k组的代码,还希望训练和测试集中的班级比例相同。因此,我在各组上进行了k倍分层,以便在折痕中保持相同的类别比率,然后使用这些组在折痕中放置样本。我还将随机种子包括在分层中以解决不同的拆分问题。

def Stratified_Group_KFold(Y, groups, n, seed=None):
    unique = np.unique(groups)
    group_Y = []
    for group in unique:
        y = Y[groups.index(subject)]
        group_Y.append(y)

    group_X = np.zeros_like(unique)
    skf_group = StratifiedKFold(n_splits = n, random_state = seed, shuffle=True)

    result = []
    for train_index, test_index in skf_group.split(group_X, group_Y):
        train_groups_in_fold = unique[train_index]
        test_groups_in_fold = unique[test_index]

        train = np.in1d(groups, train_groups_in_fold).nonzero()[0]
        test = np.in1d(groups, test_groups_in_fold).nonzero()[0]

        result.append((train, test))


    return result

答案 5 :(得分:0)

@ user0

  

例如,我想要

   GroupKFold(n_splits=2, random_state=42)
   ('TRAIN:', array([0, 1]), 
    'TEST:', array([2, 3]))

   ('TRAIN:', array([2, 3]), 
    'TEST:', array([0, 1]))

   GroupKFold(n_splits=2, random_state=13)
   ('TRAIN:', array([0, 2]), 
    'TEST:', array([1, 3]))

   ('TRAIN:', array([1, 3]), 
    'TEST:', array([0, 2]))

第二次拆分会将一组分为训练集和测试集。这是GroupKFold应该避免的。例如,在第二个拆分中,来自训练组0和组1的元素(数据集中的索引0和1)分别作为索引0和1。

对于您给出的示例,进行分组2倍拆分的方法不止一种,因为您只有2组。

答案 6 :(得分:0)

GroupKFold 根据组标签显示确定性。所以解决方案是分配新标签。我通过改组唯一组标识符列表并从 0 到 n_groups - 1 分配新标签来解决这个问题。

import numpy as np
from sklearn.model_selection import GroupKFold

def get_random_labels(labels, random_state):
    labels_shuffled = np.unique(labels)
    # shuffle works in place
    random_state.shuffle(labels_shuffled)
    new_labels_mapping = {k: i for i, k in enumerate(labels_shuffled)}
    new_labels = np.array([new_labels_mapping[label] for label in labels])
    reverse_dict = {v: k for k, v in new_labels_mapping.items()}
    return new_labels, reverse_dict

random_state = np.random.RandomState(41)
X = np.arange(20).reshape((10, 2))
y = np.arange(10)
groups = np.array([0, 0, 0, 1, 2, 3, 4, 5, 6, 7])

for _ in range(0, 5):
    group_kfold = GroupKFold(n_splits=2)
    new_labels, reverse_dict = get_random_labels(groups, random_state)
    
    print(group_kfold)

    for i, (train_index, test_index) in enumerate(group_kfold.split(X, y, new_labels)):
        X_train, X_test = X[train_index], X[test_index]
        y_train, y_test = y[train_index], y[test_index]
        groups_train, groups_test = groups[train_index], groups[test_index]
        print("Split no.", i + 1, "Training y:", y_train, "Testing y:", y_test)
    print()

输出:

GroupKFold(n_splits=2)
Split no. 1 Training y: [3 4 5 6 8] Testing y: [0 1 2 7 9]
Split no. 2 Training y: [0 1 2 7 9] Testing y: [3 4 5 6 8]

GroupKFold(n_splits=2)
Split no. 1 Training y: [3 4 7 8 9] Testing y: [0 1 2 5 6]
Split no. 2 Training y: [0 1 2 5 6] Testing y: [3 4 7 8 9]

GroupKFold(n_splits=2)
Split no. 1 Training y: [3 6 7 8 9] Testing y: [0 1 2 4 5]
Split no. 2 Training y: [0 1 2 4 5] Testing y: [3 6 7 8 9]

GroupKFold(n_splits=2)
Split no. 1 Training y: [5 6 7 8 9] Testing y: [0 1 2 3 4]
Split no. 2 Training y: [0 1 2 3 4] Testing y: [5 6 7 8 9]

GroupKFold(n_splits=2)
Split no. 1 Training y: [3 4 6 7 9] Testing y: [0 1 2 5 8]
Split no. 2 Training y: [0 1 2 5 8] Testing y: [3 4 6 7 9]

在 10 个样本中,我让前三个属于第 0 组,其他每个都属于自己独特的组。结果就是每次迭代分裂都不一样。

reverse_dict 对象用于获取原始标签的身份。