scikit-learn

ScikitLearn

Tutorial ScikitLearn: Creación de pipelines básicos

Aprende a implementar Pipelines en Scikit-Learn. Optimiza el flujo de trabajo en aprendizaje automático mediante cadenas de transformaciones y modelos eficientes.

Aprende ScikitLearn GRATIS y certifícate

Implementación de un Pipeline simple

Para comenzar a utilizar Pipelines en Scikit-Learn, es fundamental entender cómo implementarlos en un ejemplo práctico sencillo. Los Pipelines permiten encadenar múltiples transformaciones y modelos en una estructura única, facilitando el flujo de trabajo en proyectos de aprendizaje automático.

A continuación, se presenta un ejemplo de cómo crear un Pipeline que incluye un escalado de características y una regresión lineal. Utilizaremos el conjunto de datos load_diabetes proporcionado por Scikit-Learn.

from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from joblib import Memory

# Habilitar caching
memory = Memory(location='./pipeline_cache', verbose=0)

# Cargar el conjunto de datos
X, y = load_diabetes(return_X_y=True)

# Dividir los datos en conjuntos de entrenamiento y prueba
X_entrenamiento, X_prueba, y_entrenamiento, y_prueba = train_test_split(
    X, y, test_size=0.2, random_state=42)

# Crear el Pipeline
pipeline = Pipeline(steps=[
    ('escalador', StandardScaler()),
    ('regresor', LinearRegression())
], memory=memory)

# Entrenar el Pipeline
pipeline.fit(X_entrenamiento, y_entrenamiento)

# Realizar predicciones
predicciones = pipeline.predict(X_prueba)

# Mostrar las primeras predicciones
print(predicciones[:5])

En este ejemplo, el Pipeline consta de dos pasos:

  1. Escalador: Utiliza StandardScaler para estandarizar las características, centrando los datos y escalándolos a varianza unitaria.
  2. Regresor: Aplica LinearRegression para ajustar un modelo de regresión lineal sobre los datos escalados.

Al llamar al método fit del Pipeline, se ejecutan secuencialmente los pasos definidos: primero se ajusta el escalador con los datos de entrenamiento y luego se entrena el modelo de regresión. Esto garantiza que el preprocesamiento se aplique correctamente durante el entrenamiento y la predicción.

Es importante destacar que el uso de Pipelines ayuda a prevenir fugas de datos, ya que las transformaciones aprendidas se aplican exclusivamente en el conjunto de entrenamiento y luego se reutilizan en el conjunto de prueba sin recalcularse.

Además, podemos evaluar el rendimiento del modelo utilizando métricas como el error cuadrático medio (MSE):

from sklearn.metrics import mean_squared_error

# Calcular el MSE
mse = mean_squared_error(y_prueba, predicciones)
print(f"Error cuadrático medio: {mse:.2f}")

El Pipeline facilita también el acceso a los componentes individuales. Por ejemplo, para obtener los coeficientes de la regresión lineal:

# Acceder al modelo regresor entrenado
modelo_regresor = pipeline.named_steps['regresor']

# Obtener los coeficientes del modelo
coeficientes = modelo_regresor.coef_
print(coeficientes)

Este enfoque modular permite una mayor flexibilidad y claridad en el código, especialmente en proyectos más complejos donde se encadenan múltiples transformaciones y modelos. Al utilizar Pipelines, se simplifica el proceso de entrenamiento y evaluación, asegurando un flujo de trabajo coherente y reproducible.

Encadenamiento de transformaciones y modelos

Para abordar problemas más complejos, es común encadenar transformaciones y modelos dentro de un Pipeline. Esto permite aplicar múltiples etapas de preprocesamiento antes de entrenar el modelo final, asegurando un flujo de trabajo coherente y reproducible.

Al encadenar varias transformaciones, todas las operaciones se ejecutan en secuencia, manteniendo el orden adecuado de procesamiento. Por ejemplo, es habitual combinar imputación de valores faltantes, escalado y selección de características antes del modelado.

A continuación, se presenta un ejemplo de cómo crear un Pipeline que incluye varias transformaciones y un modelo de clasificación. Utilizaremos el conjunto de datos load_breast_cancer de Scikit-Learn para ilustrar este proceso.

from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from joblib import Memory

# Habilitar caching
memory = Memory(location='./pipeline_cache', verbose=0)

# Cargar el conjunto de datos
X, y = load_breast_cancer(return_X_y=True)

# Dividir los datos en conjuntos de entrenamiento y prueba
X_entrenamiento, X_prueba, y_entrenamiento, y_prueba = train_test_split(
    X, y, test_size=0.2, random_state=42)

# Crear el Pipeline
pipeline = Pipeline(steps=[
    ('imputacion', SimpleImputer(strategy='mean')),
    ('escalado', StandardScaler()),
    ('seleccion', SelectKBest(score_func=f_classif, k=10)),
    ('clasificador', LogisticRegression())
], memory=memory)

# Entrenar el Pipeline
pipeline.fit(X_entrenamiento, y_entrenamiento)

# Realizar predicciones
predicciones = pipeline.predict(X_prueba)

# Evaluar el rendimiento
precision = accuracy_score(y_prueba, predicciones)
print(f"Precisión del modelo: {precision:.2f}")

En este Pipeline, se incluyen las siguientes etapas:

  1. Imputación: Con SimpleImputer, se rellenan los valores faltantes utilizando la media.
  2. Escalado: Se aplica StandardScaler para estandarizar las características.
  3. Selección de características: SelectKBest selecciona las 10 mejores características basadas en ANOVA.
  4. Clasificador: Se utiliza LogisticRegression como modelo para la predicción.

Al entrenar el Pipeline, cada transformación se ajusta secuencialmente con los datos de entrenamiento. Durante la predicción, las mismas transformaciones se aplican al conjunto de prueba, garantizando un preprocesamiento consistente.

Encadenar transformaciones y modelos en un Pipeline es fundamental para evitar fugas de información y asegurar que los datos de prueba no influyan en las transformaciones aprendidas. Además, simplifica la gestión y el mantenimiento del código al agrupar todas las etapas en una única estructura.

Es posible incorporar transformadores personalizados en el Pipeline para realizar operaciones específicas. Por ejemplo, se puede crear un transformador que genere nuevas características basadas en combinaciones de las existentes.

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.linear_model import LogisticRegression
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.metrics import accuracy_score
from joblib import Memory
import numpy as np

# Definir el transformador personalizado
class GeneradorDeCaracteristicas(BaseEstimator, TransformerMixin):
    def __init__(self):

        pass

    def fit(self, data, y=None):
        return self 

    def transform(self, data):

        new_feature = data[:, 0] * data[:, 1]
        transformed_data = np.hstack((data, new_feature.reshape(-1, 1)))
        return transformed_data

# Crear el Pipeline actualizado
pipeline = Pipeline(steps=[
    ('imputacion', SimpleImputer(strategy='mean')),
    ('generador', GeneradorDeCaracteristicas()),
    ('escalado', StandardScaler()),
    ('seleccion', SelectKBest(score_func=f_classif, k=11)),
    ('clasificador', LogisticRegression())
], memory=memory)

# Entrenar y evaluar el Pipeline actualizado
pipeline.fit(X_entrenamiento, y_entrenamiento)
predicciones = pipeline.predict(X_prueba)
precision = accuracy_score(y_prueba, predicciones)
print(f"Precisión con transformador personalizado: {precision:.2f}")

El transformador personalizado, GeneradorDeCaracteristicas, crea una nueva característica a partir del producto de las dos primeras. Al incluirlo en el Pipeline, se integra de manera transparente con el resto de las etapas.

Es crucial considerar el orden de las transformaciones en el Pipeline. Por ejemplo, la imputación debe realizarse antes del escalado para manejar correctamente los valores faltantes. Asimismo, la selección de características debe aplicarse después de generar nuevas características o transformarlas.

Al encadenar transformaciones y modelos, el Pipeline se convierte en una herramienta poderosa para gestionar flujos de trabajo complejos. Facilita la experimentación y permite incorporar fácilmente nuevas etapas sin alterar la estructura general del código.

Además, este enfoque es compatible con técnicas avanzadas como la validación cruzada y la búsqueda de hiperparámetros, ya que el Pipeline se comporta como un único estimador. Esto simplifica el uso de funciones como GridSearchCV para optimizar el rendimiento del modelo.

En resumen, el encadenamiento de transformaciones y modelos mediante Pipelines es esencial para construir soluciones de aprendizaje automático robustas y mantenibles. Esta estrategia mejora la organización del código y asegura que todas las etapas del proceso se ejecuten de manera consistente y eficiente.

Uso de Pipeline vs. make_pipeline

En Scikit-Learn, tanto Pipeline como make_pipeline son herramientas esenciales para crear pipelines que encadenan múltiples transformaciones y modelos. Sin embargo, es importante comprender las diferencias entre ambos y cuándo es más apropiado utilizar uno u otro.

La clase Pipeline permite construir un pipeline especificando manualmente los nombres y las etapas a través de una lista de tuplas. Cada tupla contiene un nombre de etapa y un estimador o transformador correspondiente. Este enfoque ofrece un mayor control sobre los nombres de las etapas, lo cual es útil en casos donde se necesita acceder a componentes específicos del pipeline.

Por otro lado, la función make_pipeline es una forma más sencilla y concisa de crear un pipeline sin tener que asignar nombres a cada etapa. Esta función genera automáticamente los nombres de las etapas basándose en las clases de los estimadores proporcionados. Es especialmente útil para pipelines simples donde los nombres de las etapas no son críticos.

A continuación, se presentan ejemplos prácticos que ilustran el uso de Pipeline y make_pipeline.

Uso de Pipeline con nombres personalizados

Primero, crearemos un pipeline utilizando la clase Pipeline, asignando nombres específicos a cada etapa. En este ejemplo, aplicaremos un escalado de datos y luego entrenaremos un modelo de regresión logística sobre el conjunto de datos load_iris.

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
from joblib import Memory

# Habilitar caching
memory = Memory(location='./pipeline_cache', verbose=0)

# Cargar el conjunto de datos
X, y = load_iris(return_X_y=True)

# Dividir en conjuntos de entrenamiento y prueba
X_entrenamiento, X_prueba, y_entrenamiento, y_prueba = train_test_split(
    X, y, test_size=0.2, random_state=42)

# Crear el Pipeline con nombres personalizados
pipeline = Pipeline(steps=[
    ('escalado', StandardScaler()),
    ('clasificador_logistico', LogisticRegression())
], memory=memory)

# Entrenar el Pipeline
pipeline.fit(X_entrenamiento, y_entrenamiento)

# Evaluar el modelo
precision = pipeline.score(X_prueba, y_prueba)
print(f"Precisión del modelo con Pipeline: {precision:.2f}")

En este caso, asignamos los nombres 'escalado' y 'clasificador_logistico' a las etapas correspondientes. Esto nos permite acceder directamente a estas etapas si es necesario, por ejemplo:

# Acceder al clasificador entrenado
clasificador = pipeline.named_steps['clasificador_logistico']

# Ver los coeficientes del modelo
print(clasificador.coef_)

El uso de nombres personalizados es particularmente útil cuando se tienen múltiples transformaciones similares y se requiere diferenciarlas, o cuando se necesita acceder a parámetros específicos durante el ajuste o la predicción.

Uso de make_pipeline para simplificar la construcción

Ahora, utilizaremos make_pipeline para crear el mismo pipeline de manera más compacta. La función automáticamente asigna nombres a cada etapa basándose en las clases de los estimadores en minúsculas.

from sklearn.pipeline import make_pipeline

# Crear el Pipeline utilizando make_pipeline con caching
pipeline_simplificado = make_pipeline(
    StandardScaler(),
    LogisticRegression(),
    memory=memory
)

# Entrenar el Pipeline
pipeline_simplificado.fit(X_entrenamiento, y_entrenamiento)

# Evaluar el modelo
precision = pipeline_simplificado.score(X_prueba, y_prueba)
print(f"Precisión del modelo con make_pipeline: {precision:.2f}")

En este ejemplo, no es necesario asignar nombres a las etapas. Los nombres generados automáticamente pueden ser consultados a través del atributo steps del pipeline:

print(pipeline_simplificado.steps)

Esto mostrará algo similar a:

[('standardscaler', StandardScaler()), ('logisticregression', LogisticRegression())]

Los nombres asignados son las versiones en minúsculas de las clases utilizadas.

¿Cuándo usar Pipeline o make_pipeline?

La elección entre Pipeline y make_pipeline depende de las necesidades específicas del proyecto:

Use Pipeline cuando necesite un mayor control sobre los nombres de las etapas. Esto es útil para:

  • Acceder a transformadores o estimadores específicos por nombre.
  • Evitar conflictos de nombres en pipelines más complejos.
  • Personalizar los nombres para una mayor claridad en el código.

Use make_pipeline para construir pipelines de manera rápida y sencilla cuando:

  • Los nombres de las etapas no son críticos.
  • Se busca reducir la cantidad de código y mejorar la legibilidad.
  • Se está trabajando con pipelines simples o prototipos rápidos.

Es importante tener en cuenta que ambos métodos crean instancias de la clase Pipeline, por lo que, funcionalmente, son equivalentes una vez creados.

Acceso a los componentes del Pipeline

Con Pipeline, puede acceder a los componentes utilizando los nombres personalizados:

# Usando nombres personalizados
transformador = pipeline.named_steps['escalado']

Con make_pipeline, los nombres son generados automáticamente, pero aún puede acceder a las etapas por índice o por nombre:

# Acceso por índice
transformador = pipeline_simplificado.steps[0][1]

# Acceso por nombre generado automáticamente
transformador = pipeline_simplificado.named_steps['standardscaler']

Sin embargo, si se utilizan múltiples instancias de la misma clase en el pipeline, los nombres generados pueden entrar en conflicto. En ese caso, es preferible utilizar Pipeline con nombres personalizados para evitar ambigüedades.

Ejemplo con múltiples transformaciones del mismo tipo

Consideremos un pipeline que aplique dos escalados diferentes antes del modelado. Al utilizar Pipeline, podemos asignar nombres distintos a cada etapa:

from sklearn.preprocessing import StandardScaler, MinMaxScaler

pipeline_multiple = Pipeline(steps=[
    ('escalador_estandar', StandardScaler()),
    ('escalador_minmax', MinMaxScaler()),
    ('clasificador', LogisticRegression()),
], memory=memory)

# Entrenar el Pipeline
pipeline_multiple.fit(X_entrenamiento, y_entrenamiento)

# Evaluar el modelo
precision = pipeline_multiple.score(X_prueba, y_prueba)
print(f"Precisión del modelo con múltiples escaladores: {precision:.2f}")

Con make_pipeline, los nombres generados serían los mismos para ambas instancias de escalado si se usan clases idénticas, lo que causaría conflictos:

pipeline_conflictivo = make_pipeline(
    StandardScaler(),
    StandardScaler(),
    LogisticRegression(),
    memory=memory
)

Al consultar los nombres generados:

print(pipeline_conflictivo.steps)

Obtenemos:

[('standardscaler', StandardScaler()), ('standardscaler', StandardScaler()), ('logisticregression', LogisticRegression())]

Esto causaría un error, ya que los nombres de las etapas deben ser únicos dentro del pipeline. Por lo tanto, en situaciones donde se repiten clases de transformadores, es necesario utilizar Pipeline con nombres personalizados para evitar este tipo de conflictos.

Consideraciones adicionales

  • Depuración y seguimiento: Al utilizar Pipeline con nombres personalizados, es más fácil depurar y rastrear problemas, ya que los nombres asignados pueden reflejar el propósito de cada etapa.
  • Documentación del código: Nombres claros y descriptivos mejoran la legibilidad y mantenibilidad del código, lo cual es crucial en proyectos colaborativos.
  • Interoperabilidad: Ambos métodos son compatibles con otras herramientas de Scikit-Learn, como GridSearchCV y cross_val_score, permitiendo una integración fluida en el proceso de modelado.

Entender las diferencias entre Pipeline y make_pipeline y saber cuándo utilizar cada uno optimiza el flujo de trabajo y mejora la calidad del código en proyectos de aprendizaje automático.

Ejemplo práctico con datos reales

Para ilustrar el uso de Pipelines en un contexto real, desarrollaremos un ejemplo práctico utilizando el conjunto de datos California Housing. Este dataset contiene información sobre viviendas en California y se utiliza comúnmente para predecir el valor medio de las casas en distintas áreas.

En este caso, crearemos un Pipeline que incluye varias etapas de preprocesamiento y un modelo de regresión. Aplicaremos imputación de valores faltantes, escalado de características y entrenaremos un modelo de regresión lineal. Finalmente, evaluaremos el rendimiento del modelo utilizando métricas apropiadas.

Importación de librerías y carga de datos

Comenzamos importando las librerías necesarias y cargando el conjunto de datos.

import numpy as np
import pandas as pd
from sklearn.datasets import fetch_california_housing
from sklearn.model_selection import train_test_split
from joblib import Memory

# Habilitar caching
memory = Memory(location='./pipeline_cache', verbose=0)

# Cargar el conjunto de datos California Housing
datos = fetch_california_housing(as_frame=True)
X = datos.data
y = datos.target

Exploración inicial de los datos

Es importante realizar una exploración inicial para identificar posibles valores faltantes y entender la estructura de los datos.

# Verificar si existen valores faltantes
print(X.isnull().sum())

Si no se detectan valores faltantes, introducimos algunos de forma artificial para simular un caso más realista.

# Introducir valores faltantes de forma aleatoria
X = X.copy()  # Asegurarte de trabajar con una copia explícita
np.random.seed(42)
for col in X.columns:
    X.loc[X.sample(frac=0.01).index, col] = np.nan

# Comprobar los nuevos valores faltantes
print(X.isnull().sum())

División de los datos

Dividimos el conjunto de datos en entrenamiento y prueba para poder evaluar el modelo posteriormente.

# Dividir los datos en conjuntos de entrenamiento y prueba
X_entreno, X_prueba, y_entreno, y_prueba = train_test_split(
    X, y, test_size=0.2, random_state=42)

Creación del Pipeline

Ahora creamos el Pipeline que manejará todo el flujo de preprocesamiento y modelado.

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LinearRegression

# Definir el Pipeline
pipeline = Pipeline(steps=[
    ('imputacion', SimpleImputer(strategy='median')),
    ('escalado', StandardScaler()),
    ('modelo', LinearRegression())
], memory=memory)

En este Pipeline, primero se imputan los valores faltantes utilizando la mediana, luego se escalan las características y finalmente se entrena un modelo de regresión lineal.

Entrenamiento del modelo

Procedemos a entrenar el modelo utilizando el Pipeline creado.

# Entrenar el Pipeline
pipeline.fit(X_entreno, y_entreno)

Evaluación del modelo

Evaluamos el rendimiento del modelo en el conjunto de prueba utilizando métricas como el error cuadrático medio y el coeficiente de determinación.

from sklearn.metrics import mean_squared_error, r2_score

# Realizar predicciones
y_pred = pipeline.predict(X_prueba)

# Calcular el error cuadrático medio (MSE)
mse = mean_squared_error(y_prueba, y_pred)
print(f"Error cuadrático medio (MSE): {mse:.2f}")

# Calcular la raíz del MSE (RMSE)
rmse = np.sqrt(mse)
print(f"Raíz del MSE (RMSE): {rmse:.2f}")

# Calcular el coeficiente de determinación (R²)
r2 = r2_score(y_prueba, y_pred)
print(f"Coeficiente de determinación (R²): {r2:.2f}")

El RMSE nos indica el promedio de error de nuestras predicciones, y el muestra qué proporción de la variabilidad de la variable objetivo es explicada por el modelo.

Incorporación de selección de características

Podemos mejorar el modelo añadiendo una etapa de selección de características al Pipeline.

from sklearn.feature_selection import SelectKBest, f_regression

# Actualizar el Pipeline para incluir la selección de características
pipeline = Pipeline(steps=[
    ('imputacion', SimpleImputer(strategy='median')),
    ('escalado', StandardScaler()),
    ('seleccion', SelectKBest(score_func=f_regression, k=6)),
    ('modelo', LinearRegression())
], memory=memory)

# Reentrenar el Pipeline
pipeline.fit(X_entreno, y_entreno)

# Reevaluar el modelo
y_pred = pipeline.predict(X_prueba)
mse = mean_squared_error(y_prueba, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_prueba, y_pred)

print(f"MSE tras selección de características: {mse:.2f}")
print(f"RMSE tras selección de características: {rmse:.2f}")
print(f"R² tras selección de características: {r2:.2f}")

Al reducir el número de características, podemos reducir el sobreajuste y mejorar la generalización del modelo.

Uso de un modelo más complejo

Si necesitamos un modelo más potente, podemos reemplazar la regresión lineal por un Random Forest Regressor.

from sklearn.ensemble import RandomForestRegressor

# Actualizar el Pipeline con RandomForestRegressor
pipeline = Pipeline(steps=[
    ('imputacion', SimpleImputer(strategy='median')),
    ('escalado', StandardScaler()),
    ('seleccion', SelectKBest(score_func=f_regression, k=6)),
    ('modelo', RandomForestRegressor(n_estimators=100, random_state=42))
], memory=memory)

# Reentrenar el Pipeline
pipeline.fit(X_entreno, y_entreno)

# Reevaluar el modelo
y_pred = pipeline.predict(X_prueba)
mse = mean_squared_error(y_prueba, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_prueba, y_pred)

print(f"MSE con Random Forest: {mse:.2f}")
print(f"RMSE con Random Forest: {rmse:.2f}")
print(f"R² con Random Forest: {r2:.2f}")

El uso de modelos de conjunto como Random Forest puede capturar relaciones más complejas en los datos.

Optimización de hiperparámetros con GridSearchCV

Podemos utilizar GridSearchCV para optimizar los hiperparámetros del modelo dentro del Pipeline.

from sklearn.model_selection import GridSearchCV

# Definir el espacio de hiperparámetros
param_grid = {
    'modelo__n_estimators': [50, 100, 200],
    'modelo__max_depth': [None, 10, 20],
    'seleccion__k': [4, 6, 8]
}

# Configurar GridSearchCV
grid_search = GridSearchCV(pipeline, param_grid, cv=3, scoring='neg_mean_squared_error')

# Ejecutar la búsqueda
grid_search.fit(X_entreno, y_entreno)

# Mejor modelo encontrado
mejor_modelo = grid_search.best_estimator_

# Evaluar el mejor modelo
y_pred = mejor_modelo.predict(X_prueba)
mse = mean_squared_error(y_prueba, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_prueba, y_pred)

print(f"MSE tras optimización: {mse:.2f}")
print(f"RMSE tras optimización: {rmse:.2f}")
print(f"R² tras optimización: {r2:.2f}")

La optimización de hiperparámetros puede mejorar significativamente el rendimiento del modelo al encontrar la combinación óptima de parámetros.

Guardado y carga del Pipeline entrenado

Es posible guardar el Pipeline entrenado para reutilizarlo en futuras predicciones sin necesidad de reentrenar.

import joblib

# Guardar el Pipeline entrenado
joblib.dump(mejor_modelo, 'mejor_pipeline.pkl')

# Cargar el Pipeline guardado
pipeline_cargado = joblib.load('mejor_pipeline.pkl')

# Realizar predicciones con el Pipeline cargado
nuevas_predicciones = pipeline_cargado.predict(X_prueba)

El guardar el Pipeline completo asegura que todas las etapas de preprocesamiento y modelado se conserven, lo cual es esencial para un despliegue eficiente.

Evaluación y predicción con Pipelines

La integración de Pipelines en el proceso de evaluación y predicción es fundamental para obtener modelos confiables y reproducibles. Los Pipelines facilitan la aplicación coherente de transformaciones y modelos durante la validación y la generación de predicciones, asegurando que el preprocesamiento se realice de forma idéntica en cada iteración.

Validación cruzada con Pipelines

El uso de validación cruzada es esencial para evaluar el rendimiento de un modelo de manera robusta. Al emplear Pipelines, podemos integrar fácilmente técnicas de validación cruzada sin riesgo de fuga de datos (data leakage), ya que las transformaciones se aplican únicamente a los conjuntos de entrenamiento en cada partición.

Para demostrarlo, utilizaremos el conjunto de datos load_wine y aplicaremos una validación cruzada con cross_val_score.

from sklearn.datasets import load_wine
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from joblib import Memory

# Habilitar caching
memory = Memory(location='./pipeline_cache', verbose=0)

# Cargar el conjunto de datos
X, y = load_wine(return_X_y=True)

# Crear el Pipeline con caching
pipeline = Pipeline(steps=[
    ('escalado', StandardScaler()),
    ('clasificador', LogisticRegression(max_iter=1000))
], memory=memory)

# Realizar validación cruzada
scores = cross_val_score(pipeline, X, y, cv=5, scoring='accuracy')

print(f"Puntuaciones de validación cruzada: {scores}")
print(f"Precisión media: {scores.mean():.2f}")

En este ejemplo, la función cross_val_score realiza una validación cruzada de 5 pliegues (folds) utilizando el Pipeline definido. Al aplicar el escalado y la modelización dentro del Pipeline, nos aseguramos de que el escalado se ajuste únicamente sobre los datos de entrenamiento en cada partición, evitando influir en los datos de prueba.

Uso de GridSearchCV con Pipelines

La optimización de hiperparámetros es esencial para mejorar el rendimiento de los modelos. Con Pipelines, podemos utilizar GridSearchCV para ajustar los parámetros de las diferentes etapas de forma conjunta.

from sklearn.model_selection import GridSearchCV

# Definir el espacio de hiperparámetros
param_grid = {
    'escalado__with_mean': [True, False],
    'clasificador__C': [0.1, 1, 10]
}

# Configurar GridSearchCV con el Pipeline
grid_search = GridSearchCV(pipeline, param_grid, cv=5, scoring='accuracy')

# Ejecutar la búsqueda de hiperparámetros
grid_search.fit(X, y)

print(f"Mejores parámetros: {grid_search.best_params_}")
print(f"Mejor puntuación: {grid_search.best_score_:.2f}")

En este caso, optimizamos los parámetros tanto del escalador como del clasificador. Los nombres de los parámetros se construyen combinando el nombre de la etapa en el Pipeline y el nombre del hiperparámetro, separados por doble guión bajo (__).

Generación de predicciones con el Pipeline optimizado

Una vez ajustado el Pipeline mediante GridSearchCV, podemos utilizar el mejor modelo encontrado para realizar predicciones y evaluar su rendimiento en un conjunto de prueba.

from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report

# Dividir los datos en entrenamiento y prueba
X_entreno, X_prueba, y_entreno, y_prueba = train_test_split(X, y, test_size=0.2, random_state=42)

# Entrenar el mejor modelo del GridSearchCV
mejor_pipeline = grid_search.best_estimator_
mejor_pipeline.fit(X_entreno, y_entreno)

# Realizar predicciones en el conjunto de prueba
y_pred = mejor_pipeline.predict(X_prueba)

# Evaluar el rendimiento
reporte = classification_report(y_prueba, y_pred)
print("Reporte de clasificación:")
print(reporte)

El uso de classification_report proporciona una visión detallada de las métricas de rendimiento, como la precisión, el recall y el f1-score por clase.

Validación cruzada avanzada con cross_validate

La función cross_validate permite calcular múltiples métricas de evaluación simultáneamente y brinda mayor flexibilidad que cross_val_score.

from sklearn.model_selection import cross_validate

# Definir las métricas a evaluar
metricas = ['accuracy', 'precision_macro', 'recall_macro', 'f1_macro']

# Realizar validación cruzada con múltiples métricas
resultados = cross_validate(pipeline, X, y, cv=5, scoring=metricas)

print("Resultados de validación cruzada:")
for clave, valor in resultados.items():
    if 'test' in clave:
        print(f"{clave}: {valor.mean():.2f}")

Este enfoque permite analizar diferentes aspectos del rendimiento del modelo y detectar posibles desequilibrios entre clases.

Predicción probabilística

Algunos modelos, como la regresión logística, permiten obtener probabilidades asociadas a cada clase mediante el método predict_proba. Esto es útil en casos donde se requiere una medida de confianza en las predicciones.

# Obtener las probabilidades de predicción
probabilidades = mejor_pipeline.predict_proba(X_prueba)

# Mostrar las primeras probabilidades
print("Probabilidades de las primeras predicciones:")
print(probabilidades[:5])

Las probabilidades permiten establecer umbrales de decisión personalizados o realizar análisis más detallados en aplicaciones críticas.

Curvas ROC y evaluación con probabilidad

Para evaluar modelos de clasificación de manera más exhaustiva, podemos generar curvas ROC (Receiver Operating Characteristic) y calcular el AUC (Área Bajo la Curva). Esto es especialmente relevante en problemas con desequilibrios en las clases.

from sklearn.metrics import roc_curve, auc
import matplotlib.pyplot as plt

# Binarizar las etiquetas si es necesario
from sklearn.preprocessing import label_binarize
y_prueba_bin = label_binarize(y_prueba, classes=[0, 1, 2])

# Calcular las probabilidades para cada clase
probabilidades = mejor_pipeline.predict_proba(X_prueba)

# Generar la curva ROC para cada clase
for i in range(y_prueba_bin.shape[1]):
    fpr, tpr, _ = roc_curve(y_prueba_bin[:, i], probabilidades[:, i])
    roc_auc = auc(fpr, tpr)
    plt.plot(fpr, tpr, label=f'Clase {i} (AUC = {roc_auc:.2f})')

# Personalizar el gráfico
plt.plot([0, 1], [0, 1], 'k--')
plt.xlabel('Tasa de falsos positivos')
plt.ylabel('Tasa de verdaderos positivos')
plt.title('Curvas ROC por clase')
plt.legend(loc='lower right')
plt.show()

La visualización de las curvas ROC nos ayuda a entender el comportamiento del modelo en términos de sensibilidad y especificidad.

Estrategias de particionamiento personalizado

En ciertos casos, es necesario utilizar estrategias de particionamiento más avanzadas, como la validación cruzada estratificada o particiones basadas en grupos. Podemos integrar estas estrategias con Pipelines mediante el parámetro cv en funciones como cross_val_score y GridSearchCV.

from sklearn.model_selection import StratifiedKFold

# Definir el particionador estratificado
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

# Realizar validación cruzada estratificada con cross_val_score
scores = cross_val_score(pipeline, X, y, cv=skf, scoring='accuracy')

print(f"Puntuaciones estratificadas: {scores}")
print(f"Precisión media estratificada: {scores.mean():.2f}")

El uso de particionadores personalizados garantiza que la distribución de clases se mantenga equilibrada en cada pliegue, lo cual es crucial en conjuntos de datos desbalanceados.

Evaluación con métricas personalizadas

Podemos definir nuestras propias métricas de evaluación y utilizarlas durante la validación mediante la función make_scorer.

from sklearn.metrics import make_scorer, fbeta_score

# Definir una métrica personalizada
fbeta = make_scorer(fbeta_score, beta=0.5, average='macro')

# Realizar validación cruzada con la métrica personalizada
scores = cross_val_score(pipeline, X, y, cv=5, scoring=fbeta)

print(f"Puntuaciones F-beta: {scores}")
print(f"Puntuación F-beta media: {scores.mean():.2f}")

Esto permite adaptar la evaluación a las necesidades específicas del problema, enfatizando ciertas métricas sobre otras.

Aplicación del Pipeline en nuevos datos

Una vez entrenado y evaluado el Pipeline, podemos utilizarlo para realizar predicciones en nuevos datos. Es esencial que los nuevos datos se preprocesen de la misma manera que durante el entrenamiento, lo cual se garantiza al utilizar el mismo Pipeline.

# Supongamos que 'nuevos_datos' es un conjunto de datos sin etiquetar
# nuevos_datos = ...

# Realizar predicciones en los nuevos datos
# predicciones_nuevas = mejor_pipeline.predict(nuevos_datos)

# Obtener probabilidades si es necesario
# probabilidades_nuevas = mejor_pipeline.predict_proba(nuevos_datos)

Al utilizar el Pipeline entrenado, nos aseguramos de que todas las transformaciones y el modelo se apliquen de manera consistente, obteniendo predicciones fiables.

Aprende ScikitLearn GRATIS online

Ejercicios de esta lección Creación de pipelines básicos

Evalúa tus conocimientos de esta lección Creación de pipelines básicos con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Todas las lecciones de ScikitLearn

Accede a todas las lecciones de ScikitLearn y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Aprendizaje Automático

scikit-learn

Introducción Y Entorno

Introducción E Instalación

scikit-learn

Introducción Y Entorno

Introducción Al Preprocesamiento De Datos

scikit-learn

Preprocesamiento De Datos

Identificación Y Tratamiento De Valores Faltantes

scikit-learn

Preprocesamiento De Datos

Escalado De Datos

scikit-learn

Preprocesamiento De Datos

Normalización De Datos

scikit-learn

Preprocesamiento De Datos

Codificación De Variables Categóricas

scikit-learn

Preprocesamiento De Datos

Ingeniería De Características

scikit-learn

Preprocesamiento De Datos

Selección De Características

scikit-learn

Preprocesamiento De Datos

Extracción De Características

scikit-learn

Preprocesamiento De Datos

Particionamiento De Datos

scikit-learn

Preprocesamiento De Datos

Preprocesamiento De Datos Desbalanceados

scikit-learn

Preprocesamiento De Datos

Introducción A La Regresión

scikit-learn

Regresión

Regresión Lineal

scikit-learn

Regresión

Regresión Knn Kneighborsregressor

scikit-learn

Regresión

Regresión Svm Con Svr

scikit-learn

Regresión

Regresión Con Árboles Decisiontreeregressor

scikit-learn

Regresión

Regresión Con Algoritmos De Conjunto

scikit-learn

Regresión

Introducción A La Clasificación

scikit-learn

Clasificación

Clasificación Con Regresión Logística

scikit-learn

Clasificación

Clasificación Knn Kneighborsclassifier

scikit-learn

Clasificación

Clasificación Svm Con Svc

scikit-learn

Clasificación

Clasificación Con Árboles Decisiontreeclassifier

scikit-learn

Clasificación

Clasificación Con Algoritmos De Conjunto

scikit-learn

Clasificación

Reducción De La Dimensionalidad Con Pca

scikit-learn

Aprendizaje No Supervisado

Clustering Con Kmeans

scikit-learn

Aprendizaje No Supervisado

Clustering Jerárquico

scikit-learn

Aprendizaje No Supervisado

Clustering De Densidad Con Dbscan

scikit-learn

Aprendizaje No Supervisado

Preprocesamiento De Textos Para Nlp

scikit-learn

Nlp

Representación De Texto Y Extracción De Características

scikit-learn

Nlp

Clasificación De Texto Con Scikit Learn

scikit-learn

Nlp

Análisis De Sentimiento

scikit-learn

Nlp

Técnicas Avanzadas De Extracción De Características

scikit-learn

Nlp

Introducción Al Análisis De Series Temporales

scikit-learn

Series Temporales

Preprocesamiento De Datos De Series Temporales

scikit-learn

Series Temporales

Ingeniería De Características Para Series Temporales

scikit-learn

Series Temporales

Transformación Y Escalado De Series Temporales

scikit-learn

Series Temporales

Validación Y Evaluación De Modelos En Series Temporales

scikit-learn

Series Temporales

Validación Y Evaluación De Modelos

scikit-learn

Validación De Modelos

Técnicas De Validación Cruzada

scikit-learn

Validación De Modelos

Métricas De Regresión

scikit-learn

Validación De Modelos

Métricas De Clasificación

scikit-learn

Validación De Modelos

Ajuste De Hiperparámetros

scikit-learn

Validación De Modelos

Introducción A Pipelines

scikit-learn

Pipelines Y Despliegue

Creación De Pipelines Básicos

scikit-learn

Pipelines Y Despliegue

Preprocesamiento De Datos Con Pipelines

scikit-learn

Pipelines Y Despliegue

Pipelines Y Validación Cruzada

scikit-learn

Pipelines Y Despliegue

Pipelines Con Columntransformer

scikit-learn

Pipelines Y Despliegue

Exportar E Importar Pipelines

scikit-learn

Pipelines Y Despliegue

Accede GRATIS a ScikitLearn y certifícate

Objetivos de aprendizaje de esta lección

  • Comprender el concepto de Pipeline en Scikit-Learn.
  • Aprender a implementar transformaciones y modelos en un Pipeline.
  • Utilizar Pipelines para prevenir fugas de datos.
  • Optimizar Pipelines mediante GridSearchCV.
  • Aplicar validación cruzada con Pipelines.
  • Incorporar transformadores personalizados en el Pipeline.