DEV Community

Cover image for Usando pipelines para ahorrar tiempo
Leandro Ruiz
Leandro Ruiz

Posted on • Updated on

Usando pipelines para ahorrar tiempo

Existe una forma muy sencilla de ahorrarnos tiempo en la etapa de pre-procesamiento y de ajuste de parámetros para nuestros modelos de aprendizaje automático.

Nota: Por lo que entiendo, en español se le llama tubería, pero como no me siento cómodo hablando de plomería, la llamaré con su nombre en ingles; pipeline.

Ahora sin más, comenzemos:

Lo primero es importar el objeto pipeline de Scikit-Learn:

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import MinMaxScaler
from sklearn.svm import SVC

pipe = Pipeline([('scaler', MinMaxScaler()), ('svm', SVC())])
Enter fullscreen mode Exit fullscreen mode

Aquí hemos creado dos pasos: el primero, llamado "scaler", es una instancia de MinMaxScaler, y el segundo, llamado "svm", es una instancia de SVC. Ahora, podemos entrenar nuestra pipeline, como cualquier otro estimador de Scikit-Learn:

pipe.fit(X_train, y_train)
Enter fullscreen mode Exit fullscreen mode

Usando pipelines con Grid Search

Primero, creamos una grilla de parámetros (parameter grid):

param_grid = {'svm__C': [0.001, 0.01, 0.1, 1, 10, 100],
              'svm__gamma': [0.001, 0.01, 0.1, 1, 10, 100]}
Enter fullscreen mode Exit fullscreen mode

Con esta grilla, usamos GridSearchCV como siempre:

grid = GridSearchCV(pipe, param_grid=param_grid, cv=5) # we call the pipeline as an attribute
grid.fit(X_train, y_train)
print("Best cross-validation accuracy: {:.2f}".format(grid.best_score_))
print("Test set score: {:.2f}".format(grid.score(X_test, y_test)))
print("Best parameters: {}".format(grid.best_params_))
Enter fullscreen mode Exit fullscreen mode

Código completo en este link.


Una mejor froma de hacer pipelines

Usando make_pipeline de Scikit-Learn, es mucho más sencillo crear una pipeline:

from sklearn.pipeline import make_pipeline
# sintaxis estandar
pipe_long = Pipeline([("scaler", MinMaxScaler()), ("svm", SVC(C=100))])
# sintaxis abreviada
pipe_short = make_pipeline(MinMaxScaler(), SVC(C=100))
Enter fullscreen mode Exit fullscreen mode

Accediendo a los atributos de los pasos

En algún momento querrás inspeccionar los atributos de uno de los pasos de la pipeline. Por ejemplo, los coeficientes de un modelo lineal o los componentes extraidos por PCA (Principal Component Analysis). La forma mas facil de acceder a estos pasos en una pipeline es a través del atributo named_steps, que es un diccionario con los nombres de los atributos como estimadores:

from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA

pipe = make_pipeline(StandardScaler(), PCA(n_components=2), StandardScaler())
print("Pipeline steps:\n{}".format(pipe.steps))

pipe.fit(cancer.data)
# extrae los primeros dos componentes principales del paso de "pca"
components = pipe.named_steps['pca'].components_

print("components.shape: {}".format(components.shape))
Enter fullscreen mode Exit fullscreen mode

Accediendo a atributos en una pipeline con Grid Search

Vamos a realizar una grid search en un clasificador de regresión lineal, usando una pipeline y un StandardScaler para escalar los datos antes de pasarlos al clasificador.

from sklearn.linear_model import LogisticRegression

pipe = make_pipeline(StandardScaler(), LogisticRegression(max_iter=1000))
Enter fullscreen mode Exit fullscreen mode

El parametro de la regularización para ajustar en regresión logistica es el parámetro c:

param_grid = {'logisticregression__C': [0.01, 0.1, 1, 10, 100]}
Enter fullscreen mode Exit fullscreen mode

Recuerda: La sintaxis para definir una cuadrícula de parámetros para una pipeline es especificar para cada parámetro el nombre del paso, seguido por __ (doble guion bajo), seguido por el nombre del parámetro. Por ejemplo, para acceder al parámetro gamma de un modelo svm, llamamos a svm__gamma, para el parametro C de un modelo LogisticRegression, llamamos a logisticregression__C.

Divide el dataset y entrena el modelo:

X_train, X_test, y_train, y_test = train_test_split(cancer.data, cancer.target, random_state=4)

grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)
Enter fullscreen mode Exit fullscreen mode

¿Así que cómo accedemos a los coeficientes del mejor modelo de regresión logistica que fue encontrado por GridSearchCV?

print('Best estimator:\n{}'.format(grid.best_estimator_))
Enter fullscreen mode Exit fullscreen mode

En este caso, el best_estimator_ es una pipeline con dos pasos, StandardScaler y LogisticRegression. Para acceder al paso LogisticRegression, podemos usar el atributo named_steps de la pipeline:

print("Logistic regression step:\n".format(grid.best_estimator_.named_steps['logisticregression']))
Enter fullscreen mode Exit fullscreen mode

Ahora que ya tenemos la regresión logística entrenada, podemos acceder a los coeficientes asociados a cada atributo de entrada:

print("Logistic regression coefficients:\n{}".format(grid.best_estimator_.named_steps['logisticregression'].coef_)) 
Enter fullscreen mode Exit fullscreen mode

GitHub Button

Link a GitHub


Obteniendo pasos de pre-procesamiento con Grid-Search

Usando pipelines, podemos encapsular todos los pasos de pre-procesamiento en nuestro proceso de aprendizaje automático es un solo estimador de Scikit-Learn. Otro beneficio de hacer esto es que podemos ajustar los parámetros del pre-procesamiento usando el resultado de una tarea supervisada como regresión o clasificación.

from sklearn.datasets import load_boston
boston = load_boston()
from sklearn.linear_model import Ridge

X_train, X_test, y_train, y_test = train_test_split(
    boston.data, boston.target, random_state=0)

from sklearn.preprocessing import PolynomialFeatures
pipe = make_pipeline(StandardScaler(), PolynomialFeatures(), Ridge())
Enter fullscreen mode Exit fullscreen mode

¿Cómo sabemos que grados de polinomios elegir, o si elegir polinomios o interacciones? Idealmente, queremos seleccionar el parámetro de grado en función del resultado de la clasificación. Usando nuestra pipeline, podemos buscar sobre el parámetro de grado junto con el parámetro alpha o Ridge.
Para hacer esto, definimos una param_grid que contiene ambos, pre-fijados apropiadamente por el nombre de los pasos:

param_grid = {'polynomialfeatures__degree': [1, 2, 3],
              'ridge__alpha': [0.001, 0.01, 0.1, 1, 10, 100]}
Enter fullscreen mode Exit fullscreen mode

Ahora podemos correr de nuevo la grilla de parametros:

grid = GridSearchCV(pipe, param_grid=param_grid, cv=5, n_jobs=-1)
grid.fit(X_train, y_train)
Enter fullscreen mode Exit fullscreen mode

Ahora podemos visualizar los resultados de la validacion cruzada(cross-validation) usando un heat-map:

plt.matshow(grid.cv_results_['mean_test_score'].reshape(3, -1),
            vmin=0, cmap='viridis')
plt.xlabel("ridge__alpha")
plt.ylabel("polynomialfeatures__degree")
plt.xticks(range(len(param_grid['ridge__alpha'])), param_grid['ridge__alpha'])
plt.yticks(range(len(param_grid['polynomialfeatures__degree'])),
           param_grid['polynomialfeatures__degree'])
plt.colorbar()
Enter fullscreen mode Exit fullscreen mode

Usando Grid-Search para saber que modelo usar

Aquí dejo un ejemplo comparando un RandomForestClassifier y un SVC. Sabemos que el SVC quizás necesite datos escalados, así que tambien buscamos sobre usar StandardScaler o no pre-procesar los datos. Para el RandomForestClassifier, ya sabemos que no necesita ningun tipo de pre-procesamiento.
Comenzamos definiendo la pipeline. Aquí, nombramos los pasos explicitamente. Queremos dos pasos, uno de pre-procesamiento y despues el clasificador. Podemos instanciar esto usando SVC y StandardScaler:

pipe = Pipeline([('preprocessing', StandardScaler()), ('classifier', SVC())])
Enter fullscreen mode Exit fullscreen mode

Ahora podemos definir la grilla de parámetros para realizar la busqueda. Queremos que el clasificador sea un RandomForestClassifier o SVC. Como tienen diferentes parámetros a ajustar, y necesitan diferentes pre-procesamientos, podemos hacer una lista de grillas de búsqueda:

from sklearn.ensemble import RandomForestClassifier

param_grid = [
              {'classifier': [SVC()], 'preprocessing': [StandardScaler(), None],
               'classifier__gamma': [0.001, 0.01, 0.1, 1, 10, 100],
               'classifier__C': [0.001, 0.01, 0.1, 1, 10, 100]},
              {'classifier': [RandomForestClassifier (n_estimators=100)],
               'preprocessing': [None], 'classifier__max_features': [1, 2, 3]}]
Enter fullscreen mode Exit fullscreen mode

Ahora instanciamos y corremos la grilla como siempre:

X_train, X_test, y_train, y_test = train_test_split(
    cancer.data, cancer.target, random_state=0)

grid = GridSearchCV(pipe, param_grid, cv=5)
grid.fit(X_train, y_train)

print("Best params:\n{}\n".format(grid.best_params_))
print("Best cross-validation score: {:.2f}".format(grid.best_score_))
print("Test-set score: {:.2f}".format(grid.score(X_test, y_test)))
Enter fullscreen mode Exit fullscreen mode

Y de esta sencilla manera, obtenemos el mejor modelo con sus mejores parámetros incluidos.

Conclusión

Crear pipelines es una gran manera de ahorrar mucho tiempo en nuestros proyectos. Espero que este articulo les haya servido, y si llegaste hasta aquí, muchas gracias. ¡Hasta la proxima!

Top comments (0)