Una breve introduzione alle pipeline di SciKit

Introduzione alle pipeline di SciKit

E perché dovresti iniziare a usarli.

Foto di Sigmund su Unsplash

Hai mai addestrato un modello di machine learning e le tue previsioni sembravano troppo belle per essere vere? Ma poi hai realizzato di avere una perdita di dati tra i dati di addestramento e quelli di test?

O hai avuto molti passaggi di pre-elaborazione per preparare i tuoi dati in modo che fosse difficile trasferire i passaggi di pre-elaborazione dal tuo addestramento del modello in produzione per effettuare previsioni effettive?

O la tua pre-elaborazione diventa confusionaria ed è difficile condividere il tuo codice in modo leggibile e comprensibile?

Allora potresti voler provare il scikit-learn Pipeline. Il Pipeline è una soluzione elegante per impostare il tuo flusso di lavoro per l’addestramento, il test e la produzione di ML, rendendo la tua vita più facile e i tuoi risultati più riproducibili.

Ma cos’è un pipeline, quali sono i vantaggi e come si configura un pipeline? Affronterò queste domande e ti darò alcuni esempi di codice dei blocchi di costruzione. Combinando questi blocchi di costruzione puoi creare pipeline più sofisticate, adatte alle tue esigenze.

Cosa è una Pipeline?

Una pipeline ti permette di assemblare diversi passaggi nel tuo flusso di lavoro ML che trasformano sequenzialmente i tuoi dati prima di passarli a un estimatore. Pertanto, una pipeline può consistere in passaggi di pre-elaborazione, ingegneria delle caratteristiche e selezione delle caratteristiche prima di passare i dati a un estimatore finale per compiti di classificazione o regressione.

Perché dovrei usare una Pipeline?

In generale, l’utilizzo di una pipeline rende la tua vita più facile e accelera lo sviluppo dei tuoi modelli di ML. Questo perché una pipeline

  • porta a un codice più pulito e comprensibile
  • è facile replicare e capire i flussi di dati
  • è più facile da leggere e regolare
  • rende più veloce la preparazione dei dati poiché la pipeline automatizza la preparazione dei dati
  • aiuta a evitare perdite di dati
  • consente l’ottimizzazione degli iperparametri da eseguire su tutti gli estimatori e i parametri nella pipeline contemporaneamente
  • è conveniente poiché devi chiamare solo fit() e predict() una volta per eseguire l’intera pipeline dei tuoi dati

Dopo aver addestrato e ottimizzato il tuo modello e essere soddisfatto dei risultati, puoi facilmente salvare la pipeline addestrata. Quindi, ogni volta che vuoi eseguire il tuo modello, basta caricare la pipeline pre-addestrata e sei pronto per fare qualche inferenza. In questo modo puoi condividere facilmente il tuo modello in modo molto pulito, facile da replicare e capire.

Come configuro una Pipeline?

Configurare una pipeline con scikit-learn è molto semplice e diretto.

La Pipeline di scikit-learn utilizza una lista di coppie chiave-valore che contiene i trasformatori che desideri applicare ai tuoi dati come valori. Le chiavi possono essere scelte arbitrariamente. Le chiavi possono essere utilizzate per accedere ai parametri dei trasformatori, ad esempio, durante l’esecuzione di una ricerca in griglia durante un’ottimizzazione degli iperparametri. Poiché i trasformatori sono memorizzati in una lista, è anche possibile accedere ai trasformatori tramite indicizzazione.

Per adattare i dati alla tua pipeline e fare previsioni, puoi quindi eseguire fit() e predict() come faresti con qualsiasi trasformatore o regressore in scikit-learn.

Una pipeline molto semplice potrebbe apparire così:

from sklearn.impute import SimpleImputerfrom sklearn.pipeline import Pipelinefrom sklearn.preprocessing import MinMaxScalerfrom sklearn.linear_model import LinearRegressionpipeline = Pipeline(   steps=[("imputer", SimpleImputer()),           ("scaler", MinMaxScaler()),           ("regressione", LinearRegression())   ])pipeline.fit(X_train, y_train)y_pred = pipeline.predict(X_test)

scikit-learn, tuttavia, rende la tua vita ancora più semplice se non desideri inserire valori chiave per i tuoi trasformatori. Invece, puoi semplicemente utilizzare la funzione make_pipeline() e scikit-learn imposta i nomi in base al nome della classe del trasformatore.

from sklearn.impute import SimpleImputerfrom sklearn.pipeline import make_pipelinefrom sklearn.preprocessing import MinMaxScalerfrom sklearn.linear_model import LinearRegressionpipeline = make_pipeline(steps=[    SimpleImputer(),     MinMaxScaler(),     LinearRegression()    ])

Ecco fatto. Con questo hai rapidamente configurato una semplice pipeline che puoi iniziare a usare per allenare un modello e eseguire previsioni. Se vuoi dare un’occhiata a come appare la tua pipeline, puoi semplicemente stampare la pipeline e scikit-learn ti mostrerà una vista interattiva della pipeline.

Ma cosa succede se vuoi costruire qualcosa di più complesso e personalizzabile? Ad esempio, gestire valori categorici e numerici in modo diverso, aggiungere caratteristiche o trasformare il valore target.

Niente paura, scikit-learn fornisce funzionalità aggiuntive con le quali puoi creare pipeline più personalizzate e portare le tue pipeline al livello successivo. Queste funzioni sono:

  • ColumnTransformer
  • FeatureUnion
  • TransformedTargetRegressor

Le esaminerò e ti mostrerò esempi su come usarle.

Trasformazione di caratteristiche selezionate

Se hai diversi tipi di caratteristiche, ad esempio continue e categoriche, probabilmente desideri trasformare queste caratteristiche in modo diverso. Ad esempio, scalare le caratteristiche continue mentre esegui la codifica one-hot delle caratteristiche categoriche.

Potresti eseguire questi passaggi di preelaborazione prima di passare le tue caratteristiche alla pipeline. Ma facendolo, non sarai in grado di includere questi passaggi di preelaborazione e i parametri nella tua ricerca degli iperparametri successivamente. Inoltre, includerli nella pipeline rende molto più facile gestire il tuo modello di apprendimento automatico.

Per applicare una trasformazione o addirittura una sequenza di trasformazioni solo a colonne selezionate, puoi utilizzare il ColumnTransformer. L’uso è molto simile a quello di Pipeline, poiché anziché passare una coppia chiave-valore a steps, passiamo semplicemente le stesse coppie a transformers. Possiamo quindi includere il transformer creato come un passaggio nella nostra 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, ["nome_colonna"])    ])

Dato che vogliamo eseguire la trasformazione solo su determinate colonne, dobbiamo passare queste colonne nella pipeline. Inoltre, possiamo far sapere al ColumnTransformer cosa vogliamo fare con le colonne rimanenti. Ad esempio, se desideri mantenere le colonne che non vengono modificate dal transformer, devi impostare remainder su passthrough. In caso contrario, le colonne vengono eliminate. Invece di non fare nulla o eliminare le colonne, puoi anche trasformare le colonne rimanenti passando un transformer.

from sklearn.compose import ColumnTransformerfrom sklearn.preprocessing import MinMaxScaler, OneHotEncodercategorical_transformer = ColumnTransformer( transformers=[("encode", OneHotEncoder(), ["nome_colonna"])], remainder="passthrough")categorical_transformer = ColumnTransformer( transformers=[("encode", OneHotEncoder(), ["nome_colonna"])], remainder=MinMaxScaler())```

Dato che scikit-learn consente l’impilamento di pipeline, potremmo persino passare una pipeline al ColumnTransformer anziché specificare ogni trasformazione che vogliamo fare nel ColumnTransformer stesso.

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, ["nome_colonna"]),   ])pipeline = Pipeline(steps=["preprocessing", preprocessor])

Combina caratteristiche

Ora, sei in grado di eseguire diversi passaggi di preelaborazione su colonne diverse, ma cosa succede se desideri derivare nuove caratteristiche dai dati e aggiungerle al tuo set di caratteristiche?

A tal fine, puoi utilizzare FeatureUnion, che combina oggetti transformer in un nuovo transformer con gli oggetti combinati. L’esecuzione di una pipeline con una FeatureUnion adatta ciascun transformer in modo indipendente e quindi unisce le loro uscite.

Ad esempio, supponiamo di voler aggiungere la media mobile come caratteristica, potremmo farlo così:

from sklearn.compose import FeatureUnionfrom sklearn.pipeline import Pipelinepreprocessor = (   FeatureUnion(     [       ("media_mobile", MediaMobile(finestra=30)),       ("numerico", pipeline_numerico),     ]   ),)pipeline = Pipeline(steps=["preprocessing", preprocessor])

Trasformazione del valore target

Se hai un problema di regressione, a volte può essere utile trasformare il target prima di adattare una regressione.

Puoi includere tale trasformazione utilizzando la classe TransformedTargetRegressor. Con questa classe puoi utilizzare sia trasformatori forniti da scikit-learn come uno scaler MinMax, sia scrivere le tue funzioni di trasformazione.

Un enorme vantaggio del TransformedTargetRegressor è che mappa automaticamente le previsioni nello spazio originale tramite una trasformazione inversa. Quindi, non devi preoccuparti di questo in seguito quando passi dall’allenamento del modello alla creazione di previsioni in produzione.

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)

Creazione delle tue funzioni personalizzate

A volte non è sufficiente utilizzare i metodi di pre-elaborazione forniti da scikit-learn. Questo, tuttavia, non dovrebbe frenarti nell’utilizzo delle Pipeline. Puoi facilmente creare le tue funzioni che puoi poi includere nella pipeline.

A tal fine, è necessario creare una classe che contenga un metodo fit() e transform() in quanto vengono chiamati durante l’esecuzione della pipeline. Tuttavia, questi metodi non devono necessariamente fare nulla. Inoltre, possiamo far ereditare la classe dalle classi BaseEstimator e TransformerMixin di scikit-learn per fornirci alcune funzionalità di base necessarie alla nostra pipeline.

Ad esempio, supponiamo di voler fare previsioni su una serie temporale e desideriamo appiattire tutte le caratteristiche con una media mobile. A tal fine, impostiamo semplicemente una classe con un metodo transform che contiene la parte di appiattimento.

from sklearn.base import BaseEstimator, TransformerMixinfrom sklearn.impute import SimpleImputerfrom sklearn.pipeline import Pipelinefrom sklearn.preprocessing import MinMaxScalerclass MediaMobile(BaseEstimator, TransformerMixin):    def __init__(self, finestra=30):          self.finestra = finestra        def fit(self, X, y=None):          return self        def transform(self, X, y=None):        return X.rolling(window=self.finestra, min_periods=1, center=False).mean()pipeline = Pipeline(   steps=[       ("mm", MediaMobile(finestra=30)),       ("imputer", SimpleImputer()),       ("scaler", MinMaxScaler()),       ("regressor", model),   ])pipeline.fit(X_train, y_train)y_pred = pipeline.predict(X_test)

Cos’altro c’è da sapere?

Il valore di default restituito dai trasformatori in scikit-learn è un array numpy. Ciò può causare problemi nella tua pipeline se desideri applicare una trasformazione solo su determinate caratteristiche nel secondo passaggio della pipeline, ad esempio solo le caratteristiche categoriche.

Tuttavia, per evitare che la tua pipeline si interrompa, puoi modificare il valore di default restituito di tutti i trasformatori in un dataframe specificando

from sklearn import set_configset_config(transform_output = "pandas")

Quando esegui un’ottimizzazione degli iperparametri o controlli i singoli parametri della tua pipeline, può essere utile accedere direttamente ai parametri. Per accedere ai parametri puoi utilizzare la sintassi <stimatore>__<parametro>. Ad esempio, nell’esempio precedente, della media mobile potremmo accedere alla larghezza della finestra del trasformatore MediaMobile chiamando pipeline.set_params(pipeline__mm_finestra=7).

Conclusione

L’utilizzo della Pipeline di scikit-learn può rendere la tua vita molto più facile durante lo sviluppo di nuovi modelli di apprendimento automatico e la configurazione delle fasi di pre-elaborazione. Oltre a offrire molti vantaggi, la creazione di una Pipeline è anche semplice e diretta. Tuttavia, puoi costruire Pipeline sofisticate e personalizzabili di pre-elaborazione in cui solo la tua creatività pone dei limiti.

Se ti è piaciuto questo articolo o hai domande, non esitare a lasciare un commento o a contattarmi. Sono anche interessato alle tue esperienze con la Pipeline di scikit-learn.

Vuoi leggere di più su Pipelines, dai un’occhiata al seguente link:

  • https://scikit-learn.org/stable/modules/compose.html#pipeline