Как получить воспроизводимые, но различные экземпляры GroupKFold

В GroupKFold источника, random_state установлен в None

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

Следовательно, при запуске несколько раз (код отсюда)

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 

о/р

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, как предлагается в этом посте. Тем не менее, это на самом деле просто переставляет элементы каждого сгиба - это не дает нам новых расколов.

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]

Ответ 1

Сравнение групповых сплиттеров:

  • В GroupKFold наборы тестов образуют полный раздел всех данных.
  • LeavePGroupsOut оставляет все возможные подмножества P групп; тестовые наборы будут перекрываться при P> 1. Поскольку это означает, что P ** n_groups разделяется в целом, часто вам нужен маленький P, и чаще всего вы хотите LeaveOneGroupOut который в основном совпадает с GroupKFold с k=1.
  • GroupShuffleSplit делает никаких заявлений о связи между последовательными наборами тестов; каждое разделение поезда/теста выполняется независимо.

Кроме того, Дмитрий Литуев предложил альтернативный алгоритм GroupShuffleSplit , который позволяет получать правильное количество выборок (а не просто правильное количество групп) в наборе тестов для указанного test_size.

Ответ 2

Подкласс и реализация

a random_state зависимый _iter_test_masks( ... random_state = None ) метод, как это было самодокументировано в источнике sci-kit super(...). Параметр random_state, используемый в создании экземпляра (.__init__() "просто" сохранен и оставлен для творчества пользователя, если он будет использоваться или не будет использоваться каким-либо образом для генерации test_mask (как буквально выражено в комментариях исходного кода sci-kit):

(соч:.)

# 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__() интерфейс и после завершения восстановления состояния RNG из сохраненного (numpy.random.set_state( RNG_stateTUPLE )).

Таким образом, такой пользовательский процесс получает как требуемую зависимость от значения random_state, так и воспроизводимости. Q.E.D.

Ответ 3

Мое решение до сих пор состояло в том, чтобы просто случайным образом разделить группы. Это может привести к очень неуравновешенным группам (что, по-моему, 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)

Ответ 4

Вдохновлен ответом user0 (не может комментировать), но быстрее:

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

Ответ 5

Я хотел объединить код для групп k-fold, а также хотел, чтобы в обучающем и тестовом наборе было одинаковое количество классов. Итак, я прогнал стратифицированную 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

Ответ 6

@Пользователь0

Например, я хочу

   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 (обозначения 0 и 1 в наборе данных) входит в наборы обучения и тестирования как обозначения 0 и 1 соответственно.

В приведенном вами примере не существует более одного способа сделать групповое 2-кратное разбиение, поскольку у вас есть только 2 группы.