Una breve introducción a los pipelines de SciKit
Breve introducción a los pipelines de SciKit
Y por qué deberías empezar a usarlos.
¿Alguna vez has entrenado un modelo de aprendizaje automático y tus predicciones parecían demasiado buenas para ser verdad? ¿Pero luego te diste cuenta de que tenías una fuga de datos entre tus datos de entrenamiento y prueba?
¿O has tenido muchos pasos de preprocesamiento para preparar tus datos de manera que sea difícil transferir los pasos de preprocesamiento desde el entrenamiento de tu modelo hacia la producción para hacer predicciones reales?
¿O tu preprocesamiento se vuelve confuso y es difícil compartir tu código de una manera legible y fácil de entender?
Entonces es posible que desees probar el Pipeline de scikit-learn
. El Pipeline es una solución elegante para configurar tu flujo de trabajo para el entrenamiento, prueba y producción de ML, lo que facilita tu vida y hace que tus resultados sean más reproducibles.
Pero ¿qué es un pipeline, cuáles son los beneficios y cómo se configura un pipeline? Voy a responder a estas preguntas y darte algunos ejemplos de código de los bloques de construcción. Al combinar estos bloques de construcción, puedes construir pipelines más sofisticados, adaptados a tus necesidades.
- Afinamiento eficiente de parámetros de modelos de lenguaje grandes ...
- Conoce AutoGPTQ un paquete de cuantificación de modelos de lenguaje...
- Crea un mejor gráfico de barras con este truco
¿Qué es un Pipeline?
Un pipeline te permite ensamblar varios pasos en tu flujo de trabajo de ML que transforman secuencialmente tus datos antes de pasarlos a un estimador. Por lo tanto, un pipeline puede consistir en pasos de preprocesamiento, ingeniería de características y selección de características antes de pasar los datos a un estimador final para tareas de clasificación o regresión.
¿Por qué debería usar un Pipeline?
En general, usar un pipeline facilita tu vida y acelera el desarrollo de tus modelos de ML. Esto se debe a que un pipeline:
- conduce a un código más limpio y comprensible
- es fácil de replicar y entender los flujos de datos
- es más fácil de leer y ajustar
- hace que la preparación de datos sea más rápida, ya que el pipeline automatiza la preparación de datos
- ayuda a evitar la fuga de datos
- permite la optimización de hiperparámetros para ejecutarse en todos los estimadores y parámetros del pipeline a la vez
- es conveniente, ya que solo tienes que llamar a
fit()
ypredict()
una vez para ejecutar todo tu pipeline de datos
Después de haber entrenado y optimizado tu modelo y estás satisfecho con los resultados, puedes guardar fácilmente el pipeline entrenado. Luego, cada vez que quieras ejecutar tu modelo, simplemente carga el pipeline pre-entrenado y estás listo para hacer algunas inferencias. Con esto, puedes compartir fácilmente tu modelo de una manera muy limpia, que es fácil de replicar y entender.
¿Cómo configuro un Pipeline?
Configurar un pipeline con scikit-learn
es muy simple y directo.
El Pipeline
de scikit-learn
utiliza una lista de pares clave-valor que contiene los transformadores que deseas aplicar en tus datos como valores. Las claves las puedes elegir arbitrariamente. Las claves se pueden utilizar para acceder a los parámetros de los transformadores, por ejemplo, al ejecutar una búsqueda en cuadrícula durante una optimización de hiperparámetros. Como los transformadores se almacenan en una lista, también puedes acceder a los transformadores mediante indexación.
Para ajustar los datos en tu pipeline y hacer predicciones, puedes ejecutar fit()
y predict()
como lo harías con cualquier transformador o regresor en scikit-learn
.
Un pipeline muy simple podría verse así:
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LinearRegression
pipeline = Pipeline(steps=[
("imputer", SimpleImputer()),
("scaler", MinMaxScaler()),
("regression", LinearRegression())
])
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
Sin embargo, scikit-learn
te facilita aún más las cosas si no deseas ingresar valores clave para tus transformadores. En su lugar, puedes utilizar la función make_pipeline()
y scikit-learn
establecerá los nombres basados en el nombre de la clase del transformador.
from sklearn.impute import SimpleImputer
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import MinMaxScaler
from sklearn.linear_model import LinearRegression
pipeline = make_pipeline(steps=[
SimpleImputer(),
MinMaxScaler(),
LinearRegression()
])
¡Eso es todo! Con esto, has configurado rápidamente un pipeline simple que puedes empezar a utilizar para entrenar un modelo y realizar predicciones. Si quieres ver cómo se ve tu pipeline, simplemente puedes imprimirlo y scikit-learn
te muestra una vista interactiva del pipeline.
Pero, ¿qué pasa si quieres construir algo más complejo y personalizable? Por ejemplo, manejar valores categóricos y numéricos de manera diferente, agregar características o transformar el valor objetivo.
No te preocupes, scikit-learn
proporciona funcionalidades adicionales con las que puedes crear pipelines más personalizados y llevar tus pipelines al siguiente nivel. Estas funciones son:
ColumnTransformer
FeatureUnion
TransformedTargetRegressor
Exploraré cada una de ellas y te mostraré ejemplos de cómo utilizarlas.
Transformando características seleccionadas
Si tienes diferentes tipos de características, por ejemplo, continuas y categóricas, probablemente quieras transformar estas características de manera diferente. Por ejemplo, escalar las características continuas mientras codificas las características categóricas en one-hot.
Puedes realizar estos pasos de preprocesamiento antes de pasar tus características al pipeline. Pero al hacerlo, no podrás incluir estos pasos de preprocesamiento y parámetros en tu búsqueda de hiperparámetros más adelante. Además, incluirlos en el pipeline hace que sea mucho más fácil manejar tu modelo de aprendizaje automático.
Para aplicar una transformación, o incluso una secuencia de transformaciones, solo a columnas seleccionadas, puedes usar ColumnTransformer
. Su uso es muy similar a Pipeline
, en lugar de pasar un par clave-valor a steps
, simplemente pasamos los mismos pares a transformers
. Luego, podemos incluir el transformer creado como un paso en nuestro pipeline.
from sklearn.compose import ColumnTransformerfrom sklearn.pipeline import Pipelinefrom sklearn.preprocessing import OneHotEncodercategorical_transformer = ColumnTransformer( transformers=[("encode", OneHotEncoder())])pipeline = Pipeline(steps=[ ("categorical", categorical_transformer, ["col_name"]) ])
Dado que solo queremos aplicar la transformación a ciertas columnas, necesitamos pasar estas columnas al pipeline. Además, podemos indicarle al ColumnTransformer
qué hacer con las columnas restantes. Por ejemplo, si quieres mantener las columnas que no son modificadas por el transformer, debes establecer remainder
en passthrough
. De lo contrario, las columnas se eliminarán. En lugar de no hacer nada o eliminar las columnas, también podrías transformar las columnas restantes pasando un transformer.
from sklearn.compose import ColumnTransformerfrom sklearn.preprocessing import MinMaxScaler, OneHotEncodercategorical_transformer = ColumnTransformer( transformers=[("encode", OneHotEncoder(), ["col_name"])], remainder="passthrough")categorical_transformer = ColumnTransformer( transformers=[("encode", OneHotEncoder(), ["col_name"])], remainder=MinMaxScaler())```
Dado que scikit-learn
permite el apilamiento de Pipelines, incluso podríamos pasar un Pipeline al ColumnTransformer
en lugar de declarar cada transformación que queremos realizar en el ColumnTransformer
en sí mismo.
from sklearn.compose import ColumnTransformerfrom sklearn.impute import SimpleImputerfrom sklearn.pipeline import Pipelinefrom sklearn.preprocessing import MinMaxScaler, OneHotEncodercategorical_transformer = Pipeline(steps=[("encode", OneHotEncoder())])numerical_transformer = Pipeline( steps=[("imputation", SimpleImputer()), ("scaling", MinMaxScaler())])preprocessor = ColumnTransformer( transfomers=[ ("numeric", numerical_transformer), ("categoric", categorical_transformer, ["col_name"]), ])pipeline = Pipeline(steps=["preprocesssing", preprocessor])
Combinando características
Ahora, puedes ejecutar diferentes pasos de preprocesamiento en diferentes columnas, pero ¿qué pasa si quieres derivar nuevas características a partir de los datos y agregarlas a tu conjunto de características?
Para esto, puedes usar FeatureUnion
, que combina objetos transformadores en un nuevo transformador con los objetos combinados. Al ejecutar un pipeline con un FeatureUnion
, se ajusta cada transformador de forma independiente y luego se unen sus salidas.
Por ejemplo, supongamos que queremos agregar la Media Móvil como una característica, podríamos hacer esto:
from sklearn.compose import FeatureUnionfrom sklearn.pipeline import Pipelinepreprocessor = ( FeatureUnion( [ ("moving_Average", MovingAverage(window=30)), ("numerical", numerical_pipeline), ] ),)pipeline = Pipeline(steps=["preprocesssing", preprocessor])
Transformando el valor objetivo
Si tienes un problema de regresión, a veces puede ayudar transformar el objetivo antes de ajustar una regresión.
Puedes incluir dicha transformación utilizando la clase TransformedTargetRegressor
. Con esta clase, puedes usar transformadores proporcionados por scikit-learn
como un escalador MinMax o escribir tus propias funciones de transformación.
Una gran ventaja del TransformedTargetRegressor
es que automáticamente mapea las predicciones de vuelta al espacio original mediante una transformación inversa. Por lo tanto, no necesitas preocuparte por esto más adelante cuando pases del entrenamiento del modelo a hacer predicciones en producción.
from sklearn.compose import TransformedTargetRegressorfrom sklearn.impute import SimpleImputerfrom sklearn.pipeline import Pipelinefrom sklearn.preprocessing import MinMaxScalerregressor = TransformedTargetRegressor( regressor=model, func=np.log1p, inverse_func=np.expm1)pipeline = Pipeline( steps=[ ("imputer", SimpleImputer()), ("scaler", MinMaxScaler()), ("regressor", regressor) ])pipeline.fit(X_train, y_train)y_pred = pipeline.predict(X_test)
Construyendo tus propias funciones personalizadas
A veces no es suficiente utilizar los métodos de preprocesamiento que proporciona scikit-learn
. Sin embargo, esto no debería detenerte al usar Pipelines. Puedes crear fácilmente tus propias funciones que luego puedes incluir en el pipeline.
Para esto, necesitas construir una clase que contenga los métodos fit()
y transform()
ya que se llaman al ejecutar el pipeline. Sin embargo, estos métodos no necesariamente necesitan hacer algo. Además, podemos hacer que la clase herede de BaseEstimator
y TransformerMixin
de scikit-learn
para proporcionarnos alguna funcionalidad básica que nuestro pipeline necesita.
Por ejemplo, supongamos que queremos hacer predicciones en una serie de tiempo y queremos suavizar todas las características mediante un promedio móvil. Para esto, simplemente configuramos una clase con un método transform
que contiene la parte de suavizado.
from sklearn.base import BaseEstimator, TransformerMixinfrom sklearn.impute import SimpleImputerfrom sklearn.pipeline import Pipelinefrom sklearn.preprocessing import MinMaxScalerclass MovingAverage(BaseEstimator, TransformerMixin): def __init__(self, window=30): self.window = window def fit(self, X, y=None): return self def transform(self, X, y=None): return X.rolling(window=self.window, min_periods=1, center=False).mean()pipeline = Pipeline( steps=[ ("ma", MovingAverage(window=30)), ("imputer", SimpleImputer()), ("scaler", MinMaxScaler()), ("regressor", model), ])pipeline.fit(X_train, y_train)y_pred = pipeline.predict(X_test)
¿Qué más hay que saber?
El valor de retorno predeterminado de los transformadores en scikit-learn
es un arreglo de numpy. Esto puede causar problemas en tu pipeline si solo deseas aplicar una transformación en ciertas características en el segundo paso del pipeline, por ejemplo, solo características categóricas.
Sin embargo, para evitar que tu pipeline se rompa, puedes cambiar el valor de retorno predeterminado de todos los transformadores a un dataframe indicando:
from sklearn import set_configset_config(transform_output = "pandas")
Cuando ejecutas una optimización de hiperparámetros o cuando verificas parámetros individuales de tu pipeline, puede ser útil acceder a los parámetros directamente. Para acceder a los parámetros, puedes usar la sintaxis <estimador>__<parámetro>
. Por ejemplo, en el ejemplo anterior del promedio móvil, podríamos acceder al ancho de ventana del transformador MovingAverage llamando a pipeline.set_params(pipeline__ma_window=7)
.
Conclusión
Usar el Pipeline de scikit-learn
puede facilitar mucho tu vida al desarrollar nuevos modelos de ML y configurar los pasos de preprocesamiento. Además de tener muchos beneficios, configurar un Pipeline también es simple y directo. Sin embargo, puedes construir Pipelines de preprocesamiento sofisticados y personalizables en los que solo tu creatividad establece los límites.
Si te gustó este artículo o tienes alguna pregunta, no dudes en dejar un comentario o contactarme. También estoy interesado en tus experiencias con el Pipeline de scikit-learn
.
¿Quieres leer más sobre Pipelines? Echa un vistazo al siguiente enlace:
- https://scikit-learn.org/stable/modules/compose.html#pipeline