Vai al contenuto principale

Preprocessing in Data Science (Parte 1): centratura, scaling e KNN

Questo articolo spiega l'importanza del preprocessing nella pipeline di machine learning esaminando come centratura e scaling possano migliorare le prestazioni del modello.
· 11 min leggi

Scopri con l'IA

Il preprocessing dei dati è un termine ombrello che copre una serie di operazioni che i data scientist usano per portare i dati in una forma più adatta a ciò che intendono farne. Per esempio, prima di eseguire un'analisi del sentiment su dati di Twitter, potresti voler rimuovere eventuali tag html, spazi bianchi, espandere le abbreviazioni e dividere i tweet in elenchi delle parole che contengono. Quando analizzi dati spaziali potresti scalarli in modo che siano indipendenti dall'unità di misura, cioè in modo che il tuo algoritmo non si preoccupi se le misure originali erano in miglia o centimetri. Tuttavia, il preprocessing dei dati non avviene nel vuoto. Questo per dire che il preprocessing è un mezzo per un fine e non esistono regole rigide: ci sono pratiche standard, come vedremo, e puoi sviluppare un'intuizione su cosa funziona, ma, alla fine, il preprocessing è in genere parte di una pipeline orientata ai risultati e le sue prestazioni vanno valutate nel contesto.

In questo articolo userò l'esempio dello scaling dei dati numerici (dati numerici: dati costituiti da numeri, in contrapposizione a categorie/stringhe; scaling: uso di aritmetica di base per cambiare l'intervallo dei dati; maggiori dettagli a seguire) per dimostrare l'importanza di considerare il preprocessing come parte di una struttura più ampia, la pipeline di machine learning (ML). A tal fine, vedremo un esempio reale in cui lo scaling può migliorare le prestazioni del modello.

Presenterò prima i problemi di classificazione in ML e k-Nearest Neighbors, uno degli algoritmi più semplici usati in questi contesti. Per apprezzare l'importanza dello scaling dei dati numerici in un contesto del genere, dovrò introdurre le misure di performance del modello e i concetti di training set e test set. Vedrai tutti questi concetti e pratiche all'opera con un dataset in cui tento di classificare la qualità del vino rosso. Mi assicurerò anche di collocare il preprocessing nel posto più utile, vicino all'inizio di una pipeline di data science iterativa. Tutti gli esempi qui saranno in Python. Se non conosci Python, puoi dare un'occhiata ai nostri corsi DataCamp qui. Userò le librerie pandas per le esigenze sui dataframe e scikit-learn per quelle di machine learning.

Questa è la prima parte di un tutorial in tre parti:

Parte 2: Centratura, scaling e regressione logistica

Parte 3: Scaling di dati sintetizzati

Una breve introduzione ai problemi di classificazione nel Machine Learning

Classificare ed etichettare le cose nel mondo fenomenico è un'arte antica. Nel IV secolo a.C., Aristotele costruì un sistema di classificazione degli esseri viventi usato per 2.000 anni. Nel mondo moderno, la classificazione è comunemente inquadrata come un compito di machine learning, in particolare un compito di apprendimento supervisionato. Il principio di base dell'apprendimento supervisionato è semplice: abbiamo un mucchio di dati costituiti da variabili predittive e da una variabile target. L'obiettivo del learning supervisionato è costruire un modello che sia "bravo" a prevedere la variabile target, date le variabili predittive. Se la variabile target è costituita da categorie (ad es. "click" o "no", tumore "maligno" o "benigno"), chiamiamo il compito di apprendimento classificazione. In alternativa, se il target è una variabile che varia in continuo (ad es. il prezzo di una casa), si tratta di un compito di regressione.

Un esempio illustrativo aiuterà molto: considera il dataset sulle malattie cardiache, in cui ci sono 75 variabili predittive, come "età", "sesso" e "fumatore o no" e la variabile target si riferisce alla presenza di malattia cardiaca e varia da 0 (nessuna malattia cardiaca) a 4. Gran parte del lavoro su questo dataset si è concentrato sui tentativi di distinguere la presenza della malattia dalla sua assenza. Questa è una task di classificazione. Se cercassi di prevedere il valore effettivo (da 0 a 4) della variabile target, sarebbe un problema di regressione (perché la variabile target è ordinata). Parlerò di regressione nel prossimo post. Qui esaminerò uno degli algoritmi più semplici per i compiti di classificazione, l'algoritmo k-Nearest Neighbors.

k-Nearest Neighbors per la classificazione nel machine learning

Supponiamo di avere dei dati etichettati, per esempio dati che consistono nelle caratteristiche di vini rossi (ad es. contenuto alcolico, densità, quantità di acido citrico, pH, ecc.; queste sono le variabili predittive) con variabile target "Quality" e etichette "good" e "bad". Quindi, date le caratteristiche di un nuovo vino non etichettato, il compito di classificazione è prevederne la "Quality". Quando tutte le variabili predittive sono numeriche (esistono anche modi per gestire il caso categoriale), possiamo considerare ogni riga/vino come un punto in uno spazio n-dimensionale e, in questo caso, k-Nearest Neighbors (k-NN) è un metodo di classificazione concettualmente e computazionalmente semplice: per ciascun nuovo vino non etichettato, calcoliamo, per un certo intero k, i suoi k vicini più prossimi nello spazio n-dimensionale delle variabili predittive. Poi guardiamo le etichette di questi k vicini (cioè "good" o "bad") e assegniamo al nuovo vino l'etichetta con più voti (ad es. se k = 5, 3 vicini votano "good" e 2 votano "bad", allora il nostro modello etichetta il nuovo vino come "good"). Nota che, qui, l'addestramento del modello consiste interamente nel memorizzare i punti dati: non ci sono parametri da adattare!

Una descrizione visiva di k-Nearest Neighbors

Nell'immagine qui sotto c'è un esempio di k-NN in 2D: come classifichi il punto dati al centro? Be', se k=3, lo classificheresti come rosso e, se k=5, come verde.

 
k-nn in 2d example

Implementazione di k-NN in Python (scikit-learn)

Vediamo ora un esempio di k-NN in azione. Per farlo, daremo un'occhiata al dataset sulla qualità del vino: lo importeremo in un dataframe pandas e poi tracceremo gli istogrammi delle variabili predittive per farci un'idea dei dati.
import pandas as pd%matplotlib inlineimport matplotlib.pyplot as pltplt.style.use('ggplot')df = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv ' , sep = ';')X = df.drop('quality' , 1).values # drop target variabley1 = df['quality'].valuespd.DataFrame.hist(df, figsize = [15,15]);
bar charts

Notiamo innanzitutto l'intervallo delle variabili predittive: "free sulfur dioxide" varia da 0 a ~70 e "volatile acidity" da ~0 a ~1,2. Più nello specifico, la prima ha un intervallo di 2 ordini di grandezza più ampio rispetto alla seconda. Qualsiasi algoritmo, come k-NN, che tenga conto della distanza tra i punti dati, potrebbe quindi concentrarsi in modo eccessivo e ingiusto su variabili con un intervallo più ampio, come "free sulfur dioxide", una variabile che, per quanto ne sappiamo, potrebbe contenere solo rumore. Questo motiva lo scaling dei nostri dati, a cui arriveremo tra poco.

Ora la variabile target è una valutazione di "Quality" del vino e varia da 3 a 8. Per semplicità espositiva, trasformiamola in una variabile a due categorie composta da "good" (rating > 5) e "bad" (rating <= 5). Tracceremo anche gli istogrammi di entrambe le formulazioni della variabile target per farci un'idea di cosa stia succedendo.

y = y1 <= 5 # is the rating <= 5?# plot histograms of original target variable# and aggregated target variableplt.figure(figsize=(20,5));plt.subplot(1, 2, 1 );plt.hist(y1);plt.xlabel('original target value')plt.ylabel('count')plt.subplot(1, 2, 2);plt.hist(y)plt.xlabel('aggregated target value')plt.show()
bar charts
Ora siamo quasi pronti per eseguire k-Nearest Neighbors. Prima, però, se vogliamo confrontare come si comporta il nostro modello con e senza preprocessing, dobbiamo capire come misurare la "bontà" di un modello:

k-Nearest Neighbors: quanto bene funziona?

Esistono diverse misure di performance per le sfide di classificazione. È fondamentale rendersi conto che la scelta della misura di performance è profondamente specifica al dominio e alla domanda. Nel caso di un dataset con classi bilanciate (dove tutti i valori target sono ~ ugualmente rappresentati), i data scientist guardano comunemente all'accuratezza come misura di performance. Infatti, come vedremo, l'accuratezza è il metodo di scoring predefinito sia per k-Nearest Neighbors sia per la regressione logistica in scikit-learn. Cos'è l'accuratezza? È semplicemente il numero di previsioni corrette diviso per il numero totale di previsioni:
$$\text{Accuracy}=\frac{\text{Number of Correct Predictions}}{\text{Total Number of Predictions}}.$$

Nota: l'accuratezza può anche essere definita in termini di matrice di confusione ed è comunemente definita per problemi di classificazione binaria in termini di veri positivi e falsi negativi; altre misure comuni delle prestazioni di un modello derivabili dalla matrice di confusione sono la precision, il numero di veri positivi diviso per il numero di veri e falsi positivi, e il recall, il numero di veri positivi diviso per il numero di veri positivi più il numero di falsi negativi; un'altra misura, la F1-score, è la media armonica di precision e recall. Vedi machine learning mastery per una buona esposizione di queste misure; consulta anche le voci di Wikipedia sulla confusion matrix e sull'F1 score.

k-Nearest Neighbors: performance in pratica e train test split

Va bene avere una misura di performance come l'accuratezza, ma se adattiamo il modello su tutti i dati che abbiamo, su quale dataset riporteremo l'accuratezza? Ricorda che vogliamo un modello che generalizzi bene su nuovi dati. Quindi, se addestriamo il nostro modello su un dataset D, riportare l'accuratezza del modello sugli stessi dati D può far sembrare che si comporti meglio di quanto faccia in realtà. Questo è noto come overfitting. Per contrastare questo problema, i data scientist addestrano comunemente il loro modello su un sottoinsieme dei dati, chiamato training set e valutano le sue prestazioni sul resto dei dati, il test set. È proprio quello che faremo qui! Una regola pratica generale è usare circa l'80% dei dati per il training e il 20% per il test. Suddividiamo ora i nostri dati sulla qualità del vino rosso:
from sklearn.cross_validation import train_test_splitX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)We now build the k-NN model, make predictions on the test set and compare these predictions to the ground truth in order to get a measure of model performance:from sklearn import neighbors, linear_modelknn = neighbors.KNeighborsClassifier(n_neighbors = 5)knn_model_1 = knn.fit(X_train, y_train)print('k-NN accuracy for test set: %f' % knn_model_1.score(X_test, y_test))k-NN accuracy for test set: 0.612500

Vale la pena ribadire che il metodo di scoring predefinito per k-NN in scikit-learn è la
accuracy. Un'accuratezza del 61% non è eccezionale ma, per un modello pronto all'uso senza alcun preprocessing, non è nemmeno pessima. Per esplorare una varietà di altre metriche, possiamo anche usare il classification report di scikit-learn:

from sklearn.metrics import classification_reporty_true, y_pred = y_test, knn_model_1.predict(X_test)print(classification_report(y_true, y_pred))
 
             precision    recall  f1-score   support      False       0.66      0.64      0.65       179       True       0.56      0.57      0.57       141avg / total       0.61      0.61      0.61       320

Ora introdurremo lo scaling e la centratura, i metodi più basilari di preprocessing dei dati numerici, e vedremo se e come influenzano le prestazioni del nostro modello.

La meccanica del preprocessing: scaling e centratura

Prima di eseguire un modello, come una regressione (prevedere una variabile continua) o una classificazione (prevedere una variabile discreta), sui dati, quasi sempre conviene fare un po' di preprocessing. Per le variabili numeriche, è comune normalizzare o standardizzare i dati. Cosa significano questi termini?

La normalizzazione significa semplicemente scalare un dataset in modo che il suo minimo sia 0 e il massimo 1. Per ottenere questo trasformiamo ogni punto dato x in

$$x_{normalized} = \frac{x-x_{min}}{x_{max}-x_{min}}.$$

La standardizzazione è leggermente diversa; il suo scopo è centrare i dati attorno a 0 e scalarli rispetto alla deviazione standard:

$$x_{standardized} = \frac{x-\mu}{\sigma},$$

dove $\mu$ e $\sigma$ sono rispettivamente la media e la deviazione standard del dataset. Nota innanzitutto che queste trasformazioni cambiano semplicemente l'intervallo dei dati e non la distribuzione. Potresti successivamente voler usare altre trasformazioni, come una trasformazione logaritmica o una trasformazione di Box-Cox, per rendere i tuoi dati più gaussiani (a campana). Ma prima di proseguire, è importante porsi le seguenti domande: perché scaliamo i dati? Ci sono momenti in cui è più opportuno farlo rispetto ad altri? Per esempio, è più importante nei problemi di classificazione che in quelli di regressione?

Approfondiamo prima le sfide di classificazione e vediamo come lo scaling influisce sulle prestazioni di k-Nearest Neighbors:

Preprocessing: scaling in pratica

Qui sotto (i) scalo i dati, (ii) uso k-Nearest Neighbors e (iii) verifico le prestazioni del modello. Userò la funzione scale di scikit-learn, che standardizza tutte le feature (colonne) nell'array che le viene passato.

from sklearn.preprocessing import scaleXs = scale(X)from sklearn.cross_validation import train_test_splitXs_train, Xs_test, y_train, y_test = train_test_split(Xs, y, test_size=0.2, random_state=42)knn_model_2 = knn.fit(Xs_train, y_train)print('k-NN score for test set: %f' % knn_model_2.score(Xs_test, y_test))print('k-NN score for training set: %f' % knn_model_2.score(Xs_train, y_train))y_true, y_pred = y_test, knn_model_2.predict(Xs_test)print(classification_report(y_true, y_pred))
 
 k-NN score for test set: 0.712500k-NN score for training set: 0.814699             precision    recall  f1-score   support      False       0.72      0.79      0.75       179       True       0.70      0.62      0.65       141avg / total       0.71      0.71      0.71       320

Tutte queste misure sono migliorate di 0,1, cioè un miglioramento del 16% e significativo! Come accennato sopra, prima dello scaling c'erano diverse variabili predittive con intervalli di ordini di grandezza diversi, il che significa che una o due avrebbero potuto dominare nel contesto di un algoritmo come k-NN. I due motivi principali per eseguire lo scaling dei dati sono

  1. Le tue variabili predittive possono avere intervalli significativamente diversi e, in certe situazioni, come quando si implementa k-NN, questo va mitigato in modo che alcune feature non dominino l'algoritmo;
  2. Vuoi che le feature siano indipendenti dall'unità di misura, cioè non legate alla scala della misurazione coinvolta: per esempio, tu potresti avere una feature misurata in metri e io la stessa feature espressa in centimetri. Se entrambi scaliamo i rispettivi dati, questa feature risulterà la stessa per entrambi.

Abbiamo visto il ruolo essenziale occupato dal preprocessing, nelle sue forme di scaling e centratura, all'interno della pipeline di data science, e lo abbiamo fatto per promuovere un approccio olistico alle sfide del machine learning. In futuri articoli, spero di estendere questa discussione ad altri tipi di preprocessing, come le trasformazioni dei dati numerici e il preprocessing dei dati categoriali, entrambi aspetti essenziali del toolkit di qualsiasi data scientist. Prima di passare oltre, nel prossimo articolo esplorerò il ruolo dello scaling nei metodi di regressione applicati alla classificazione. In particolare, esaminerò la regressione logistica e vedrai che il risultato è molto diverso da quello che hai appena visto nel contesto di k-Nearest Neighbors.

Nella finestra interattiva qui sotto puoi sperimentare direttamente con i dati. Inizia modificando la variabile n_neig, che è il numero di vicini da usare nell'algoritmo k-NN. Puoi anche scalare i dati impostando sc = True, se lo desideri. Quindi esegui l'intero script per riportare l'accuratezza del modello, insieme a un classification report.

import pandas as pdimport matplotlib.pyplot as pltfrom sklearn.cross_validation import train_test_splitfrom sklearn import neighborsfrom sklearn.preprocessing import scalefrom sklearn.metrics import classification_report # Set the the number of neighbors for k-NNn_neig = 5# Set sc = True if you want to scale your featuressc = False# Load datadf = pd.read_csv('http://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv ' , sep = ';')X = df.drop('quality' , 1).values# drop target variable# Here we scale, if desiredif sc == True: X = scale(X)# Target valuey1 = df['quality'].values# original target variabley = y1 <= 5# new target variable: is the rating <= 5?# Split the data into a test set and a training setX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# Train k-NN model and print performance on the test setknn = neighbors.KNeighborsClassifier(n_neighbors = n_neig)knn_model = knn.fit(X_train, y_train)y_true, y_pred = y_test, knn_model.predict(X_test)print('k-NN accuracy for test set: %f' % knn_model.score(X_test, y_test))print(classification_report(y_true, y_pred))

Dizionario

Supervised learning: Il compito di inferire una variabile target a partire da variabili predittive. Per esempio, inferire la variabile target "presenza di malattia cardiaca" da variabili predittive come "età", "sesso" e "stato di fumatore".

Classification task: Un compito di apprendimento supervisionato è una classification task se la variabile target è costituita da categorie (ad es. "click" o "no", tumore "maligno" o "benigno").

Regression task: Un compito di apprendimento supervisionato è una regression task se la variabile target è una variabile che varia in continuo (ad es. prezzo di una casa) o una variabile categoriale ordinata come "valutazione della qualità del vino".

k-Nearest Neighbors: Un algoritmo per classification task, in cui a un punto dato viene assegnata l'etichetta decisa dalla maggioranza dei suoi k vicini più prossimi.

Preprocessing: Qualsiasi numero di operazioni che i data scientist usano per portare i dati in una forma più adatta a ciò che intendono farne. Per esempio, prima di eseguire un'analisi del sentiment su dati di Twitter, potresti voler rimuovere eventuali tag html, spazi bianchi, espandere le abbreviazioni e dividere i tweet in elenchi delle parole che contengono.

Centratura e scaling: Entrambi sono forme di preprocessing dei dati numerici, cioè dati costituiti da numeri, in contrapposizione a categorie o stringhe, per esempio; centrare una variabile significa sottrarre la media della variabile da ogni punto dato in modo che la media della nuova variabile sia 0; scalare una variabile significa moltiplicare ogni punto dato per una costante al fine di modificare l'intervallo dei dati. Vedi il corpo dell'articolo per l'importanza di questi passaggi, con esempi.

Questo articolo è stato generato da un Jupyter notebook. Puoi scaricare il notebook qui.

Argomenti

Correlato