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ícateImplementació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:
- Escalador: Utiliza
StandardScaler
para estandarizar las características, centrando los datos y escalándolos a varianza unitaria. - 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:
- Imputación: Con
SimpleImputer
, se rellenan los valores faltantes utilizando la media. - Escalado: Se aplica
StandardScaler
para estandarizar las características. - Selección de características:
SelectKBest
selecciona las 10 mejores características basadas en ANOVA. - 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
ycross_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 R² 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.
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
Introducción Y Entorno
Introducción E Instalación
Introducción Y Entorno
Introducción Al Preprocesamiento De Datos
Preprocesamiento De Datos
Identificación Y Tratamiento De Valores Faltantes
Preprocesamiento De Datos
Escalado De Datos
Preprocesamiento De Datos
Normalización De Datos
Preprocesamiento De Datos
Codificación De Variables Categóricas
Preprocesamiento De Datos
Ingeniería De Características
Preprocesamiento De Datos
Selección De Características
Preprocesamiento De Datos
Extracción De Características
Preprocesamiento De Datos
Particionamiento De Datos
Preprocesamiento De Datos
Preprocesamiento De Datos Desbalanceados
Preprocesamiento De Datos
Introducción A La Regresión
Regresión
Regresión Lineal
Regresión
Regresión Knn Kneighborsregressor
Regresión
Regresión Svm Con Svr
Regresión
Regresión Con Árboles Decisiontreeregressor
Regresión
Regresión Con Algoritmos De Conjunto
Regresión
Introducción A La Clasificación
Clasificación
Clasificación Con Regresión Logística
Clasificación
Clasificación Knn Kneighborsclassifier
Clasificación
Clasificación Svm Con Svc
Clasificación
Clasificación Con Árboles Decisiontreeclassifier
Clasificación
Clasificación Con Algoritmos De Conjunto
Clasificación
Reducción De La Dimensionalidad Con Pca
Aprendizaje No Supervisado
Clustering Con Kmeans
Aprendizaje No Supervisado
Clustering Jerárquico
Aprendizaje No Supervisado
Clustering De Densidad Con Dbscan
Aprendizaje No Supervisado
Preprocesamiento De Textos Para Nlp
Nlp
Representación De Texto Y Extracción De Características
Nlp
Clasificación De Texto Con Scikit Learn
Nlp
Análisis De Sentimiento
Nlp
Técnicas Avanzadas De Extracción De Características
Nlp
Introducción Al Análisis De Series Temporales
Series Temporales
Preprocesamiento De Datos De Series Temporales
Series Temporales
Ingeniería De Características Para Series Temporales
Series Temporales
Transformación Y Escalado De Series Temporales
Series Temporales
Validación Y Evaluación De Modelos En Series Temporales
Series Temporales
Validación Y Evaluación De Modelos
Validación De Modelos
Técnicas De Validación Cruzada
Validación De Modelos
Métricas De Regresión
Validación De Modelos
Métricas De Clasificación
Validación De Modelos
Ajuste De Hiperparámetros
Validación De Modelos
Introducción A Pipelines
Pipelines Y Despliegue
Creación De Pipelines Básicos
Pipelines Y Despliegue
Preprocesamiento De Datos Con Pipelines
Pipelines Y Despliegue
Pipelines Y Validación Cruzada
Pipelines Y Despliegue
Pipelines Con Columntransformer
Pipelines Y Despliegue
Exportar E Importar Pipelines
Pipelines Y Despliegue
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.