Scikit-learn: Stratifizierte GruppeKFold

Erstellt am 11. Apr. 2019  ·  48Kommentare  ·  Quelle: scikit-learn/scikit-learn

Beschreibung

Derzeit verfügt sklearn nicht über eine kfold-Funktion für stratifizierte Gruppen. Entweder können wir Schichtung verwenden oder wir können die Gruppe kfold verwenden. Es wäre jedoch gut, beides zu haben.

Ich würde es gerne implementieren, wenn wir uns dafür entscheiden.

Hilfreichster Kommentar

Es wäre gut, wenn Interessierte ihren Anwendungsfall beschreiben könnten und was sie wirklich davon erwarten.

Sehr häufiger Anwendungsfall in Medizin und Biologie bei wiederholten Messungen.
Ein Beispiel: Angenommen, Sie möchten eine Krankheit klassifizieren, z. B. Alzheimer-Krankheit (AD) vs. gesunde Kontrollen anhand von MR-Bildern. Für dasselbe Subjekt haben Sie möglicherweise mehrere Scans (von Nachsorgesitzungen oder Längsschnittdaten). Nehmen wir an, Sie haben insgesamt 1000 Probanden, von denen 200 mit AD (unausgeglichene Klassen) diagnostiziert wurden. Die meisten Motive haben einen Scan, aber für einige sind 2 oder 3 Bilder verfügbar. Beim Trainieren/Testen des Klassifikators möchten Sie sicherstellen, dass sich Bilder desselben Motivs immer in derselben Falte befinden, um Datenlecks zu vermeiden.
Verwenden Sie hierfür am besten StratifiedGroupKFold: Stratifizieren Sie, um Klassenungleichgewichte zu berücksichtigen, aber mit der Gruppenbeschränkung, dass ein Subjekt nicht in verschiedenen Folds erscheinen darf.
NB: Es wäre schön, es wiederholbar zu machen.

Unten eine Beispielimplementierung, inspiriert von kaggle-kernel .

import numpy as np
from collections import Counter, defaultdict
from sklearn.utils import check_random_state

class RepeatedStratifiedGroupKFold():

    def __init__(self, n_splits=5, n_repeats=1, random_state=None):
        self.n_splits = n_splits
        self.n_repeats = n_repeats
        self.random_state = random_state

    # Implementation based on this kaggle kernel:
    #    https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation
    def split(self, X, y=None, groups=None):
        k = self.n_splits
        def eval_y_counts_per_fold(y_counts, fold):
            y_counts_per_fold[fold] += y_counts
            std_per_label = []
            for label in range(labels_num):
                label_std = np.std(
                    [y_counts_per_fold[i][label] / y_distr[label] for i in range(k)]
                )
                std_per_label.append(label_std)
            y_counts_per_fold[fold] -= y_counts
            return np.mean(std_per_label)

        rnd = check_random_state(self.random_state)
        for repeat in range(self.n_repeats):
            labels_num = np.max(y) + 1
            y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
            y_distr = Counter()
            for label, g in zip(y, groups):
                y_counts_per_group[g][label] += 1
                y_distr[label] += 1

            y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
            groups_per_fold = defaultdict(set)

            groups_and_y_counts = list(y_counts_per_group.items())
            rnd.shuffle(groups_and_y_counts)

            for g, y_counts in sorted(groups_and_y_counts, key=lambda x: -np.std(x[1])):
                best_fold = None
                min_eval = None
                for i in range(k):
                    fold_eval = eval_y_counts_per_fold(y_counts, i)
                    if min_eval is None or fold_eval < min_eval:
                        min_eval = fold_eval
                        best_fold = i
                y_counts_per_fold[best_fold] += y_counts
                groups_per_fold[best_fold].add(g)

            all_groups = set(groups)
            for i in range(k):
                train_groups = all_groups - groups_per_fold[i]
                test_groups = groups_per_fold[i]

                train_indices = [i for i, g in enumerate(groups) if g in train_groups]
                test_indices = [i for i, g in enumerate(groups) if g in test_groups]

                yield train_indices, test_indices

Vergleich RepeatedStratifiedKFold (Beispiel derselben Gruppe kann in beiden Falten erscheinen) mit RepeatedStratifiedGroupKFold :

import matplotlib.pyplot as plt
from sklearn import model_selection

def plot_cv_indices(cv, X, y, group, ax, n_splits, lw=10):
    for ii, (tr, tt) in enumerate(cv.split(X=X, y=y, groups=group)):
        indices = np.array([np.nan] * len(X))
        indices[tt] = 1
        indices[tr] = 0

        ax.scatter(range(len(indices)), [ii + .5] * len(indices),
                   c=indices, marker='_', lw=lw, cmap=plt.cm.coolwarm,
                   vmin=-.2, vmax=1.2)

    ax.scatter(range(len(X)), [ii + 1.5] * len(X), c=y, marker='_',
               lw=lw, cmap=plt.cm.Paired)
    ax.scatter(range(len(X)), [ii + 2.5] * len(X), c=group, marker='_',
               lw=lw, cmap=plt.cm.tab20c)

    yticklabels = list(range(n_splits)) + ['class', 'group']
    ax.set(yticks=np.arange(n_splits+2) + .5, yticklabels=yticklabels,
           xlabel='Sample index', ylabel="CV iteration",
           ylim=[n_splits+2.2, -.2], xlim=[0, 100])
    ax.set_title('{}'.format(type(cv).__name__), fontsize=15)


# demonstration
np.random.seed(1338)
n_splits = 4
n_repeats=5


# Generate the class/group data
n_points = 100
X = np.random.randn(100, 10)

percentiles_classes = [.4, .6]
y = np.hstack([[ii] * int(100 * perc) for ii, perc in enumerate(percentiles_classes)])

# Evenly spaced groups
g = np.hstack([[ii] * 5 for ii in range(20)])


fig, ax = plt.subplots(1,2, figsize=(14,4))

cv_nogrp = model_selection.RepeatedStratifiedKFold(n_splits=n_splits,
                                                   n_repeats=n_repeats,
                                                   random_state=1338)
cv_grp = RepeatedStratifiedGroupKFold(n_splits=n_splits,
                                      n_repeats=n_repeats,
                                      random_state=1338)

plot_cv_indices(cv_nogrp, X, y, g, ax[0], n_splits * n_repeats)
plot_cv_indices(cv_grp, X, y, g, ax[1], n_splits * n_repeats)

plt.show()

RepeatedStratifiedGroupKFold_demo

Alle 48 Kommentare

@TomDLT @NicolasHug Was denkst du?

Vielleicht interessant in der Theorie, aber ich bin mir nicht sicher, wie nützlich es in der Praxis wäre. Wir können das Problem sicherlich offen halten und sehen, wie viele Leute diese Funktion anfordern

Gehen Sie davon aus, dass jede Gruppe in einer einzigen Klasse ist?

Siehe auch #9413

@jnothman Ja, ich hatte etwas Ähnliches im Sinn. Ich sehe jedoch, dass der Pull-Request noch offen ist. Ich meinte, dass eine Gruppe nicht über Falten wiederholt wird. Wenn wir ID als Gruppen haben, wird dieselbe ID nicht über mehrere Folds hinweg auftreten

Ich verstehe, dass dies für die Verwendung von RFECV relevant ist.
Derzeit wird standardmäßig ein StratifiedKFold-Lebenslauf verwendet. Seine fit() nimmt auch groups=
Allerdings: Es scheint, dass Gruppen bei der Ausführung von fit() nicht berücksichtigt werden. Keine Warnung (kann als Fehler angesehen werden).

Gruppierung UND Schichtung sind nützlich für ziemlich unausgeglichene Datensätze mit Abhängigkeiten zwischen Datensätzen
(In meinem Fall hat dieselbe Person mehrere Datensätze, aber es gibt immer noch eine große Anzahl von Gruppen = Personen im Verhältnis zur Anzahl der Splits; ich stelle mir vor, dass es praktische Probleme geben würde, wenn die Anzahl der eindeutigen Gruppen in der Minderheitsklasse in die Nähe kommt die Anzahl der Splits).

Also: +1!

Dies wäre auf jeden Fall nützlich. Zum Beispiel die Arbeit mit stark unausgewogenen medizinischen Zeitreihendaten, die Patienten getrennt zu halten, aber die unausgewogene Klasse in jeder Falte (ungefähr) auszugleichen.

Ich habe auch festgestellt, dass StratifiedKFold Gruppen als Parameter verwendet, aber nicht nach ihnen gruppiert, sollte wahrscheinlich gekennzeichnet werden.

Eine weitere gute Verwendung dieser Funktion wären Finanzdaten, die normalerweise sehr unausgewogen sind. In meinem Fall habe ich einen stark unausgewogenen Datensatz mit mehreren Datensätzen für dieselbe Entität (nur zu unterschiedlichen Zeitpunkten). Wir wollen ein GroupKFold machen, um Lecks zu vermeiden, aber auch stratifizieren, da wir aufgrund des hohen Ungleichgewichts mit Gruppen mit sehr wenigen oder keinen positiven Ergebnissen enden könnten.

Siehe auch #14524 denke ich?

Ein weiterer Anwendungsfall für Stratified GroupShuffleSplit und GroupKFold sind biologische „Wiederholungsmessungen“-Designs, bei denen Sie mehrere Proben pro Proband oder einer anderen übergeordneten biologischen Einheit haben. Auch in vielen Datensätzen der realen Welt in der Biologie gibt es ein Klassenungleichgewicht. Jede Probengruppe hat die gleiche Klasse. Daher ist es wichtig, Gruppen zu schichten und zusammenzuhalten.

Beschreibung

Derzeit verfügt sklearn nicht über eine kfold-Funktion für stratifizierte Gruppen. Entweder können wir Schichtung verwenden oder wir können die Gruppe kfold verwenden. Es wäre jedoch gut, beides zu haben.

Ich würde es gerne implementieren, wenn wir uns dafür entscheiden.

Hi, ich denke, es wäre ganz nützlich für die Medizin ML. Ist es bereits implementiert?

@amueller Meinst du, wir sollten das umsetzen, da die Leute daran interessiert sind?

Ich bin auch sehr interessiert ... es wäre sehr nützlich in der Spektroskopie, wenn Sie mehrere Wiederholungsmessungen für jede Ihrer Proben haben, sie müssen während der Kreuzvalidierung wirklich in derselben Falte bleiben. Und wenn Sie mehrere unausgeglichene Klassen haben, die Sie klassifizieren möchten, möchten Sie unbedingt auch die Schichtungsfunktion verwenden. Deshalb stimme ich auch dafür! Tut mir leid, dass ich nicht gut genug bin, um an der Entwicklung teilzunehmen, aber für diejenigen, die daran teilnehmen werden, können Sie sicher sein, dass es verwendet wird :-)
Daumen hoch für das gesamte Team. Danke!

Bitte sehen Sie sich die referenzierten Probleme und PRs in diesem Thread an, da zumindest versucht wurde, an StratifiedGroupKFold zu arbeiten. Ich habe bereits ein StratifiedGroupShuffleSplit #15239 gemacht, das nur getestet werden muss, aber ich habe es schon ziemlich oft für meine eigene Arbeit verwendet.

Ich denke, wir sollten es umsetzen, aber ich glaube, ich weiß immer noch nicht, was wir eigentlich wollen. @hermidalc hat eine Einschränkung, dass Mitglieder derselben Gruppe derselben Klasse angehören müssen. Das ist nicht der allgemeine Fall, oder?

Es wäre gut, wenn Interessierte ihren Anwendungsfall beschreiben könnten und was sie wirklich davon erwarten.

Es gibt # 15239 # 14524 und # 9413, von denen ich mich erinnere, dass sie alle unterschiedliche Semantik haben.

@amueller stimme dir voll und ganz zu, ich habe heute ein paar Stunden damit verbracht, zwischen den verschiedenen verfügbaren Versionen (#15239, #14524 und #9413) nach etwas zu suchen, konnte aber nicht wirklich verstehen, ob eine davon meinen Anforderungen entspricht. Also hier ist mein Anwendungsfall, wenn es helfen kann:
Ich habe 1000 Proben. jede Probe wurde 3 Mal mit einem NIR-Spektrometer gemessen, also hat jede Probe 3 Wiederholungen, die ich den ganzen Weg zusammen bleiben möchte ...
Diese 1000 Proben gehören zu 6 verschiedenen Klassen mit jeweils sehr unterschiedlicher Anzahl von Proben:
Klasse 1: 400 Proben
Klasse 2: 300 Proben
Klasse 3: 100 Proben
Klasse 4: 100 Proben
Klasse 5: 70 Proben
Klasse 6: 30 Proben
Ich möchte für jede Klasse einen Klassifikator erstellen. Also Klasse 1 gegen alle anderen Klassen, dann Klasse 2 gegen alle anderen Klassen usw.
Um die Genauigkeit jedes meiner Klassifikatoren zu maximieren, ist es wichtig, dass ich Proben der 6 Klassen habe, die in jeder Falte vertreten sind, da meine Klassen nicht so unterschiedlich sind, daher hilft es wirklich, eine genaue Grenze zu erstellen, damit immer die 6 Klassen vertreten sind in jeder Falte.

Aus diesem Grund glaube ich, dass eine geschichtete (immer meine 6 Klassen in jeder Falte vertretene) Gruppe (immer die 3 Wiederholungsmaße jeder meiner Stichproben zusammen halten) kfold sehr genau das zu sein scheint, wonach ich hier suche.
Irgendeine Meinung?

Mein Anwendungsfall und warum ich StratifiedGroupShuffleSplit geschrieben habe, ist die Unterstützung von Designs mit wiederholten Messungen https://en.wikipedia.org/wiki/Repeated_measures_design. In meinen Anwendungsfällen müssen Mitglieder derselben Gruppe derselben Klasse angehören.

@fcoppey Für dich haben die Samples innerhalb einer Gruppe immer die gleiche Klasse, oder?

@hermidalc Ich bin mit der Terminologie nicht sehr vertraut, aber aus Wikipedia klingt es so, als ob "Design mit wiederholten Messungen" nicht bedeutet, dass dieselbe Gruppe in derselben Klasse sein muss, da es heißt: "Eine Crossover-Studie hat ein Design mit wiederholten Messungen, in dem Jeder Patient wird einer Folge von zwei oder mehr Behandlungen zugewiesen, von denen eine eine Standardbehandlung oder ein Placebo sein kann."
Wenn Sie dies auf eine ML-Einstellung beziehen, könnten Sie entweder versuchen, anhand von Messungen vorherzusagen, ob eine Person gerade eine Behandlung oder ein Placebo erhalten hat, oder Sie könnten versuchen, ein Ergebnis angesichts der Behandlung vorherzusagen.
Für beide könnte sich die Klasse für dieselbe Person ändern, richtig?

Unabhängig vom Namen klingt es für mich so, als hätten Sie beide denselben Anwendungsfall, während ich über einen ähnlichen Fall nachgedacht habe, wie er in der Crossover-Studie beschrieben wurde. Oder vielleicht etwas einfacher: Sie könnten einen Patienten im Laufe der Zeit krank werden lassen (oder sich bessern), sodass sich das Ergebnis für einen Patienten ändern könnte.

Tatsächlich heißt es in dem Wikipedia-Artikel, auf den Sie verlinken, ausdrücklich: "Längsschnittanalyse - Designs mit wiederholten Messungen ermöglichen es Forschern, zu überwachen, wie sich die Teilnehmer im Laufe der Zeit verändern, sowohl in lang- als auch in kurzfristigen Situationen." Ich denke also, dass das Ändern der Klasse enthalten ist.
Wenn es ein anderes Wort gibt, das bedeutet, dass die Messung unter den gleichen Bedingungen durchgeführt wird, könnten wir dann dieses Wort verwenden?

@amueller ja, du hast recht, mir wurde klar, dass ich oben falsch geschrieben habe, wo ich sagen wollte, dass in meinen Anwendungsfällen dieses Designs nicht in diesem Anwendungsfall im Allgemeinen.

Es kann viele ziemlich ausgefeilte Arten von Designs mit wiederholten Messungen geben, obwohl bei den beiden Arten, die ich benötigt habe, StratifiedGroupShuffleSplit die Beschränkung der gleichen Klasse innerhalb der Gruppe gilt (Längsstichprobe vor und nach der Behandlung bei der Vorhersage des Behandlungserfolgs, mehrfache Vorbehandlung). Proben pro Person an verschiedenen Körperstellen bei der Vorhersage des Ansprechens auf die Behandlung).

Ich brauchte sofort etwas, das funktioniert, also wollte ich es für andere zur Verfügung stellen und etwas mit sklearn anfangen, und wenn ich mich nicht irre, ist es komplizierter, die Stratifizierungslogik zu entwerfen, wenn innerhalb der Gruppenklassenbezeichnungen unterschiedlich sein können.

@amueller ja immer. Sie sind Replikate einer gleichen Messung, um die Intravariabilität des Geräts in die Vorhersage einzubeziehen.

@hermidalc ja, dieser Fall ist viel einfacher. Wenn es ein gemeinsames Bedürfnis ist, freue ich mich, wenn wir es hinzufügen. Wir sollten nur sicherstellen, dass aus dem Namen einigermaßen klar wird, was es tut, und wir sollten darüber nachdenken, ob diese beiden Versionen in derselben Klasse leben sollten.

Es sollte ziemlich einfach sein, StratifiedKFold dazu zu bringen. Es gibt zwei Möglichkeiten: Stellen Sie sicher, dass jede Faltung eine ähnliche Anzahl von Proben enthält, oder stellen Sie sicher, dass jede Faltung eine ähnliche Anzahl von Gruppen enthält.
Die zweite ist trivial (indem man einfach vorgibt, dass jede Gruppe ein einzelner Punkt ist und zu StratifiedKFold ). So sieht es in Ihrer PR aus.

GroupKFold Ich denke, heuristisch tauscht die beiden aus, indem sie zuerst zum kleinsten Fold addiert. Ich bin mir nicht sicher, wie sich das auf den stratifizierten Fall übertragen würde, daher bin ich mit Ihrem Ansatz zufrieden.

Sollten wir auch GroupStratifiedKFold in derselben PR hinzufügen? Oder lassen Sie das für später?
Die anderen PRs haben etwas andere Ziele. Es wäre gut, wenn jemand aufschreiben könnte, was die verschiedenen Anwendungsfälle sind (ich habe wahrscheinlich gerade keine Zeit).

+1 für die separate Behandlung der Gruppenbeschränkung, bei der alle Proben dieselbe Klasse haben.

@hermidalc ja, dieser Fall ist viel einfacher. Wenn es ein gemeinsames Bedürfnis ist, freue ich mich, wenn wir es hinzufügen. Wir sollten nur sicherstellen, dass aus dem Namen einigermaßen klar wird, was es tut, und wir sollten darüber nachdenken, ob diese beiden Versionen in derselben Klasse leben sollten.

Ich verstehe das nicht ganz, ein StratifiedGroupShuffleSplit und StratifiedGroupKFold , wo Sie Mitglieder jeder Gruppe aus verschiedenen Klassen haben können, sollten genau das gleiche Split-Verhalten haben, wenn der Benutzer alle Gruppenmitglieder angibt der gleichen Klasse. Wann kann man später einfach die Interna verbessern und bestehendes Verhalten bleibt gleich?

Die zweite ist trivial (indem man einfach vorgibt, dass jede Gruppe ein einzelner Punkt ist und zu StratifiedKFold ). So sieht es in Ihrer PR aus.

GroupKFold Ich denke, heuristisch tauscht die beiden aus, indem sie zuerst zum kleinsten Fold addiert. Ich bin mir nicht sicher, wie sich das auf den stratifizierten Fall übertragen würde, daher bin ich mit Ihrem Ansatz zufrieden.

Sollten wir auch GroupStratifiedKFold in derselben PR hinzufügen? Oder lassen Sie das für später?
Die anderen PRs haben etwas andere Ziele. Es wäre gut, wenn jemand aufschreiben könnte, was die verschiedenen Anwendungsfälle sind (ich habe wahrscheinlich gerade keine Zeit).

Ich werde StatifiedGroupKFold hinzufügen, indem ich den von mir verwendeten Ansatz "Einzelprobe für jede Gruppe" verwende.

Es wäre gut, wenn Interessierte ihren Anwendungsfall beschreiben könnten und was sie wirklich davon erwarten.

Sehr häufiger Anwendungsfall in Medizin und Biologie bei wiederholten Messungen.
Ein Beispiel: Angenommen, Sie möchten eine Krankheit klassifizieren, z. B. Alzheimer-Krankheit (AD) vs. gesunde Kontrollen anhand von MR-Bildern. Für dasselbe Subjekt haben Sie möglicherweise mehrere Scans (von Nachsorgesitzungen oder Längsschnittdaten). Nehmen wir an, Sie haben insgesamt 1000 Probanden, von denen 200 mit AD (unausgeglichene Klassen) diagnostiziert wurden. Die meisten Motive haben einen Scan, aber für einige sind 2 oder 3 Bilder verfügbar. Beim Trainieren/Testen des Klassifikators möchten Sie sicherstellen, dass sich Bilder desselben Motivs immer in derselben Falte befinden, um Datenlecks zu vermeiden.
Verwenden Sie hierfür am besten StratifiedGroupKFold: Stratifizieren Sie, um Klassenungleichgewichte zu berücksichtigen, aber mit der Gruppenbeschränkung, dass ein Subjekt nicht in verschiedenen Folds erscheinen darf.
NB: Es wäre schön, es wiederholbar zu machen.

Unten eine Beispielimplementierung, inspiriert von kaggle-kernel .

import numpy as np
from collections import Counter, defaultdict
from sklearn.utils import check_random_state

class RepeatedStratifiedGroupKFold():

    def __init__(self, n_splits=5, n_repeats=1, random_state=None):
        self.n_splits = n_splits
        self.n_repeats = n_repeats
        self.random_state = random_state

    # Implementation based on this kaggle kernel:
    #    https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation
    def split(self, X, y=None, groups=None):
        k = self.n_splits
        def eval_y_counts_per_fold(y_counts, fold):
            y_counts_per_fold[fold] += y_counts
            std_per_label = []
            for label in range(labels_num):
                label_std = np.std(
                    [y_counts_per_fold[i][label] / y_distr[label] for i in range(k)]
                )
                std_per_label.append(label_std)
            y_counts_per_fold[fold] -= y_counts
            return np.mean(std_per_label)

        rnd = check_random_state(self.random_state)
        for repeat in range(self.n_repeats):
            labels_num = np.max(y) + 1
            y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
            y_distr = Counter()
            for label, g in zip(y, groups):
                y_counts_per_group[g][label] += 1
                y_distr[label] += 1

            y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
            groups_per_fold = defaultdict(set)

            groups_and_y_counts = list(y_counts_per_group.items())
            rnd.shuffle(groups_and_y_counts)

            for g, y_counts in sorted(groups_and_y_counts, key=lambda x: -np.std(x[1])):
                best_fold = None
                min_eval = None
                for i in range(k):
                    fold_eval = eval_y_counts_per_fold(y_counts, i)
                    if min_eval is None or fold_eval < min_eval:
                        min_eval = fold_eval
                        best_fold = i
                y_counts_per_fold[best_fold] += y_counts
                groups_per_fold[best_fold].add(g)

            all_groups = set(groups)
            for i in range(k):
                train_groups = all_groups - groups_per_fold[i]
                test_groups = groups_per_fold[i]

                train_indices = [i for i, g in enumerate(groups) if g in train_groups]
                test_indices = [i for i, g in enumerate(groups) if g in test_groups]

                yield train_indices, test_indices

Vergleich RepeatedStratifiedKFold (Beispiel derselben Gruppe kann in beiden Falten erscheinen) mit RepeatedStratifiedGroupKFold :

import matplotlib.pyplot as plt
from sklearn import model_selection

def plot_cv_indices(cv, X, y, group, ax, n_splits, lw=10):
    for ii, (tr, tt) in enumerate(cv.split(X=X, y=y, groups=group)):
        indices = np.array([np.nan] * len(X))
        indices[tt] = 1
        indices[tr] = 0

        ax.scatter(range(len(indices)), [ii + .5] * len(indices),
                   c=indices, marker='_', lw=lw, cmap=plt.cm.coolwarm,
                   vmin=-.2, vmax=1.2)

    ax.scatter(range(len(X)), [ii + 1.5] * len(X), c=y, marker='_',
               lw=lw, cmap=plt.cm.Paired)
    ax.scatter(range(len(X)), [ii + 2.5] * len(X), c=group, marker='_',
               lw=lw, cmap=plt.cm.tab20c)

    yticklabels = list(range(n_splits)) + ['class', 'group']
    ax.set(yticks=np.arange(n_splits+2) + .5, yticklabels=yticklabels,
           xlabel='Sample index', ylabel="CV iteration",
           ylim=[n_splits+2.2, -.2], xlim=[0, 100])
    ax.set_title('{}'.format(type(cv).__name__), fontsize=15)


# demonstration
np.random.seed(1338)
n_splits = 4
n_repeats=5


# Generate the class/group data
n_points = 100
X = np.random.randn(100, 10)

percentiles_classes = [.4, .6]
y = np.hstack([[ii] * int(100 * perc) for ii, perc in enumerate(percentiles_classes)])

# Evenly spaced groups
g = np.hstack([[ii] * 5 for ii in range(20)])


fig, ax = plt.subplots(1,2, figsize=(14,4))

cv_nogrp = model_selection.RepeatedStratifiedKFold(n_splits=n_splits,
                                                   n_repeats=n_repeats,
                                                   random_state=1338)
cv_grp = RepeatedStratifiedGroupKFold(n_splits=n_splits,
                                      n_repeats=n_repeats,
                                      random_state=1338)

plot_cv_indices(cv_nogrp, X, y, g, ax[0], n_splits * n_repeats)
plot_cv_indices(cv_grp, X, y, g, ax[1], n_splits * n_repeats)

plt.show()

RepeatedStratifiedGroupKFold_demo

+1 für stratifiedGroupKfold. Ich versuche, Stürze von Senioren zu erkennen, indem ich Sensoren von der Samrt-Uhr nehme. Da wir nicht viele Falldaten haben, führen wir Simulationen mit verschiedenen Uhren durch, die unterschiedliche Klassen erhalten. Ich mache auch Augmentationen an den Daten, bevor ich sie trainiere. Von jedem Datenpunkt erstelle ich 9 Punkte – und das ist eine Gruppe. Es ist wichtig, dass eine Gruppe nicht wie erläutert sowohl im Training als auch im Test ist

Ich möchte auch StratifiedGroupKFold verwenden können. Ich betrachte einen Datensatz zur Vorhersage von Finanzkrisen, bei dem die Jahre vor, nach und während jeder Krise eine eigene Gruppe sind. Während des Trainings und der Kreuzvalidierung sollten die Mitglieder jeder Gruppe nicht zwischen den Falten auslaufen.

Gibt es überhaupt eine Verallgemeinerung für das Multilabel-Szenario (Multilabel_
stratifiedGroupKfold)?

+1 dafür. Wir analysieren Benutzerkonten auf Spam, daher möchten wir nach Benutzern gruppieren, aber auch schichten, da Spam relativ selten vorkommt. Für unseren Anwendungsfall wird jeder Benutzer, der einmal spammt, in allen Daten als Spammer gekennzeichnet, sodass ein Gruppenmitglied immer das gleiche Label hat.

Vielen Dank für die Bereitstellung eines klassischen Anwendungsfalls zum Rahmen der Dokumentation.
@philipp-iv!

Ich habe meiner gleichen PR #15239 eine StratifiedGroupKFold Implementierung als StratifiedGroupShuffleSplit hinzugefügt.

Wie Sie in der PR sehen können, ist die Logik für beide viel einfacher als https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -557802602, da meine nur versucht, den Prozentsatz der Gruppen für zu erhalten jede Klasse (nicht Prozentsatz der Proben), damit ich den vorhandenen StratifiedKFold - und StratifiedShuffleSplit -Code nutzen kann, indem ich ihm eindeutige Gruppeninformationen übergebe. Aber beide Implementierungen erzeugen Faltungen, bei denen die Proben jeder Gruppe zusammen in der gleichen Faltung bleiben.

Obwohl ich für ausgefeiltere Methoden basierend auf https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -557802602 stimmen würde

Hier sind vollwertige Versionen von StratifiedGroupKFold und RepeatedStratifiedGroupKFold mit dem bereitgestellten Code @mrunibe , den ich weiter vereinfacht und ein paar Dinge geändert habe. Diese Klassen folgen auch dem Design anderer sklearn-CV-Klassen des gleichen Typs.

class StratifiedGroupKFold(_BaseKFold):
    """Stratified K-Folds iterator variant with non-overlapping groups.

    This cross-validation object is a variation of StratifiedKFold that returns
    stratified folds with non-overlapping groups. The folds are made by
    preserving the percentage of samples for each class.

    The same group will not appear in two different folds (the number of
    distinct groups has to be at least equal to the number of folds).

    The difference between GroupKFold and StratifiedGroupKFold is that
    the former attempts to create balanced folds such that the number of
    distinct groups is approximately the same in each fold, whereas
    StratifiedGroupKFold attempts to create folds which preserve the
    percentage of samples for each class.

    Read more in the :ref:`User Guide <cross_validation>`.

    Parameters
    ----------
    n_splits : int, default=5
        Number of folds. Must be at least 2.

    shuffle : bool, default=False
        Whether to shuffle each class's samples before splitting into batches.
        Note that the samples within each split will not be shuffled.

    random_state : int or RandomState instance, default=None
        When `shuffle` is True, `random_state` affects the ordering of the
        indices, which controls the randomness of each fold for each class.
        Otherwise, leave `random_state` as `None`.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.

    Examples
    --------
    >>> import numpy as np
    >>> from sklearn.model_selection import StratifiedGroupKFold
    >>> X = np.ones((17, 2))
    >>> y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    >>> groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8])
    >>> cv = StratifiedGroupKFold(n_splits=3)
    >>> for train_idxs, test_idxs in cv.split(X, y, groups):
    ...     print("TRAIN:", groups[train_idxs])
    ...     print("      ", y[train_idxs])
    ...     print(" TEST:", groups[test_idxs])
    ...     print("      ", y[test_idxs])
    TRAIN: [2 2 4 5 5 5 5 6 6 7]
           [1 1 1 0 0 0 0 0 0 0]
     TEST: [1 1 3 3 3 8 8]
           [0 0 1 1 1 0 0]
    TRAIN: [1 1 3 3 3 4 5 5 5 5 8 8]
           [0 0 1 1 1 1 0 0 0 0 0 0]
     TEST: [2 2 6 6 7]
           [1 1 0 0 0]
    TRAIN: [1 1 2 2 3 3 3 6 6 7 8 8]
           [0 0 1 1 1 1 1 0 0 0 0 0]
     TEST: [4 5 5 5 5]
           [1 0 0 0 0]

    See also
    --------
    StratifiedKFold: Takes class information into account to build folds which
        retain class distributions (for binary or multiclass classification
        tasks).

    GroupKFold: K-fold iterator variant with non-overlapping groups.
    """

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

    # Implementation based on this kaggle kernel:
    # https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation
    def _iter_test_indices(self, X, y, groups):
        labels_num = np.max(y) + 1
        y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
        y_distr = Counter()
        for label, group in zip(y, groups):
            y_counts_per_group[group][label] += 1
            y_distr[label] += 1

        y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
        groups_per_fold = defaultdict(set)

        groups_and_y_counts = list(y_counts_per_group.items())
        rng = check_random_state(self.random_state)
        if self.shuffle:
            rng.shuffle(groups_and_y_counts)

        for group, y_counts in sorted(groups_and_y_counts,
                                      key=lambda x: -np.std(x[1])):
            best_fold = None
            min_eval = None
            for i in range(self.n_splits):
                y_counts_per_fold[i] += y_counts
                std_per_label = []
                for label in range(labels_num):
                    std_per_label.append(np.std(
                        [y_counts_per_fold[j][label] / y_distr[label]
                         for j in range(self.n_splits)]))
                y_counts_per_fold[i] -= y_counts
                fold_eval = np.mean(std_per_label)
                if min_eval is None or fold_eval < min_eval:
                    min_eval = fold_eval
                    best_fold = i
            y_counts_per_fold[best_fold] += y_counts
            groups_per_fold[best_fold].add(group)

        for i in range(self.n_splits):
            test_indices = [idx for idx, group in enumerate(groups)
                            if group in groups_per_fold[i]]
            yield test_indices


class RepeatedStratifiedGroupKFold(_RepeatedSplits):
    """Repeated Stratified K-Fold cross validator.

    Repeats Stratified K-Fold with non-overlapping groups n times with
    different randomization in each repetition.

    Read more in the :ref:`User Guide <cross_validation>`.

    Parameters
    ----------
    n_splits : int, default=5
        Number of folds. Must be at least 2.

    n_repeats : int, default=10
        Number of times cross-validator needs to be repeated.

    random_state : int or RandomState instance, default=None
        Controls the generation of the random states for each repetition.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.

    Examples
    --------
    >>> import numpy as np
    >>> from sklearn.model_selection import RepeatedStratifiedGroupKFold
    >>> X = np.ones((17, 2))
    >>> y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    >>> groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8])
    >>> cv = RepeatedStratifiedGroupKFold(n_splits=2, n_repeats=2,
    ...                                   random_state=36851234)
    >>> for train_index, test_index in cv.split(X, y, groups):
    ...     print("TRAIN:", groups[train_idxs])
    ...     print("      ", y[train_idxs])
    ...     print(" TEST:", groups[test_idxs])
    ...     print("      ", y[test_idxs])
    TRAIN: [2 2 4 5 5 5 5 8 8]
           [1 1 1 0 0 0 0 0 0]
     TEST: [1 1 3 3 3 6 6 7]
           [0 0 1 1 1 0 0 0]
    TRAIN: [1 1 3 3 3 6 6 7]
           [0 0 1 1 1 0 0 0]
     TEST: [2 2 4 5 5 5 5 8 8]
           [1 1 1 0 0 0 0 0 0]
    TRAIN: [3 3 3 4 7 8 8]
           [1 1 1 1 0 0 0]
     TEST: [1 1 2 2 5 5 5 5 6 6]
           [0 0 1 1 0 0 0 0 0 0]
    TRAIN: [1 1 2 2 5 5 5 5 6 6]
           [0 0 1 1 0 0 0 0 0 0]
     TEST: [3 3 3 4 7 8 8]
           [1 1 1 1 0 0 0]

    Notes
    -----
    Randomized CV splitters may return different results for each call of
    split. You can make the results identical by setting `random_state`
    to an integer.

    See also
    --------
    RepeatedStratifiedKFold: Repeats Stratified K-Fold n times.
    """

    def __init__(self, n_splits=5, n_repeats=10, random_state=None):
        super().__init__(StratifiedGroupKFold, n_splits=n_splits,
                         n_repeats=n_repeats, random_state=random_state)

@hermidalc Ich bin ziemlich verwirrt darüber, was wir gelöst haben, wenn ich von Zeit zu Zeit darauf zurückblicke. (Leider ist meine Zeit nicht mehr so ​​wie früher!) Können Sie mir eine Vorstellung davon geben, was Sie für scikit-learn empfehlen würden?

@hermidalc Ich bin ziemlich verwirrt darüber, was wir gelöst haben, wenn ich von Zeit zu Zeit darauf zurückblicke. (Leider ist meine Zeit nicht mehr so ​​wie früher!) Können Sie mir eine Vorstellung davon geben, was Sie für scikit-learn empfehlen würden?

Ich wollte eine bessere Implementierung machen als in #15239. Die Implementierung in dieser PR funktioniert, schichtet jedoch die Gruppen auf, um die Logik einfach zu machen, obwohl dies nicht ideal ist.

Was ich oben gemacht habe (dank @mrunibe und kaggle von jakubwasikowski) ist eine bessere Implementierung von StratifiedGroupKFold , die auf den Samples geschichtet ist. Ich möchte dieselbe Logik portieren, um ein besseres StratifiedGroupShuffleSplit zu machen, und dann wird es fertig sein. Ich werde den neuen Code in #15239 einfügen, um die ältere Implementierung zu ersetzen.

Ich entschuldige mich für meine unvollendeten PRs, ich mache meinen PhD, also habe ich nie Zeit!

Vielen Dank an @hermidalc und @mrunibe für die Bereitstellung der Implementierung. Ich habe auch nach einer StratifiedGroupKFold Methode für den Umgang mit medizinischen Daten gesucht, die ein starkes Klassenungleichgewicht und eine stark unterschiedliche Anzahl von Proben pro Subjekt aufweisen. GroupKFold selbst erstellt Untergruppen von Trainingsdaten, die nur eine Klasse enthalten.

Ich möchte dieselbe Logik portieren, um einen besseren StratifiedGroupShuffleSplit zu machen, und dann ist es fertig.

Wir könnten sicherlich erwägen, StratifiedGroupKFold zusammenzuführen, bevor StratifiedGroupShuffleSplit fertig ist.

Ich entschuldige mich für meine unvollendeten PRs, ich mache meinen PhD, also habe ich nie Zeit!

Lassen Sie uns wissen, wenn Sie Unterstützung bei der Fertigstellung wünschen!

Und viel Erfolg bei Ihrer Doktorarbeit

Hier sind vollwertige Versionen von StratifiedGroupKFold und RepeatedStratifiedGroupKFold mit dem bereitgestellten Code @mrunibe , den ich weiter vereinfacht und ein paar Dinge geändert habe. Diese Klassen folgen auch dem Design anderer sklearn-CV-Klassen des gleichen Typs.

Kann man das ausprobieren? Ich habe versucht, einige der verschiedenen Abhängigkeiten auszuschneiden und einzufügen, aber es endete nie. Ich würde diese Klasse gerne in meinem Projekt ausprobieren. Ich versuche nur zu sehen, ob es jetzt eine Möglichkeit gibt, dies zu tun.

@hermidalc Ich hoffe, Ihre Doktorarbeit war erfolgreich!
Ich freue mich darauf, diese Implementierung ebenfalls zu sehen, da meine Doktorarbeit in den Geowissenschaften diese Schichtungsfunktion mit Gruppensteuerung benötigt. Ich habe einige Stunden damit verbracht, diese Idee der manuellen Aufteilung in meinem Projekt umzusetzen. Aber ich habe aus dem gleichen Grund aufgegeben, es fertig zu machen ... Fortschritte bei der Promotion. Ich kann also vollkommen verstehen, wie die Doktorarbeit die Zeit einer Person quälen kann. LOL Kein Druck. Als Alternative verwende ich vorerst GroupShuffleSplit.

Beifall

@bfeeny @dispink Es ist sehr einfach, die beiden Klassen zu verwenden, die ich oben geschrieben habe. Erstellen Sie eine Datei zB split.py mit dem Folgenden. Wenn sich das Skript dann in Ihrem Benutzercode im selben Verzeichnis wie split.py befindet, importieren Sie einfach from split import StratifiedGroupKFold, RepeatedStratifiedGroupKFold

from collections import Counter, defaultdict

import numpy as np

from sklearn.model_selection._split import _BaseKFold, _RepeatedSplits
from sklearn.utils.validation import check_random_state


class StratifiedGroupKFold(_BaseKFold):
    """Stratified K-Folds iterator variant with non-overlapping groups.

    This cross-validation object is a variation of StratifiedKFold that returns
    stratified folds with non-overlapping groups. The folds are made by
    preserving the percentage of samples for each class.

    The same group will not appear in two different folds (the number of
    distinct groups has to be at least equal to the number of folds).

    The difference between GroupKFold and StratifiedGroupKFold is that
    the former attempts to create balanced folds such that the number of
    distinct groups is approximately the same in each fold, whereas
    StratifiedGroupKFold attempts to create folds which preserve the
    percentage of samples for each class.

    Read more in the :ref:`User Guide <cross_validation>`.

    Parameters
    ----------
    n_splits : int, default=5
        Number of folds. Must be at least 2.

    shuffle : bool, default=False
        Whether to shuffle each class's samples before splitting into batches.
        Note that the samples within each split will not be shuffled.

    random_state : int or RandomState instance, default=None
        When `shuffle` is True, `random_state` affects the ordering of the
        indices, which controls the randomness of each fold for each class.
        Otherwise, leave `random_state` as `None`.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.

    Examples
    --------
    >>> import numpy as np
    >>> from sklearn.model_selection import StratifiedGroupKFold
    >>> X = np.ones((17, 2))
    >>> y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    >>> groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8])
    >>> cv = StratifiedGroupKFold(n_splits=3)
    >>> for train_idxs, test_idxs in cv.split(X, y, groups):
    ...     print("TRAIN:", groups[train_idxs])
    ...     print("      ", y[train_idxs])
    ...     print(" TEST:", groups[test_idxs])
    ...     print("      ", y[test_idxs])
    TRAIN: [2 2 4 5 5 5 5 6 6 7]
           [1 1 1 0 0 0 0 0 0 0]
     TEST: [1 1 3 3 3 8 8]
           [0 0 1 1 1 0 0]
    TRAIN: [1 1 3 3 3 4 5 5 5 5 8 8]
           [0 0 1 1 1 1 0 0 0 0 0 0]
     TEST: [2 2 6 6 7]
           [1 1 0 0 0]
    TRAIN: [1 1 2 2 3 3 3 6 6 7 8 8]
           [0 0 1 1 1 1 1 0 0 0 0 0]
     TEST: [4 5 5 5 5]
           [1 0 0 0 0]

    See also
    --------
    StratifiedKFold: Takes class information into account to build folds which
        retain class distributions (for binary or multiclass classification
        tasks).

    GroupKFold: K-fold iterator variant with non-overlapping groups.
    """

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

    # Implementation based on this kaggle kernel:
    # https://www.kaggle.com/jakubwasikowski/stratified-group-k-fold-cross-validation
    def _iter_test_indices(self, X, y, groups):
        labels_num = np.max(y) + 1
        y_counts_per_group = defaultdict(lambda: np.zeros(labels_num))
        y_distr = Counter()
        for label, group in zip(y, groups):
            y_counts_per_group[group][label] += 1
            y_distr[label] += 1

        y_counts_per_fold = defaultdict(lambda: np.zeros(labels_num))
        groups_per_fold = defaultdict(set)

        groups_and_y_counts = list(y_counts_per_group.items())
        rng = check_random_state(self.random_state)
        if self.shuffle:
            rng.shuffle(groups_and_y_counts)

        for group, y_counts in sorted(groups_and_y_counts,
                                      key=lambda x: -np.std(x[1])):
            best_fold = None
            min_eval = None
            for i in range(self.n_splits):
                y_counts_per_fold[i] += y_counts
                std_per_label = []
                for label in range(labels_num):
                    std_per_label.append(np.std(
                        [y_counts_per_fold[j][label] / y_distr[label]
                         for j in range(self.n_splits)]))
                y_counts_per_fold[i] -= y_counts
                fold_eval = np.mean(std_per_label)
                if min_eval is None or fold_eval < min_eval:
                    min_eval = fold_eval
                    best_fold = i
            y_counts_per_fold[best_fold] += y_counts
            groups_per_fold[best_fold].add(group)

        for i in range(self.n_splits):
            test_indices = [idx for idx, group in enumerate(groups)
                            if group in groups_per_fold[i]]
            yield test_indices


class RepeatedStratifiedGroupKFold(_RepeatedSplits):
    """Repeated Stratified K-Fold cross validator.

    Repeats Stratified K-Fold with non-overlapping groups n times with
    different randomization in each repetition.

    Read more in the :ref:`User Guide <cross_validation>`.

    Parameters
    ----------
    n_splits : int, default=5
        Number of folds. Must be at least 2.

    n_repeats : int, default=10
        Number of times cross-validator needs to be repeated.

    random_state : int or RandomState instance, default=None
        Controls the generation of the random states for each repetition.
        Pass an int for reproducible output across multiple function calls.
        See :term:`Glossary <random_state>`.

    Examples
    --------
    >>> import numpy as np
    >>> from sklearn.model_selection import RepeatedStratifiedGroupKFold
    >>> X = np.ones((17, 2))
    >>> y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0])
    >>> groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8])
    >>> cv = RepeatedStratifiedGroupKFold(n_splits=2, n_repeats=2,
    ...                                   random_state=36851234)
    >>> for train_index, test_index in cv.split(X, y, groups):
    ...     print("TRAIN:", groups[train_idxs])
    ...     print("      ", y[train_idxs])
    ...     print(" TEST:", groups[test_idxs])
    ...     print("      ", y[test_idxs])
    TRAIN: [2 2 4 5 5 5 5 8 8]
           [1 1 1 0 0 0 0 0 0]
     TEST: [1 1 3 3 3 6 6 7]
           [0 0 1 1 1 0 0 0]
    TRAIN: [1 1 3 3 3 6 6 7]
           [0 0 1 1 1 0 0 0]
     TEST: [2 2 4 5 5 5 5 8 8]
           [1 1 1 0 0 0 0 0 0]
    TRAIN: [3 3 3 4 7 8 8]
           [1 1 1 1 0 0 0]
     TEST: [1 1 2 2 5 5 5 5 6 6]
           [0 0 1 1 0 0 0 0 0 0]
    TRAIN: [1 1 2 2 5 5 5 5 6 6]
           [0 0 1 1 0 0 0 0 0 0]
     TEST: [3 3 3 4 7 8 8]
           [1 1 1 1 0 0 0]

    Notes
    -----
    Randomized CV splitters may return different results for each call of
    split. You can make the results identical by setting `random_state`
    to an integer.

    See also
    --------
    RepeatedStratifiedKFold: Repeats Stratified K-Fold n times.
    """

    def __init__(self, n_splits=5, n_repeats=10, random_state=None):
        super().__init__(StratifiedGroupKFold, n_splits=n_splits,
                         n_repeats=n_repeats, random_state=random_state)

@hermidalc Vielen Dank für die positive Antwort!
Ich nehme es schnell an, wie du es beschrieben hast. Ich kann jedoch nur die Splits abrufen, die nur Daten im Trainings- oder Testsatz enthalten. Soweit ich die Codebeschreibung verstehe, gibt es keinen Parameter, um das Verhältnis zwischen Trainings- und Testsätzen anzugeben, oder?
Ich weiß, es ist ein Konflikt zwischen Schichtung, Gruppenkontrolle und Datensatzanteil ... Deshalb habe ich es aufgegeben, weiterzumachen ... Aber vielleicht können wir immer noch Kompromisse finden, um dies zu umgehen.
image

Aufrichtig

@hermidalc Vielen Dank für die positive Antwort!
Ich nehme es schnell an, wie du es beschrieben hast. Ich kann jedoch nur die Splits abrufen, die nur Daten im Trainings- oder Testsatz enthalten. Soweit ich die Codebeschreibung verstehe, gibt es keinen Parameter, um das Verhältnis zwischen Trainings- und Testsätzen anzugeben, oder?
Ich weiß, es ist ein Konflikt zwischen Schichtung, Gruppenkontrolle und Datensatzanteil ... Deshalb habe ich es aufgegeben, weiterzumachen ... Aber vielleicht können wir immer noch Kompromisse finden, um dies zu umgehen.

Zum Testen habe ich split.py gemacht und dieses Beispiel in ipython ausgeführt und es funktioniert. Ich verwende diese benutzerdefinierten CV-Iteratoren seit langem in meiner Arbeit und sie haben keine Probleme auf meiner Seite. Übrigens verwende ich scikit-learn 0.22.2 und nicht 0.23.x, also bin ich mir nicht sicher, ob dies die Ursache des Problems ist. Könnten Sie bitte versuchen, dieses Beispiel unten auszuführen und zu sehen, ob Sie es reproduzieren können? Wenn Sie können, dann könnte es etwas mit den y und groups in Ihrer Arbeit sein.

In [6]: import numpy as np 
   ...: from split import StratifiedGroupKFold 
   ...:  
   ...: X = np.ones((17, 2)) 
   ...: y = np.array([0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]) 
   ...: groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8]) 
   ...: cv = StratifiedGroupKFold(n_splits=3, shuffle=True, random_state=777) 
   ...: for train_idxs, test_idxs in cv.split(X, y, groups): 
   ...:     print("TRAIN:", groups[train_idxs]) 
   ...:     print("      ", y[train_idxs]) 
   ...:     print(" TEST:", groups[test_idxs]) 
   ...:     print("      ", y[test_idxs]) 
   ...:                                                                                                                                                                                                    
TRAIN: [2 2 4 5 5 5 5 6 6 7]
       [1 1 1 0 0 0 0 0 0 0]
 TEST: [1 1 3 3 3 8 8]
       [0 0 1 1 1 0 0]
TRAIN: [1 1 3 3 3 4 5 5 5 5 8 8]
       [0 0 1 1 1 1 0 0 0 0 0 0]
 TEST: [2 2 6 6 7]
       [1 1 0 0 0]
TRAIN: [1 1 2 2 3 3 3 6 6 7 8 8]
       [0 0 1 1 1 1 1 0 0 0 0 0]
 TEST: [4 5 5 5 5]
       [1 0 0 0 0]

Es scheint regelmäßiges Interesse an dieser Funktion zu geben, @hermidalc und wir
könnte wahrscheinlich jemanden finden, der es erledigt, wenn es Ihnen nichts ausmacht.

@hermidalc 'Sie müssen sicherstellen, dass jede Probe in derselben Gruppe dieselbe Klassenbezeichnung hat.' Offensichtlich ist das das Problem. Meine Proben in derselben Gruppe haben nicht dieselbe Klasse. Mmm... es scheint ein anderer Entwicklungszweig zu sein.
Vielen Dank trotzdem.

@hermidalc 'Sie müssen sicherstellen, dass jede Probe in derselben Gruppe dieselbe Klassenbezeichnung hat.' Offensichtlich ist das das Problem. Meine Proben in derselben Gruppe haben nicht dieselbe Klasse. Mmm... es scheint ein anderer Entwicklungszweig zu sein.
Vielen Dank trotzdem.

Ja, das wurde hier in verschiedenen Threads diskutiert. Es ist ein weiterer komplexerer Anwendungsfall, der nützlich ist, aber viele wie ich brauchen diesen Anwendungsfall derzeit nicht, sondern brauchen etwas, das Gruppen zusammenhält, aber die Proben schichtet. Die Anforderung des obigen Codes ist, dass alle Proben in jeder Gruppe zur selben Klasse gehören.

Eigentlich @dispink Ich habe mich geirrt, dieser Algorithmus erfordert nicht, dass alle Mitglieder einer Gruppe derselben Klasse angehören. Beispielsweise:

In [2]: X = np.ones((17, 2)) 
   ...: y =      np.array([0, 2, 1, 1, 2, 0, 0, 1, 2, 1, 1, 1, 0, 2, 0, 1, 0]) 
   ...: groups = np.array([1, 1, 2, 2, 3, 3, 3, 4, 5, 5, 5, 5, 6, 6, 7, 8, 8]) 
   ...: cv = StratifiedGroupKFold(n_splits=3) 
   ...: for train_idxs, test_idxs in cv.split(X, y, groups): 
   ...:     print("TRAIN:", groups[train_idxs]) 
   ...:     print("      ", y[train_idxs]) 
   ...:     print(" TEST:", groups[test_idxs]) 
   ...:     print("      ", y[test_idxs]) 
   ...:                                                                                                                                                                                                    
TRAIN: [1 1 2 2 3 3 3 4 8 8]
       [0 2 1 1 2 0 0 1 1 0]
 TEST: [5 5 5 5 6 6 7]
       [2 1 1 1 0 2 0]
TRAIN: [1 1 4 5 5 5 5 6 6 7 8 8]
       [0 2 1 2 1 1 1 0 2 0 1 0]
 TEST: [2 2 3 3 3]
       [1 1 2 0 0]
TRAIN: [2 2 3 3 3 5 5 5 5 6 6 7]
       [1 1 2 0 0 2 1 1 1 0 2 0]
 TEST: [1 1 4 8 8]
       [0 2 1 1 0]

Ich bin mir also nicht ganz sicher, was mit Ihren Daten los ist, da Sie selbst mit Ihren Screenshots nicht wirklich sehen können, wie Ihr Datenlayout ist und was passieren könnte. Ich würde vorschlagen, dass Sie zuerst die Beispiele reproduzieren, die ich hier gezeigt habe, um sicherzustellen, dass es sich nicht um ein Problem mit der scikit-learn-Version handelt (da ich 0.22.2 verwende), und wenn Sie es reproduzieren können, würde ich vorschlagen, dass Sie mit kleinen Teilen Ihrer beginnen Daten und teste es. Die Verwendung von ca. 104.000 Samples ist schwierig zu beheben.

@hermidalc Danke für die Antwort!
Ich kann das obige Ergebnis tatsächlich reproduzieren, daher suche ich jetzt mit kleineren Daten nach Fehlern.

+1

Hat jemand etwas dagegen, wenn ich dieses Problem aufgreife?
Scheint, dass #15239 zusammen mit https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -600894432 bereits eine Implementierung haben und nur noch Unit-Tests durchgeführt werden müssen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen