Scikit-Learn's train_test_split() - Conjuntos de entrenamiento, prueba y validación

En esta guía, veremos cómo dividir un conjunto de datos en un conjunto de entrenamiento, prueba y validación usando el método train_test_split() de Scikit-Learn, con ejemplos prácticos y consejos para las mejores prácticas.

Introducción

Scikit-Learn es una de las bibliotecas de aprendizaje automático más utilizadas en Python. Está optimizado y es eficiente, y su API de alto nivel es simple y fácil de usar.

Scikit-Learn tiene una plétora de herramientas y métodos convenientes que hacen que el preprocesamiento, la evaluación y otros procesos minuciosos sean tan fáciles como llamar a un solo método, y dividir los datos entre un conjunto de entrenamiento y prueba es sin excepción.

En términos generales, la regla general para dividir datos es 80/20, donde el 80 % de los datos se usa para entrenar un modelo, mientras que el 20 % se usa para probarlo. Esto depende del conjunto de datos con el que esté trabajando, pero una división 80/20 es muy común y le permitiría superar la mayoría de los conjuntos de datos sin problemas.

En esta guía, veremos cómo usar el método split_train_test() en Scikit-Learn y cómo configurar los parámetros para que tenga control sobre el proceso de división.

Instalación de Scikit-Learn {#instalación de Scikit-Learn}

Suponiendo que aún no esté instalado, Scikit-Learn se puede instalar fácilmente a través de pip:

1
$ pip install scikit-learn

Una vez instalada, puede importar la propia biblioteca a través de:

1
import sklearn

En la mayoría de los casos, las personas evitan importar toda la biblioteca, ya que es bastante amplia, e importan clases o módulos específicos que usarán específicamente.

{.icon aria-hidden=“true”}

Nota: Esto tiende a significar que las personas tienen una gran lista de importación cuando usan Scikit-Learn.

Importancia de los conjuntos de entrenamiento y prueba

El procedimiento más común cuando se entrena un modelo (básico) en Machine Learning sigue el mismo esquema general:

  • Adquirir y procesar datos que introduciremos en un modelo.

Scikit-learn tiene varios conjuntos de datos para cargar y usar para entrenar el modelo (iris, diabetes, dígitos...), principalmente para evaluación comparativa/aprendizaje.

  • Dividir conjuntos en conjuntos de entrenamiento y prueba
  • Construcción de un modelo y definición de la arquitectura.
  • Compilar el modelo
  • Entrenando al modelo
  • Verificando los resultados

El conjunto de entrenamiento es un subconjunto del conjunto de datos completo y, por lo general, no entrenamos un modelo en la totalidad de los datos. En modelos no generativos, un conjunto de entrenamiento generalmente contiene alrededor del 80% de los datos del conjunto de datos principal. Como su nombre lo indica, se utiliza para entrenar el modelo. Este procedimiento también se conoce como ajuste del modelo.

Sin embargo, hay excepciones a esta regla.

Por ejemplo, al entrenar Generative Adversarial Networks (GAN) que generan imágenes, ¿cómo prueba los resultados? Son muy subjetivos en algunos casos, ya que representan instancias nuevas que nunca antes se habían visto. En la mayoría de los modelos generativos, al menos a partir de ahora, normalmente se requiere que un ser humano juzgue los resultados, en cuyo caso, un conjunto de prueba es totalmente redundante.

Además, a veces necesita más o menos del 20 % para las pruebas, y si está utilizando técnicas como validación cruzada, es posible que desee tener un poco menos de datos de prueba para no "quitar" demasiado de los datos de entrenamiento. Por ejemplo, si tiene 1.000.000 de instancias en un conjunto de datos, reservar solo el 5 % para un conjunto de prueba equivale a 50.000 instancias, lo que probablemente sea más que suficiente para probar cualquier modelo.

El conjunto de prueba es un subconjunto de todo el conjunto de datos y se usa para evaluar el modelo y verificar qué tan bien aprendió del conjunto de entrenamiento.

El modelo no debe interactuar o ver el conjunto de prueba antes de evaluar. Los datos deben ser desconocidos cuando se evalúa por primera vez; de lo contrario, realmente no se está probando el modelo.

¿Qué sucede con los conjuntos de validación? {#qué pasa con los conjuntos de validación}

Los conjuntos de validación son algo común en los modelos profesionales y académicos. Los conjuntos de validación se extraen del conjunto de entrenamiento y se usan durante el entrenamiento para validar la precisión del modelo aproximadamente.

El conjunto de prueba está completamente desconectado hasta que el modelo termina de entrenarse, pero el conjunto de validación se usa para validarlo durante el entrenamiento.

{.icon aria-hidden=“true”}

Nota: El conjunto de validación no se usa para el entrenamiento y el modelo no se entrena con los datos. Simplemente valida la época actual. De esta manera, indirectamente se entrena con los datos, ya que afecta sus creencias previas, por lo que el conjunto de validación no se puede usar para realizar pruebas.

Similar a cómo aprenderá más sobre su propio conocimiento si escucha que es incorrecto, incluso si no sabe por qué. Esta es la razón por la cual los conjuntos de validación aproximan la precisión de un modelo, y los conjuntos de prueba aún son necesarios incluso cuando se usa un conjunto de validación.

Ayudan a aproximar el rendimiento real de un modelo durante el entrenamiento, por lo que no termina con un modelo de sobreajuste ilusorio sin darse cuenta después de probarlo a través de un conjunto de prueba. También puede usar conjuntos de validación para ajustar modelos y evaluar aproximadamente su capacidad sin exponerlos a un conjunto de prueba.

Los marcos de aprendizaje profundo como Keras pueden mostrar un val_accuracy además de su precisión de entrenamiento regular como una buena señal de sobreajuste. Si comienzan a divergir, su modelo se está sobreajustando durante el entrenamiento y no necesita perder el tiempo entrenándolo más cuando divergen lo suficiente. Además, las devoluciones de llamadas como EarlyStopping se pueden usar para detener automáticamente el entrenamiento de un modelo si val_accuracy no mejora después de n épocas, incluso si la precisión está aumentando.

Crear un conjunto de validación es fácil.

Puedes, literalmente, simplemente ejecutar el método train_test_split() en el conjunto de entrenamiento, que ya fue dividido por el método train_test_split() y extraer un conjunto de validación de él. Como alternativa, puede crear manualmente un conjunto de validación.

El tamaño del conjunto de validación generalmente se divide de manera similar a un conjunto de prueba; lo normal es que entre el 10 y el 20% del conjunto de entrenamiento. Para conjuntos de datos grandes, puede hacer mucho menos que esto, pero para conjuntos de datos pequeños, puede sacar demasiado, lo que dificulta que el modelo se ajuste a los datos en el conjunto de entrenamiento.

En las secciones siguientes, también sacaremos un conjunto de validación usando el mismo método train_test_split().

Módulo de conjuntos de datos de Scikit-Learn

Varios conjuntos de datos limpios y populares están disponibles integrados en Scikit-Learn, que generalmente se usan durante el aprendizaje y para modelos de evaluación comparativa en tareas simples.

Si alguna vez ha leído recursos sobre Machine Learning en Python, probablemente haya visto algunos de estos conjuntos de datos más populares:

  • Iris - conjunto de 3 clases (flores), con 50 muestras por clase, usado para clasificación.
  • Diabetes - conjunto con un total de 442 muestras, usado para regresión.
  • Dígitos - conjunto de 10 clases (dígitos escritos a mano), con ~180 muestras por clase, usado para clasificación.
  • Vino - conjunto de 3 clases (de vino), con un total de 178 muestras, utilizado para la clasificación.

Cada uno de estos conjuntos de datos se puede cargar a través del módulo conjuntos de datos con su función respectiva:

1
2
3
4
5
6
from sklearn import datasets

X_iris, y_iris = datasets.load_iris(return_X_y=True)
X_diabetes, y_diabetes = datasets.load_diabetes(return_X_y=True)
X_digits, y_digits = datasets.load_digits(return_X_y=True)
X_wine, y_wine = datasets.load_wine(return_X_y=True)

Alternativamente, puede cargar las funciones específicas en su lugar:

1
2
3
4
5
6
7
8
9
from sklearn.datasets import load_iris
from sklearn.datasets import load_diabetes
from sklearn.datasets import load_digits
from sklearn.datasets import load_wine

X_iris, y_iris = load_iris(return_X_y=True)
X_diabetes, y_diabetes = load_diabetes(return_X_y=True)
X_digits, y_digits = load_digits(return_X_y=True)
X_wine, y_wine = load_wine(return_X_y=True)

De forma predeterminada, estos métodos devuelven un objeto Bunch, que contiene los datos y los objetivos (datos y sus clases), sin embargo, si establece el argumento return_X_y en True, una tupla de (datos, objetivos) se devuelve, denotando los datos en los que estará entrenando y las clases de destino que desea que alcance su clasificador/modelo de regresión.

Dividir un conjunto de datos con train_test_split()

El método train_test_split() reside en el módulo sklearn.model_selection:

1
from sklearn.model_selection import train_test_split

Hay un par de argumentos que podemos establecer mientras trabajamos con este método, y el valor predeterminado es muy sensato y realiza una división 75/25. En la práctica, todos los valores predeterminados de Scikit-Learn\ son bastante razonables y están configurados para servir bien para la mayoría de las tareas. Sin embargo, vale la pena señalar cuáles son estos valores predeterminados, en los casos en que no funcionan tan bien.

Los dos argumentos principales son train_size y test_size, donde sus valores oscilan entre 0 y 1 y su suma debe ser 1. Sus valores denotan la proporción porcentual del conjunto de datos, por lo que incluso si define solo uno, como train_size, test_size es igual a 1 - train_size, y viceversa.

Configuración de los argumentos train_size y test_size

Este es el enfoque más común, que nos deja con 4 subconjuntos: X_train, X_test, y_train y y_test:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

X, y = load_iris(return_X_y=True)

X_train, X_test, y_train, y_test = train_test_split(X, y)

print(X_train.shape)
print(X_test.shape)
print(y_train.shape)
print(y_test.shape)

Sin configurar train_size o test_size, los valores predeterminados se activan, configurando test_size en 0.25 y un train_size complementario (0.75):

1
2
3
4
(112, 4)
(38, 4)
(112,)
(38,)

Como puede ver, los conjuntos de entrenamiento y prueba se dividen en un 75 %/25 %, ya que hay 112 instancias en el conjunto X_train y 38 instancias en el conjunto X_test.

Algunas otras proporciones divididas son: 80%/20% (muy común), 67%/33% y más raramente 50%/50%.

Establecer cualquiera de estos se reduce a definir uno o ambos argumentos en el método train_test_split():

1
2
3
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8)
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8, test_size=0.2)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)

Las tres divisiones darían como resultado la misma división de:

1
2
3
4
(120, 4)
(30, 4)
(120,)
(30,)

Creación de un conjunto de validación con train_test_split()

Los conjuntos de validación son realmente útiles durante la capacitación y facilitan significativamente su vida como científico de datos.

Siempre que sea posible, intente utilizar un conjunto de validación.

Sin embargo, no hay una función integrada para extraer un conjunto de validación de un conjunto de entrenamiento, ya que esto se reduce a dividirlo como antes. ¿Por qué no usar el mismo método train_test_split()?

Reutilicémoslo para tener en nuestras manos un conjunto de validación, tomando el 10% de los datos del conjunto de entrenamiento:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

X, y = load_iris(return_X_y=True)

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.8)

X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, train_size=0.9)

print(X_train.shape)
print(X_test.shape)
print(X_valid.shape)

Esto no creará una división del 70%-20%-10%, ya que estamos dividiendo el 10% del ya dividido X_train, por lo que en realidad estamos terminando con un 72%-20%- 8% dividido aquí:

1
2
3
(108, 4)
(30, 4)
(12, 4)

Para dar cuenta de esto, puede manualmente establecer un número diferente, esperando esto, o puede definir sus proporciones por adelantado y calcular una división actualizada para hacer referencia al tamaño original, en lugar del tamaño ya truncado.

Para dividir los datos proporcionalmente en un conjunto de entrenamiento, prueba y validación, necesitamos configurar el argumento test_size en la segunda llamada de función para:

$$
prueba_s = validación_r/(tren_r+prueba_r)
$$

Carguemos el conjunto de datos de Diabetes, ya que tiene más instancias (debido al redondeo, los conjuntos de datos pequeños a menudo producen divisiones ligeramente diferentes incluso con las mismas proporciones):

1
2
3
4
5
6
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split

X, y = load_diabetes(return_X_y=True)

print(X.shape)
1
(442, 10)

Digamos que apuntamos a una división del 80 %/10 %/10 %: nos gustaría tener instancias 352, 45 y 45 respectivamente. Definamos estas proporciones y dividamos el conjunto de datos en un conjunto de entrenamiento, prueba y validación con la función train_test_split():

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split

train_ratio = 0.80
test_ratio = 0.10
validation_ratio = 0.10

X, y = load_diabetes(return_X_y=True)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_ratio)

X_train, X_valid, y_train, y_valid = train_test_split(X_train, y_train, test_size=validation_ratio/(train_ratio+test_ratio))

print(X_train.shape)
print(X_test.shape)
print(X_valid.shape)

Esto resulta en:

1
2
3
(352, 10)
(45, 10)
(45, 10)

¡Impresionante! Nuestro conjunto de datos se ha dividido con éxito en tres conjuntos, que ahora podemos introducir en un modelo y realizar la validación durante el entrenamiento para ajustar los hiperparámetros.

División estratificada

A veces, hay un número diferente de muestras para cada clase en un conjunto de datos. Digamos, una clase tiene 100 muestras, la segunda tiene 50, la tercera 30, etc. Dividir sin esto en mente crea un problema cuando estás entrenando un modelo de clasificación (aunque los modelos de regresión no sufren de esto ).

Es mejor dividir el conjunto de alguna manera, para que conserve las proporciones de las clases. Esta es una división estratificada.

Afortunadamente, el método train_test_split tiene un argumento llamado stratify que toma una matriz que define el número de muestras por clase, cuando se divide, para permanecer proporcional:

1
2
3
4
5
6
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

X, y = load_iris(return_X_y=True)

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

En muchos casos, simplemente puede usar la matriz y NumPy de su conjunto de datos para una buena matriz dividida estratificada. Esto asegura que su modelo pueda combatir la falta de equilibrio entre las instancias de las clases y se vuelva menos sesgado hacia algunas.

Conclusión

En esta guía, nos familiarizamos con parte de la biblioteca Scikit-Learn y su módulo de “conjuntos de datos”. Ha aprendido qué son los conjuntos de entrenamiento, prueba y validación, dónde se aplican y los beneficios de validar sus modelos.

Hemos echado un vistazo a cómo emplear el método train_test_split() para dividir sus datos en un conjunto de entrenamiento y prueba, así como también cómo separar un conjunto de validación, conservando dinámicamente las proporciones de estos conjuntos. .