Conjunto de trem / teste / validação dividido no Sklearn

59

Como dividir aleatoriamente uma matriz de dados e o vetor de rótulo correspondente em um X_train, X_test, X_val, y_train, y_test, y_val com o Sklearn? Tanto quanto eu sei, sklearn.cross_validation.train_test_splitsó é capaz de se dividir em dois, não em três ...

Hendrik
fonte

Respostas:

81

Você pode apenas usar sklearn.model_selection.train_test_splitduas vezes. Primeiro divida para treinar, teste e, em seguida, divida novamente o trem em validação e trem. Algo assim:

 X_train, X_test, y_train, y_test 
    = train_test_split(X, y, test_size=0.2, random_state=1)

 X_train, X_val, y_train, y_val 
    = train_test_split(X_train, y_train, test_size=0.2, random_state=1)
hh32
fonte
11
Sim, isso funciona, é claro, mas eu esperava algo mais elegante;) Não importa, eu aceito esta resposta.
Hendrik
11
Eu queria acrescentar que, se você quiser usar o conjunto de validação para procurar os melhores hiper-parâmetros que você pode fazer o seguinte após o desdobramento: gist.github.com/albertotb/1bad123363b186267e3aeaa26610b54b
SKD
12
Então, qual é a proporção final de trem, teste e validação neste exemplo? Porque no segundo train_test_split , você está fazendo isso na divisão 80/20 anterior. Portanto, seu valor é 20% de 80%. As proporções divididas não são muito diretas dessa maneira.
Monica Heddneck
11
Concordo com a @Monica Heddneck que o trem de 64%, a validação de 16% e a divisão de teste de 20% podem ser mais claros. É uma inferência irritante que você precisa fazer com esta solução.
Perry
32

Há uma ótima resposta para essa pergunta no SO que usa numpy e pandas.

O comando (veja a resposta para a discussão):

train, validate, test = np.split(df.sample(frac=1), [int(.6*len(df)), int(.8*len(df))])

produz uma divisão de 60%, 20%, 20% para os conjuntos de treinamento, validação e teste.

0_0
fonte
2
Eu posso ver o .6significado 60% ... mas o que .8significa?
Tom Hale
11
O @TomHale np.splitse dividirá em 60% do comprimento da matriz aleatória e, em seguida, 80% do comprimento (que é um 20% adicional de dados), deixando assim 20% dos dados restantes. Isto é devido à definição da função. Você pode testar / jogar com x = np.arange(10.0)np.split(x, [ int(len(x)*0.6), int(len(x)*0.8)])
:,
3

Na maioria das vezes, você não os dividirá uma vez, mas em um primeiro passo você dividirá seus dados em um conjunto de treinamento e teste. Posteriormente, você realizará uma pesquisa de parâmetros incorporando splings mais complexos, como validação cruzada com o algoritmo 'split k-fold' ou 'leave-one-out (LOO)'.

JLT
fonte
3

Você pode usar train_test_splitduas vezes. Eu acho que isso é mais direto.

X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=1)
X_train, X_val, y_train, y_val = train_test_split(
    X_train, y_train, test_size=0.25, random_state=1)

Desta forma, train, val, testconjunto será de 60%, 20%, 20% do conjunto de dados, respectivamente.

David Jung
fonte
2

A melhor resposta acima não menciona que, ao separar duas vezes, train_test_splitsem alterar os tamanhos das partições, você não fornecerá a partição inicialmente pretendida:

x_train, x_remain = train_test_split(x, test_size=(val_size + test_size))

Em seguida, a parte dos conjuntos de validação e teste no x_remain muda e pode ser contada como

new_test_size = np.around(test_size / (val_size + test_size), 2)
# To preserve (new_test_size + new_val_size) = 1.0 
new_val_size = 1.0 - new_test_size

x_val, x_test = train_test_split(x_remain, test_size=new_test_size)

Nesta ocasião, todas as partições iniciais são salvas.

A.Ametov
fonte
1

Aqui está outra abordagem (assume igual divisão de três vias):

# randomly shuffle the dataframe
df = df.reindex(np.random.permutation(df.index))

# how many records is one-third of the entire dataframe
third = int(len(df) / 3)

# Training set (the top third from the entire dataframe)
train = df[:third]

# Testing set (top half of the remainder two third of the dataframe)
test = df[third:][:third]

# Validation set (bottom one third)
valid = df[-third:]

Isso pode ser mais conciso, mas eu o mantive detalhado para fins de explicação.

Vishal
fonte
0

Dada train_frac=0.8, essa função cria uma divisão de 80% / 10% / 10%:

import sklearn

def data_split(examples, labels, train_frac, random_state=None):
    ''' https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html
    param data:       Data to be split
    param train_frac: Ratio of train set to whole dataset

    Randomly split dataset, based on these ratios:
        'train': train_frac
        'valid': (1-train_frac) / 2
        'test':  (1-train_frac) / 2

    Eg: passing train_frac=0.8 gives a 80% / 10% / 10% split
    '''

    assert train_frac >= 0 and train_frac <= 1, "Invalid training set fraction"

    X_train, X_tmp, Y_train, Y_tmp = sklearn.model_selection.train_test_split(
                                        examples, labels, train_size=train_frac, random_state=random_state)

    X_val, X_test, Y_val, Y_test   = sklearn.model_selection.train_test_split(
                                        X_tmp, Y_tmp, train_size=0.5, random_state=random_state)

    return X_train, X_val, X_test,  Y_train, Y_val, Y_test
Tom Hale
fonte
0

Adicionando à resposta de @ hh32 , respeitando as proporções predefinidas, como (75, 15, 10):

train_ratio = 0.75
validation_ratio = 0.15
test_ratio = 0.10

# train is now 75% of the entire data set
# the _junk suffix means that we drop that variable completely
x_train, x_test, y_train, y_test = train_test_split(dataX, dataY, test_size=1 - train_ratio)

# test is now 10% of the initial data set
# validation is now 15% of the initial data set
x_val, x_test, y_val, y_test = train_test_split(x_test, y_test, test_size=test_ratio/(test_ratio + validation_ratio)) 

print(x_train, x_val, x_test)
Andrei Florea
fonte
0

Extensão da resposta do @ hh32 com proporções preservadas.

# Defines ratios, w.r.t. whole dataset.
ratio_train = 0.8
ratio_val = 0.1
ratio_test = 0.1

# Produces test split.
x_remaining, x_test, y_remaining, y_test = train_test_split(
    x, y, test_size=test_ratio)

# Adjusts val ratio, w.r.t. remaining dataset.
ratio_remaining = 1 - ratio_test
ratio_val_adjusted = ratio_val / ratio_remaining

# Produces train and val splits.
x_train, x_val, y_train, y_val = train_test_split(
    x_remaining, y_remaining, test_size=ratio_val_adjusted)

Como o conjunto de dados restante é reduzido após a primeira divisão, novas proporções com relação ao conjunto de dados reduzido devem ser calculadas resolvendo a equação:

RremainingRnew=Rold

Jorge Barrios
fonte