train test prueba mineria independientes entrenamiento datos conjuntos machine-learning scikit-learn svm cross-validation

machine-learning - prueba - datos train y test



Cómo dividir los datos en un conjunto de entrenamiento equilibrado y un conjunto de pruebas en sklearn (4)

Estoy usando sklearn para la tarea de clasificación múltiple. Necesito dividir todos los datos en train_set y test_set. Quiero tomar al azar el mismo número de muestra de cada clase. En realidad, estoy divirtiendo esta función.

X_train, X_test, y_train, y_test = cross_validation.train_test_split(Data, Target, test_size=0.3, random_state=0)

Pero da datos desbalanceados! Cualquier sugerencia.


Aunque la sugerencia de Christian es correcta, técnicamente train_test_split debería proporcionarle resultados estratificados utilizando el stratify stratify.

Para que pudieras hacer:

X_train, X_test, y_train, y_test = cross_validation.train_test_split(Data, Target, test_size=0.3, random_state=0, stratify=Target)

El truco aquí es que comienza a partir de la versión 0.17 en sklearn .

De la documentación sobre el parámetro stratify :

stratify: array-like o None (el valor predeterminado es None) Si no es None, los datos se dividen de forma estratificada, utilizando esto como la matriz de etiquetas. Nuevo en la versión 0.17: estratificar la división


Esta es mi implementación que uso para obtener índices de datos de tren / prueba

def get_safe_balanced_split(target, trainSize=0.8, getTestIndexes=True, shuffle=False, seed=None): classes, counts = np.unique(target, return_counts=True) nPerClass = float(len(target))*float(trainSize)/float(len(classes)) if nPerClass > np.min(counts): print("Insufficient data to produce a balanced training data split.") print("Classes found %s"%classes) print("Classes count %s"%counts) ts = float(trainSize*np.min(counts)*len(classes)) / float(len(target)) print("trainSize is reset from %s to %s"%(trainSize, ts)) trainSize = ts nPerClass = float(len(target))*float(trainSize)/float(len(classes)) # get number of classes nPerClass = int(nPerClass) print("Data splitting on %i classes and returning %i per class"%(len(classes),nPerClass )) # get indexes trainIndexes = [] for c in classes: if seed is not None: np.random.seed(seed) cIdxs = np.where(target==c)[0] cIdxs = np.random.choice(cIdxs, nPerClass, replace=False) trainIndexes.extend(cIdxs) # get test indexes testIndexes = None if getTestIndexes: testIndexes = list(set(range(len(target))) - set(trainIndexes)) # shuffle if shuffle: trainIndexes = random.shuffle(trainIndexes) if testIndexes is not None: testIndexes = random.shuffle(testIndexes) # return indexes return trainIndexes, testIndexes


Puede usar StratifiedShuffleSplit para crear conjuntos de datos con el mismo porcentaje de clases que el original:

import numpy as np from sklearn.cross_validation import StratifiedShuffleSplit X = np.array([[1, 3], [3, 7], [2, 4], [4, 8]]) y = np.array([0, 1, 0, 1]) stratSplit = StratifiedShuffleSplit(y, 1, test_size=0.5,random_state=42) StratifiedShuffleSplit(y, n_iter=1, test_size=0.5) for train_idx,test_idx in stratSplit: X_train=X[train_idx] y_train=y[train_idx] print(X_train) print(y_train) //[[3 7] // [2 4]] //[1 0]


Si las clases no están equilibradas pero desea que la división sea equilibrada, entonces la estratificación no ayudará. Parece que no hay un método para realizar un muestreo equilibrado en sklearn, pero es bastante fácil usar un número básico, por ejemplo, una función como esta podría ayudarlo a:

def split_balanced(data, target, test_size=0.2): classes = np.unique(target) # can give test_size as fraction of input data size of number of samples if test_size<1: n_test = np.round(len(target)*test_size) else: n_test = test_size n_train = max(0,len(target)-n_test) n_train_per_class = max(1,int(np.floor(n_train/len(classes)))) n_test_per_class = max(1,int(np.floor(n_test/len(classes)))) ixs = [] for cl in classes: if (n_train_per_class+n_test_per_class) > np.sum(target==cl): # if data has too few samples for this class, do upsampling # split the data to training and testing before sampling so data points won''t be # shared among training and test data splitix = int(np.ceil(n_train_per_class/(n_train_per_class+n_test_per_class)*np.sum(target==cl))) ixs.append(np.r_[np.random.choice(np.nonzero(target==cl)[0][:splitix], n_train_per_class), np.random.choice(np.nonzero(target==cl)[0][splitix:], n_test_per_class)]) else: ixs.append(np.random.choice(np.nonzero(target==cl)[0], n_train_per_class+n_test_per_class, replace=False)) # take same num of samples from all classes ix_train = np.concatenate([x[:n_train_per_class] for x in ixs]) ix_test = np.concatenate([x[n_train_per_class:(n_train_per_class+n_test_per_class)] for x in ixs]) X_train = data[ix_train,:] X_test = data[ix_test,:] y_train = target[ix_train] y_test = target[ix_test] return X_train, X_test, y_train, y_test

Tenga en cuenta que si usa esto y muestra más puntos por clase que en los datos de entrada, entonces se muestrearán (muestreando con reemplazo). Como resultado, algunos puntos de datos aparecerán varias veces y esto puede tener un efecto en las medidas de precisión, etc. Y si alguna clase tiene solo un punto de datos, habrá un error. Puede verificar fácilmente el número de puntos por clase, por ejemplo, con np.unique(target, return_counts=True)