Jak podzielić / podzielić zbiór danych na treningowe i testowe zestawy danych, np. W celu weryfikacji krzyżowej?


99

Jaki jest dobry sposób na losowe podzielenie tablicy NumPy na zbiór danych treningowych i testowych / walidacyjnych? Coś podobnego do funkcji cvpartitionlub crossvalindw Matlabie.

Odpowiedzi:


125

Jeśli chcesz podzielić zestaw danych raz na dwie części, możesz użyć numpy.random.shufflelub numpy.random.permutationjeśli chcesz śledzić indeksy:

import numpy
# x is your dataset
x = numpy.random.rand(100, 5)
numpy.random.shuffle(x)
training, test = x[:80,:], x[80:,:]

lub

import numpy
# x is your dataset
x = numpy.random.rand(100, 5)
indices = numpy.random.permutation(x.shape[0])
training_idx, test_idx = indices[:80], indices[80:]
training, test = x[training_idx,:], x[test_idx,:]

Istnieje wiele sposobów wielokrotnego dzielenia tego samego zestawu danych na potrzeby weryfikacji krzyżowej . Jedną ze strategii jest ponowne próbkowanie ze zbioru danych z powtórzeniem:

import numpy
# x is your dataset
x = numpy.random.rand(100, 5)
training_idx = numpy.random.randint(x.shape[0], size=80)
test_idx = numpy.random.randint(x.shape[0], size=20)
training, test = x[training_idx,:], x[test_idx,:]

Wreszcie, sklearn zawiera kilka metod walidacji krzyżowej (k-fold, opuszczanie n-out, ...). Obejmuje również bardziej zaawansowane metody „próbkowania warstwowego” , które tworzą podział danych, który jest zrównoważony w odniesieniu do niektórych cech, na przykład w celu upewnienia się, że w zbiorze uczącym i testowym jest taki sam udział przykładów pozytywnych i negatywnych.


13
dzięki za te rozwiązania. Ale czy ostatnia metoda, wykorzystująca randint, nie ma dużej szansy na podanie tych samych wskaźników zarówno dla zestawów testowych, jak i szkoleniowych?
— ggauravr

3
Drugie rozwiązanie jest poprawną odpowiedzią, a pierwsze i trzecie nie. W przypadku pierwszego rozwiązania tasowanie zestawu danych nie zawsze jest opcją, istnieje wiele przypadków, w których trzeba zachować kolejność wprowadzania danych. A trzecia z nich mogłaby bardzo dobrze wytworzyć te same wskaźniki dla testów i treningu (jak wskazał @ggauravr).
— pedram bashiri

Należy nie resample dla przekroju zestawu walidacji. Cały pomysł polega na tym, że zestaw CV nigdy wcześniej nie był widziany przez twojego algo. Zestawy treningowe i testowe służą do dopasowania danych, więc oczywiście uzyskasz dobre wyniki, jeśli włączysz je do zestawu CV. Chcę zagłosować za tą odpowiedzią, ponieważ potrzebowałem drugiego rozwiązania, ale ta odpowiedź ma problemy.
— RubberDuck

55

Jest jeszcze jedna opcja, która po prostu pociąga za sobą użycie scikit-learn. Jak opisuje wiki scikit , możesz po prostu użyć następujących instrukcji:

from sklearn.model_selection import train_test_split

data, labels = np.arange(10).reshape((5, 2)), range(5)

data_train, data_test, labels_train, labels_test = train_test_split(data, labels, test_size=0.20, random_state=42)

W ten sposób możesz zsynchronizować etykiety danych, które próbujesz podzielić na trening i test.


1
Jest to bardzo praktyczna odpowiedź ze względu na realistyczną obsługę zarówno zestawu pociągów, jak i etykiet.
— chinnychinchin

38

Tylko uwaga. Jeśli chcesz trenować, testować ORAZ zestawy walidacyjne, możesz to zrobić:

from sklearn.cross_validation import train_test_split

X = get_my_X()
y = get_my_y()
x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
x_test, x_val, y_test, y_val = train_test_split(x_test, y_test, test_size=0.5)

Te parametry dadzą 70% na trening i po 15% na zestawy testowe i wartościowe. Mam nadzieję że to pomoże.


5
powinien prawdopodobnie dodać to do swojego kodu: from sklearn.cross_validation import train_test_splitaby było jasne, jakiego modułu używasz
— Radix,

Czy to musi być przypadkowe?
— liang

To znaczy, czy można podzielić zgodnie z podaną kolejnością X i Y?
— liang

1
@liang nie, to nie musi być przypadkowe. możesz po prostu powiedzieć, że rozmiary zestawów pociągów, testów i walidacji będą procentami a, b i c całkowitego zbioru danych. powiedzmy a=0.7, b=0.15, c=0.15, i d = dataset, N=len(dataset), a następnie x_train = dataset[0:int(a*N)], x_test = dataset[int(a*N):int((a+b)*N)]i x_val = dataset[int((a+b)*N):].
— offwhitelotus

1
Wycofane: stackoverflow.com/a/34844352/4237080 , użyjfrom sklearn.model_selection import train_test_split
— briennakh

14

Ponieważ sklearn.cross_validationmoduł został wycofany, możesz użyć:

import numpy as np
from sklearn.model_selection import train_test_split
X, y = np.arange(10).reshape((5, 2)), range(5)

X_trn, X_tst, y_trn, y_tst = train_test_split(X, y, test_size=0.2, random_state=42)

5

Możesz również rozważyć rozwarstwiony podział na zbiór uczący i testowy. Rozpoczęty podział również generuje losowo zestaw treningowy i testowy, ale w taki sposób, aby zachować oryginalne proporcje klasowe. Dzięki temu zestawy uczące i testowe lepiej odzwierciedlają właściwości oryginalnego zestawu danych.

import numpy as np  

def get_train_test_inds(y,train_proportion=0.7):
    '''Generates indices, making random stratified split into training set and testing sets
    with proportions train_proportion and (1-train_proportion) of initial sample.
    y is any iterable indicating classes of each observation in the sample.
    Initial proportions of classes inside training and 
    testing sets are preserved (stratified sampling).
    '''

    y=np.array(y)
    train_inds = np.zeros(len(y),dtype=bool)
    test_inds = np.zeros(len(y),dtype=bool)
    values = np.unique(y)
    for value in values:
        value_inds = np.nonzero(y==value)[0]
        np.random.shuffle(value_inds)
        n = int(train_proportion*len(value_inds))

        train_inds[value_inds[:n]]=True
        test_inds[value_inds[n:]]=True

    return train_inds,test_inds

y = np.array([1,1,2,2,3,3])
train_inds,test_inds = get_train_test_inds(y,train_proportion=0.5)
print y[train_inds]
print y[test_inds]

Ten kod wyprowadza:

[1 2 3]
[1 2 3]

Dziękuję Ci! Nazewnictwo jest nieco mylące, value_indssą naprawdę indeksami, ale wynik nie jest indeksami, tylko maskami.
— greenoldman

1

Napisałem funkcję do mojego własnego projektu, aby to zrobić (chociaż nie używa numpy):

def partition(seq, chunks):
    """Splits the sequence into equal sized chunks and them as a list"""
    result = []
    for i in range(chunks):
        chunk = []
        for element in seq[i:len(seq):chunks]:
            chunk.append(element)
        result.append(chunk)
    return result

Jeśli chcesz, aby fragmenty były losowe, po prostu potasuj listę przed przekazaniem jej.


0

Oto kod do podziału danych na n = 5 razy w sposób warstwowy

% X = data array
% y = Class_label
from sklearn.cross_validation import StratifiedKFold
skf = StratifiedKFold(y, n_folds=5)
for train_index, test_index in skf:
    print("TRAIN:", train_index, "TEST:", test_index)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

0

Dzięki pberkes za odpowiedź. Po prostu zmodyfikowałem to, aby uniknąć (1) zamiany podczas próbkowania (2) zduplikowane instancje występowały zarówno podczas treningu, jak i testowania:

training_idx = np.random.choice(X.shape[0], int(np.round(X.shape[0] * 0.8)),replace=False)
training_idx = np.random.permutation(np.arange(X.shape[0]))[:np.round(X.shape[0] * 0.8)]
    test_idx = np.setdiff1d( np.arange(0,X.shape[0]), training_idx)

0

Po przeczytaniu i wzięciu pod uwagę (wielu ...) różnych sposobów dzielenia danych w celu trenowania i testowania, postanowiłem trochę poczekać!

Użyłem 4 różnych metod (żadna z nich nie korzysta z biblioteki sklearn, która na pewno da najlepsze rezultaty, dając dobrze zaprojektowany i przetestowany kod):

  1. przetasuj całą macierz arr, a następnie podziel dane w celu trenowania i testowania
  2. przetasuj indeksy, a następnie przypisz im x i y, aby podzielić dane
  3. to samo, co metoda 2, ale w bardziej efektywny sposób
  4. przy użyciu pandy dataframe do podziału

metoda 3 wygrała zdecydowanie z najkrótszym czasem, po tym jak metoda 1, a metody 2 i 4 okazały się naprawdę nieefektywne.

Kod dla 4 różnych metod, które mierzyłem czasowo:

import numpy as np
arr = np.random.rand(100, 3)
X = arr[:,:2]
Y = arr[:,2]
spl = 0.7
N = len(arr)
sample = int(spl*N)

#%% Method 1:  shuffle the whole matrix arr and then split
np.random.shuffle(arr)
x_train, x_test, y_train, y_test = X[:sample,:], X[sample:, :], Y[:sample, ], Y[sample:,]

#%% Method 2: shuffle the indecies and then shuffle and apply to X and Y
train_idx = np.random.choice(N, sample)
Xtrain = X[train_idx]
Ytrain = Y[train_idx]

test_idx = [idx for idx in range(N) if idx not in train_idx]
Xtest = X[test_idx]
Ytest = Y[test_idx]

#%% Method 3: shuffle indicies without a for loop
idx = np.random.permutation(arr.shape[0])  # can also use random.shuffle
train_idx, test_idx = idx[:sample], idx[sample:]
x_train, x_test, y_train, y_test = X[train_idx,:], X[test_idx,:], Y[train_idx,], Y[test_idx,]

#%% Method 4: using pandas dataframe to split
import pandas as pd
df = pd.read_csv(file_path, header=None) # Some csv file (I used some file with 3 columns)

train = df.sample(frac=0.7, random_state=200)
test = df.drop(train.index)

A dla czasów minimalny czas na wykonanie z 3 powtórzeń 1000 pętli to:

  • Metoda 1: 0,35883826200006297 sekund
  • Metoda 2: 1,7157016959999964 sekund
  • Metoda 3: 1,7876616719995582 sekund
  • Metoda 4: 0,07562861499991413 sekund

Mam nadzieję, że to pomocne!


0

Prawdopodobnie będziesz musiał nie tylko podzielić się na trenowanie i testowanie, ale także weryfikację krzyżową, aby upewnić się, że model uogólnia. Tutaj zakładam 70% danych treningowych, 20% walidację i 10% wstrzymanie / dane testowe.

Sprawdź np.split :

Jeśli indices_or_sections to 1-D tablica posortowanych liczb całkowitych, wpisy wskazują, gdzie wzdłuż osi tablica jest podzielona. Na przykład [2, 3] dałoby dla osi = 0 wynik

ary [: 2] ary [2: 3] ary [3:]

t, v, h = np.split(df.sample(frac=1, random_state=1), [int(0.7*len(df)), int(0.9*len(df))]) 

0

Podzielony na test pociągu i ważny

x =np.expand_dims(np.arange(100), -1)


print(x)

indices = np.random.permutation(x.shape[0])

training_idx, test_idx, val_idx = indices[:int(x.shape[0]*.9)], indices[int(x.shape[0]*.9):int(x.shape[0]*.95)],  indices[int(x.shape[0]*.9):int(x.shape[0]*.95)]


training, test, val = x[training_idx,:], x[test_idx,:], x[val_idx,:]

print(training, test, val)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.