Scikit-learn: Grup BertingkatKFold

Dibuat pada 11 Apr 2019  ·  48Komentar  ·  Sumber: scikit-learn/scikit-learn

Keterangan

Saat ini sklearn tidak memiliki fitur kfold grup bertingkat. Entah kita bisa menggunakan stratifikasi atau kita bisa menggunakan grup kfold. Namun, akan lebih baik untuk memiliki keduanya.

Saya ingin menerapkannya, jika kita memutuskan untuk memilikinya.

Komentar yang paling membantu

Akan lebih baik jika orang yang tertarik dapat menjelaskan kasus penggunaan mereka dan apa yang sebenarnya mereka inginkan dari ini.

Kasus penggunaan yang sangat umum dalam kedokteran dan biologi ketika Anda melakukan tindakan berulang.
Contoh: Asumsikan Anda ingin mengklasifikasikan suatu penyakit, misalnya penyakit Alzheimer (AD) vs. kontrol sehat dari gambar MR. Untuk subjek yang sama, Anda mungkin memiliki beberapa pemindaian (dari sesi tindak lanjut atau data longitudinal). Mari kita asumsikan Anda memiliki total 1000 mata pelajaran, 200 di antaranya didiagnosis dengan AD (kelas tidak seimbang). Sebagian besar subjek memiliki satu pemindaian, tetapi untuk beberapa di antaranya tersedia 2 atau 3 gambar. Saat melatih/menguji pengklasifikasi, Anda ingin memastikan bahwa gambar dari subjek yang sama selalu berada di lipatan yang sama untuk menghindari kebocoran data.
Yang terbaik adalah menggunakan StratifiedGroupKFold untuk ini: stratifikasi untuk memperhitungkan ketidakseimbangan kelas tetapi dengan batasan grup bahwa subjek tidak boleh muncul di lipatan yang berbeda.
NB: Akan lebih baik untuk membuatnya berulang.

Di bawah contoh implementasi, terinspirasi oleh 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

Membandingkan RepeatedStratifiedKFold (contoh dari grup yang sama mungkin muncul di kedua lipatan) dengan 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

Semua 48 komentar

@TomDLT @NicolasHug Bagaimana menurut Anda?

Mungkin menarik secara teori, tapi saya tidak yakin seberapa berguna dalam praktiknya. Kami pasti dapat menjaga masalah ini tetap terbuka dan melihat berapa banyak orang yang meminta fitur ini

Apakah Anda berasumsi bahwa setiap kelompok berada dalam satu kelas?

Lihat juga #9413

@jnothman Ya, saya memiliki hal serupa dalam pikiran. Namun, saya melihat bahwa permintaan tarik masih terbuka. Maksud saya bahwa grup tidak akan diulang di lipatan. Jika kami memiliki ID sebagai grup maka ID yang sama tidak akan muncul di beberapa lipatan

Saya mengerti ini relevan dengan penggunaan RFECV.
Saat ini defaultnya adalah menggunakan cv StratifiedKFold. Fit() juga membutuhkan groups=
Namun: tampaknya grup tidak dihormati saat menjalankan fit(). Tidak ada peringatan (mungkin dianggap sebagai bug).

Pengelompokan DAN stratifikasi berguna untuk kumpulan data yang cukup tidak seimbang dengan ketergantungan antar-rekaman
(dalam kasus saya, individu yang sama memiliki banyak catatan, tetapi masih ada banyak grup=orang relatif terhadap jumlah pemisahan; Saya membayangkan akan ada masalah praktis karena jumlah grup unik di kelas minoritas mendekati jumlah pecahan).

Jadi: +1!

Ini pasti akan berguna. Misalnya, bekerja dengan data medis deret waktu yang sangat tidak seimbang, memisahkan pasien tetapi (kurang-lebih) menyeimbangkan kelas yang tidak seimbang di setiap lipatan.

Saya juga menemukan bahwa StratifiedKFold mengambil grup sebagai parameter tetapi tidak mengelompokkan menurut mereka, mungkin harus ditandai.

Penggunaan lain yang baik dari fitur ini adalah data keuangan, yang biasanya sangat tidak seimbang. Dalam kasus saya, saya memiliki kumpulan data yang sangat tidak seimbang dengan beberapa catatan untuk entitas yang sama (hanya titik waktu yang berbeda). Kami ingin melakukan GroupKFold untuk menghindari kebocoran, tetapi juga membuat stratifikasi karena karena ketidakseimbangan yang tinggi, kami dapat berakhir dengan grup dengan sangat sedikit atau tidak sama sekali positif.

juga lihat #14524 saya pikir?

Kasus penggunaan lain untuk Stratified GroupShuffleSplit dan GroupKFold adalah desain "pengukuran berulang" biologis, di mana Anda memiliki beberapa sampel per subjek atau unit biologis induk lainnya. Juga di banyak kumpulan data dunia nyata dalam biologi ada ketidakseimbangan kelas. Setiap kelompok sampel memiliki kelas yang sama. Jadi, penting untuk membuat stratifikasi dan menjaga kelompok tetap bersama.

Keterangan

Saat ini sklearn tidak memiliki fitur kfold grup bertingkat. Entah kita bisa menggunakan stratifikasi atau kita bisa menggunakan grup kfold. Namun, akan lebih baik untuk memiliki keduanya.

Saya ingin menerapkannya, jika kita memutuskan untuk memilikinya.

Hai, saya pikir itu akan sangat berguna untuk obat ML. Apakah sudah diterapkan?

@amueller Apakah menurut Anda kita harus menerapkan ini, mengingat orang-orang tertarik dengan ini?

Saya juga sangat tertarik... itu akan sangat berguna dalam spektroskopi ketika Anda memiliki beberapa ukuran ulangan untuk setiap sampel Anda, mereka benar-benar harus tetap berada di lipatan yang sama selama validasi silang. Dan jika Anda memiliki beberapa kelas tidak seimbang yang Anda coba klasifikasikan, Anda benar-benar ingin menggunakan fitur stratifikasi juga. Oleh karena itu saya memilih untuk itu juga! Maaf saya tidak cukup baik untuk berpartisipasi dalam pengembangan tetapi bagi yang akan mengambil bagian itu pasti akan digunakan :-)
acungan jempol untuk semua tim. Terima kasih!

Silakan lihat referensi masalah dan PR di utas ini karena pekerjaan setidaknya telah dicoba pada StratifiedGroupKFold . Saya sudah melakukan StratifiedGroupShuffleSplit #15239 yang hanya membutuhkan tes tetapi saya sudah cukup sering menggunakannya untuk pekerjaan saya sendiri.

Saya pikir kita harus menerapkannya, tetapi saya pikir saya masih tidak tahu apa yang sebenarnya kita inginkan. @hermidalc memiliki batasan bahwa anggota dari grup yang sama harus dari kelas yang sama. Itu bukan kasus umum, kan?

Akan lebih baik jika orang yang tertarik dapat menjelaskan kasus penggunaan mereka dan apa yang sebenarnya mereka inginkan dari ini.

Ada #15239 #14524 dan #9413 yang saya ingat semuanya memiliki semantik yang berbeda.

@amueller sangat setuju dengan Anda, saya menghabiskan beberapa jam hari ini untuk mencari sesuatu di antara berbagai versi yang tersedia (#15239 #14524 dan #9413) tetapi tidak dapat benar-benar memahami apakah semua ini sesuai dengan kebutuhan saya. Jadi inilah kasus penggunaan saya jika itu dapat membantu:
Saya punya 1000 sampel. setiap sampel telah diukur 3 kali dengan Spektrometer NIR, jadi setiap sampel memiliki 3 ulangan yang saya ingin tetap bersama sepanjang jalan...
1000 sampel ini termasuk dalam 6 kelas yang berbeda dengan jumlah sampel yang sangat berbeda di masing-masing kelas:
kelas 1: 400 sampel
kelas 2: 300 sampel
kelas 3: 100 sampel
kelas 4: 100 sampel
kelas 5: 70 sampel
kelas 6: 30 sampel
Saya ingin membangun classifier untuk setiap kelas. Jadi kelas 1 vs semua kelas lain, lalu kelas 2 vs semua kelas lain, dll.
Untuk memaksimalkan akurasi masing-masing pengklasifikasi saya, penting bahwa saya memiliki sampel dari 6 kelas yang diwakili di setiap lipatan, karena kelas saya tidak begitu berbeda oleh karena itu sangat membantu untuk membuat batas yang akurat untuk selalu mewakili 6 kelas di setiap lipatan.

Inilah sebabnya mengapa saya percaya kelompok bertingkat (Selalu 6 kelas saya terwakili di setiap lipatan) (selalu pertahankan 3 ukuran ulangan dari masing-masing sampel saya bersama-sama) kfold tampaknya sangat sesuai dengan apa yang saya cari di sini.
Ada pendapat?

Kasus penggunaan saya dan mengapa saya menulis StratifiedGroupShuffleSplit adalah untuk mendukung desain tindakan berulang https://en.wikipedia.org/wiki/Repeated_measures_design. Dalam kasus penggunaan saya, anggota dari grup yang sama harus dari kelas yang sama.

@fcoppey Bagi Anda, sampel dalam grup selalu memiliki kelas yang sama, bukan?

@hermidalc Saya tidak terlalu akrab dengan terminologi, tetapi dari wikipedia kedengarannya seperti "desain ukuran berulang" tidak berarti grup yang sama harus berada dalam kelas yang sama seperti yang dikatakan "Percobaan crossover memiliki desain pengukuran berulang di mana setiap pasien ditugaskan ke urutan dua atau lebih perawatan, yang salah satunya mungkin pengobatan standar atau plasebo."
Mengaitkan hal ini dengan pengaturan ML, Anda dapat mencoba memprediksi dari pengukuran apakah seseorang baru saja menerima pengobatan atau plasebo, atau Anda dapat mencoba memprediksi hasil yang diberikan pengobatan tersebut.
Untuk salah satu dari mereka kelas untuk individu yang sama bisa berubah, kan?

Terlepas dari namanya, bagi saya sepertinya Anda berdua memiliki kasus penggunaan yang sama, sementara saya memikirkan kasus yang mirip dengan apa yang dijelaskan dalam studi silang. Atau mungkin sedikit lebih sederhana: Anda bisa membuat pasien menjadi sakit dari waktu ke waktu (atau menjadi lebih baik), sehingga hasil untuk pasien bisa berubah.

Sebenarnya artikel wikipedia yang Anda tautkan secara eksplisit mengatakan "Analisis longitudinal—Desain pengukuran berulang memungkinkan peneliti untuk memantau bagaimana peserta berubah dari waktu ke waktu, baik situasi jangka panjang maupun jangka pendek.", jadi saya pikir itu berarti bahwa mengubah kelas disertakan.
Jika ada kata lain yang berarti pengukuran dilakukan dalam kondisi yang sama maka kita bisa menggunakan kata itu?

@amueller ya Anda benar, saya menyadari saya salah menulis di atas di mana saya bermaksud mengatakan dalam kasus penggunaan saya desain ini tidak dalam kasus penggunaan ini secara umum.

Ada banyak jenis desain tindakan berulang yang cukup rumit, meskipun dalam dua jenis saya membutuhkan StratifiedGroupShuffleSplit dalam kelompok yang sama berlaku pembatasan kelas (pengambilan sampel longitudinal sebelum dan sesudah perawatan ketika memprediksi respons pengobatan, beberapa pra-perawatan sampel per subjek di lokasi tubuh yang berbeda saat memprediksi respons pengobatan).

Saya membutuhkan sesuatu segera yang berfungsi jadi ingin meletakkannya di luar sana untuk digunakan orang lain dan untuk memulai sesuatu di sklearn, ditambah jika saya tidak salah itu lebih rumit untuk merancang logika stratifikasi ketika dalam label kelas grup bisa berbeda.

@amueller ya selalu. Mereka adalah ulangan dari ukuran yang sama untuk memasukkan intravariabilitas perangkat dalam prediksi.

@hermidalc ya, kasus ini jauh lebih mudah. Jika itu kebutuhan bersama, saya senang kita menambahkannya. Kita harus memastikan bahwa dari namanya sudah jelas apa fungsinya, dan kita harus memikirkan apakah kedua versi ini harus hidup di kelas yang sama.

Seharusnya cukup mudah untuk membuat StratifiedKFold melakukan ini. Ada dua opsi: pastikan bahwa setiap lipatan berisi jumlah sampel yang sama, atau pastikan setiap lipatan berisi jumlah grup yang serupa.
Yang kedua sepele untuk dilakukan (dengan hanya berpura-pura setiap grup adalah satu titik dan meneruskan ke StratifiedKFold ). Itulah yang Anda lakukan dalam PR Anda, sepertinya.

GroupKFold Saya pikir secara heuristik menukar keduanya dengan menambahkan lipatan terkecil terlebih dahulu. Saya tidak yakin bagaimana itu akan diterjemahkan ke dalam kasus bertingkat, jadi saya senang menggunakan pendekatan Anda.

Haruskah kita juga menambahkan GroupStratifiedKFold dalam PR yang sama? Atau biarkan itu untuk nanti?
PR lainnya memiliki tujuan yang sedikit berbeda. Akan lebih baik jika seseorang dapat menulis apa kasus penggunaan yang berbeda (saya mungkin tidak punya waktu sekarang).

+1 untuk menangani batasan grup secara terpisah di mana semua sampel memiliki kelas yang sama.

@hermidalc ya, kasus ini jauh lebih mudah. Jika itu kebutuhan bersama, saya senang kita menambahkannya. Kita harus memastikan bahwa dari namanya sudah jelas apa fungsinya, dan kita harus memikirkan apakah kedua versi ini harus hidup di kelas yang sama.

Saya tidak sepenuhnya memahami ini, StratifiedGroupShuffleSplit dan StratifiedGroupKFold di mana Anda dapat memiliki anggota setiap grup dari kelas yang berbeda harus memiliki perilaku pemisahan yang sama persis ketika pengguna menentukan semua anggota grup menjadi dari kelas yang sama. Kapan bisa memperbaiki internal saja nanti dan perilaku yang ada akan sama?

Yang kedua sepele untuk dilakukan (dengan hanya berpura-pura setiap grup adalah satu titik dan meneruskan ke StratifiedKFold ). Itulah yang Anda lakukan dalam PR Anda, sepertinya.

GroupKFold Saya pikir secara heuristik menukar keduanya dengan menambahkan lipatan terkecil terlebih dahulu. Saya tidak yakin bagaimana itu akan diterjemahkan ke dalam kasus bertingkat, jadi saya senang menggunakan pendekatan Anda.

Haruskah kita juga menambahkan GroupStratifiedKFold dalam PR yang sama? Atau biarkan itu untuk nanti?
PR lainnya memiliki tujuan yang sedikit berbeda. Akan lebih baik jika seseorang dapat menulis apa kasus penggunaan yang berbeda (saya mungkin tidak punya waktu sekarang).

Saya akan menambahkan StatifiedGroupKFold menggunakan pendekatan "setiap kelompok sampel tunggal" yang saya gunakan.

Akan lebih baik jika orang yang tertarik dapat menjelaskan kasus penggunaan mereka dan apa yang sebenarnya mereka inginkan dari ini.

Kasus penggunaan yang sangat umum dalam kedokteran dan biologi ketika Anda melakukan tindakan berulang.
Contoh: Asumsikan Anda ingin mengklasifikasikan suatu penyakit, misalnya penyakit Alzheimer (AD) vs. kontrol sehat dari gambar MR. Untuk subjek yang sama, Anda mungkin memiliki beberapa pemindaian (dari sesi tindak lanjut atau data longitudinal). Mari kita asumsikan Anda memiliki total 1000 mata pelajaran, 200 di antaranya didiagnosis dengan AD (kelas tidak seimbang). Sebagian besar subjek memiliki satu pemindaian, tetapi untuk beberapa di antaranya tersedia 2 atau 3 gambar. Saat melatih/menguji pengklasifikasi, Anda ingin memastikan bahwa gambar dari subjek yang sama selalu berada di lipatan yang sama untuk menghindari kebocoran data.
Yang terbaik adalah menggunakan StratifiedGroupKFold untuk ini: stratifikasi untuk memperhitungkan ketidakseimbangan kelas tetapi dengan batasan grup bahwa subjek tidak boleh muncul di lipatan yang berbeda.
NB: Akan lebih baik untuk membuatnya berulang.

Di bawah contoh implementasi, terinspirasi oleh 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

Membandingkan RepeatedStratifiedKFold (contoh dari grup yang sama mungkin muncul di kedua lipatan) dengan 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 untuk stratifiedGroupKfold. Saya mencoba mendeteksi jatuhnya senior, mengambil sensor dari jam tangan samrt. karena kami tidak memiliki banyak data musim gugur - kami melakukan simulasi dengan jam tangan berbeda yang mendapatkan kelas berbeda. Saya juga melakukan augmentasi pada data sebelum saya melatihnya. dari setiap titik data saya membuat 9 titik- dan ini adalah grup. penting bahwa suatu kelompok tidak akan berada dalam pelatihan dan ujian seperti yang dijelaskan

Saya ingin dapat menggunakan StratifiedGroupKFold juga. Saya melihat kumpulan data untuk memprediksi krisis keuangan, di mana tahun-tahun sebelum, sesudah dan selama setiap krisis adalah kelompoknya sendiri. Selama pelatihan dan validasi silang, anggota setiap kelompok tidak boleh bocor di antara lipatan.

Apakah ada cara untuk menggeneralisasi itu untuk skenario multilabel (Multilabel_
stratifiedGroupKfold)?

+1 untuk ini. Kami sedang menganalisis akun pengguna untuk spam, jadi kami ingin mengelompokkan berdasarkan pengguna, tetapi juga membuat stratifikasi karena insiden spam relatif rendah. Untuk kasus penggunaan kami, setiap pengguna yang pernah melakukan spam ditandai sebagai spammer di semua data, sehingga anggota grup akan selalu memiliki label yang sama.

Terima kasih telah menyediakan kasus penggunaan klasik untuk membingkai dokumentasi,
@philip-iv!

Saya menambahkan implementasi StratifiedGroupKFold ke PR #15239 saya yang sama dengan StratifiedGroupShuffleSplit .

Meskipun seperti yang Anda lihat di PR, logika keduanya jauh lebih sederhana daripada https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -557802602 karena milik saya hanya mencoba mempertahankan persentase grup untuk setiap kelas (bukan persentase sampel) sehingga saya dapat memanfaatkan kode StratifiedKFold dan StratifiedShuffleSplit yang ada dengan memberikannya informasi grup yang unik. Tetapi kedua implementasi menghasilkan lipatan di mana sampel masing-masing kelompok tetap bersama di lipatan yang sama.

Meskipun saya akan memilih metode yang lebih canggih berdasarkan https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -557802602

Berikut adalah versi lengkap dari StratifiedGroupKFold dan RepeatedStratifiedGroupKFold menggunakan kode yang disediakan @mrunibe yang selanjutnya saya sederhanakan dan ubah beberapa hal. Kelas-kelas ini juga mengikuti desain bagaimana kelas-kelas CV sklearn lain dari jenis yang sama dilakukan.

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 Saya cukup bingung dengan apa yang telah kami selesaikan ketika melihat kembali ini dari waktu ke waktu. (Sayangnya waktu saya tidak seperti dulu!) Bisakah Anda memberi saya gambaran tentang apa yang Anda rekomendasikan untuk dimasukkan dalam scikit-learn?

@hermidalc Saya cukup bingung dengan apa yang telah kami selesaikan ketika melihat kembali ini dari waktu ke waktu. (Sayangnya waktu saya tidak seperti dulu!) Bisakah Anda memberi saya gambaran tentang apa yang Anda rekomendasikan untuk dimasukkan dalam scikit-learn?

Saya ingin melakukan implementasi yang lebih baik daripada yang saya lakukan di #15239. Implementasi dalam PR itu berhasil tetapi membuat stratifikasi pada kelompok untuk membuat logika langsung, meskipun ini tidak ideal.

Jadi apa yang saya lakukan di atas (terima kasih kepada @mrunibe dan kaggle dari jakubwasikowski) adalah implementasi yang lebih baik dari StratifiedGroupKFold yang bertingkat pada sampel. Saya ingin mem-porting logika yang sama untuk melakukan StratifiedGroupShuffleSplit yang lebih baik dan kemudian akan siap. Saya akan meletakkan kode baru di #15239 untuk menggantikan implementasi yang lebih lama.

Saya minta maaf tentang PR saya yang belum selesai, saya mendapatkan PhD saya jadi tidak pernah punya waktu!

Terima kasih @hermidalc dan @mrunibe telah menyediakan implementasinya. Saya juga telah mencari metode StratifiedGroupKFold untuk menangani data medis yang memiliki ketidakseimbangan kelas yang kuat dan jumlah sampel yang sangat bervariasi per subjek. GroupKFold dengan sendirinya membuat sub-set data pelatihan yang hanya berisi satu kelas.

Saya ingin mem-porting logika yang sama untuk melakukan StratifiedGroupShuffleSplit yang lebih baik dan kemudian akan siap.

Kita pasti bisa mempertimbangkan untuk menggabungkan StratifiedGroupKFold sebelum StratifiedGroupShuffleSplit siap.

Saya minta maaf tentang PR saya yang belum selesai, saya mendapatkan PhD saya jadi tidak pernah punya waktu!

Beri tahu kami jika Anda menginginkan dukungan untuk menyelesaikannya!

Dan semoga sukses dengan pekerjaan PhD Anda

Berikut adalah versi lengkap dari StratifiedGroupKFold dan RepeatedStratifiedGroupKFold menggunakan kode yang disediakan @mrunibe yang selanjutnya saya sederhanakan dan ubah beberapa hal. Kelas-kelas ini juga mengikuti desain bagaimana kelas-kelas CV sklearn lain dari jenis yang sama dilakukan.

Apakah mungkin untuk mencoba ini? Saya mencoba memotong dan menempel dengan beberapa dari berbagai dependensi tetapi tidak pernah berakhir. Saya ingin mencoba kelas ini dalam proyek saya. Hanya mencoba untuk melihat apakah ada cara yang tersedia sekarang untuk melakukan itu.

@hermidalc Semoga pekerjaan PhD Anda berhasil!
Saya berharap untuk melihat penerapan ini dilakukan juga karena pekerjaan PhD saya di Geosains membutuhkan fitur stratifikasi ini dengan kontrol grup. Saya telah menghabiskan beberapa jam untuk menerapkan gagasan pemisahan secara manual pada proyek saya. Tapi saya menyerah menyelesaikannya karena alasan yang sama ... kemajuan PhD. Jadi, saya benar-benar dapat memahami bagaimana pekerjaan PhD dapat menyiksa waktu seseorang. LOL Tidak ada tekanan. Untuk saat ini, saya menggunakan GroupShuffleSplit sebagai alternatif.

Bersulang

@bfeeny @dispink sangat mudah menggunakan dua kelas yang saya tulis di atas. Buat file misalnya split.py dengan yang berikut ini. Kemudian dalam kode pengguna Anda jika skrip berada di direktori yang sama dengan split.py Anda cukup mengimpor 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 Terima kasih atas balasan positifnya!
Saya segera mengadopsinya seperti yang Anda jelaskan. Namun, saya hanya bisa mendapatkan pemisahan yang hanya memiliki data di set pelatihan atau pengujian. Sejauh yang saya pahami deskripsi kode, tidak ada parameter untuk menentukan proporsi antara set pelatihan dan tes, bukan?
Saya tahu ini konflik antara Stratifikasi, kontrol grup, dan proporsi kumpulan data... Itu sebabnya saya berhenti melanjutkan... Tapi mungkin kita masih bisa menemukan kompromi untuk diselesaikan.
image

Sungguh-sungguh

@hermidalc Terima kasih atas balasan positifnya!
Saya segera mengadopsinya seperti yang Anda jelaskan. Namun, saya hanya bisa mendapatkan pemisahan yang hanya memiliki data di set pelatihan atau pengujian. Sejauh yang saya pahami deskripsi kode, tidak ada parameter untuk menentukan proporsi antara set pelatihan dan tes, bukan?
Saya tahu ini konflik antara Stratifikasi, kontrol grup, dan proporsi kumpulan data... Itu sebabnya saya berhenti melanjutkan... Tapi mungkin kita masih bisa menemukan kompromi untuk diselesaikan.

Untuk menguji saya membuat split.py dan menjalankan contoh ini di ipython dan berfungsi. Saya telah menggunakan iterator CV khusus ini dalam pekerjaan saya untuk waktu yang lama dan mereka tidak memiliki masalah di pihak saya. BTW saya menggunakan scikit-learn 0.22.2 bukan 0.23.x, jadi tidak yakin apakah itu penyebab masalah. Bisakah Anda mencoba menjalankan contoh di bawah ini dan melihat apakah Anda dapat mereproduksinya? Jika Anda bisa, maka itu mungkin sesuatu dengan y dan groups dalam pekerjaan Anda.

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]

Tampaknya ada minat reguler pada fitur ini, @hermidalc , dan kami
mungkin bisa menemukan seseorang untuk menyelesaikannya jika Anda tidak keberatan.

@hermidalc 'Anda harus memastikan bahwa setiap sampel dalam kelompok yang sama memiliki label kelas yang sama.' Jelas itu masalahnya. Sampel saya di grup yang sama tidak berbagi kelas yang sama. Mmm...sepertinya itu adalah cabang lain dari perkembangan.
Terima kasih banyak.

@hermidalc 'Anda harus memastikan bahwa setiap sampel dalam kelompok yang sama memiliki label kelas yang sama.' Jelas itu masalahnya. Sampel saya di grup yang sama tidak berbagi kelas yang sama. Mmm...sepertinya itu adalah cabang lain dari perkembangan.
Terima kasih banyak.

Ya ini sudah dibahas di berbagai thread disini. Ini adalah kasus penggunaan lain yang lebih kompleks yang berguna, tetapi banyak orang seperti saya tidak membutuhkan kasus penggunaan itu saat ini tetapi membutuhkan sesuatu dengan tetap menyatukan grup namun membuat stratifikasi pada sampel. Persyaratan kode di atas adalah bahwa semua sampel dalam setiap kelompok termasuk dalam kelas yang sama.

Sebenarnya @dispink saya salah, algoritma ini tidak mengharuskan semua anggota grup memiliki kelas yang sama. Sebagai contoh:

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]

Jadi saya tidak yakin apa yang terjadi dengan data Anda, karena bahkan dengan tangkapan layar Anda, Anda tidak dapat benar-benar melihat apa tata letak data Anda dan apa yang mungkin terjadi. Saya sarankan Anda terlebih dahulu mereproduksi contoh yang saya tunjukkan di sini untuk memastikan itu bukan masalah versi scikit-learn (karena saya menggunakan 0.22.2) dan jika Anda dapat mereproduksinya maka saya sarankan Anda mulai dari bagian kecil Anda data dan mengujinya. Menggunakan ~104k sampel sulit untuk dipecahkan.

@hermidalc Terima kasih atas jawabannya!
Saya sebenarnya dapat mereproduksi hasil di atas, jadi saya memecahkan masalah dengan data yang lebih kecil sekarang.

+1

Adakah yang keberatan jika saya mengangkat masalah ini?
Tampaknya #15239 bersama dengan https://github.com/scikit-learn/scikit-learn/issues/13621#issuecomment -600894432 sudah memiliki implementasi dan hanya unit test yang tersisa untuk dilakukan.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat