
La regressione è uno dei due principali problemi dell’apprendimento supervisionato, la branca del machine learning che vuole insegnare ai computer a risolvere un determinato problema mostrandogli come questo problema è stato già risolto in passato.
Intuitivamente la regressione consente di trovare la relazione tra input e output, in modo da predire output futuri avendo a disposizione solamente l’input.
Ad esempio, avendo a disposizione un set di dati contenente le specifiche di diversi smartphone e il relativo prezzo di listino, potremmo utilizzare la regressione per predire a che prezzo potrà essere venduto un nuovo smartphone basandosi sulle sue specifiche.
Altri esempi molto più comuni sono il provare a predire il valore futuro di un titolo in borsa in base ai valori passati, provare a predire l’affluenza di utenti al blog in base ai contenuti di un articolo che si vuole pubblicare o il numero di nuovi iscritti in base a delle campagne di advertising in pianificazione.
Matematicamente la regressione può essere intesa come il trovare la funzione che meglio approssima la relazione tra la variabile indipendente X (l’input) e la variabile dipendente Y (l’output). Nel caso di una regressione lineare questa funzione è un semplice polinomio.
Graficamente questa relazione lineare può essere rappresentata come una retta che passa il più vicino possibile a tutti i punti costituiti da input X e output Y.
Predire il valore di un’abitazione
Per questo primo esempio utilizzeremo il popolarissimo Boston Housing Dataset, un dataset contenente diverse informazioni riguardo alcune alcune abitazioni nei dintorni di Boston.
Un dataset strutturato si può presentare in diversi formati: CSV, TSV, XML, HTTP, JSON, EXCEL eccetera, in ogni caso questo ha una struttura tabulare.
- Una delle colonne della tabella è il valore che vogliamo addestrare il nostro modello a predire e prende il nome di target.
- Tutte le altre colonne sono proprietà che possiamo potenzialmente usare per creare il nostro modello, purché abbiano una relazione con il target, e vengono chiamate features.
Pandas è una libreria Python sviluppata appositamente per chi lavora con i dati e mette a disposizione una particolare struttura dati chiamata DataFrame che si presta particolarmente bene a contenere dati in forma tabulare.
Carichiamo il Boston Housing Dataset all’interno di un DataFrame utilizzando la funzione read_csv, seguita dal metodo head del DataFrame per vedere le sue prime 5 righe.
import pandas as pd boston = pd.read_csv("https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data", sep='\s+', #le colonne all'interno del file sono separate da un numero variabile di spazi, in questo caso dobbiamo utilizzare l'espressione regolare '\s+' names=["CRIM","ZN","INDUS","CHAS","NOX","RM","AGE","DIS","RAD","TAX","PRATIO","B","LSTAT","MEDV"]) # impostiamo dei nomi per le colonne boston.head() # passando un valore n a questo metodo stamperemo i primi n esempi del dataset
Il DataFrame avrà questa struttura:
La colonna che abbiamo denominato MEDV contiene il valore dell’abitazione in $1000 dollari (ad esempio un MEDV di 24 corrisponde a 24.000$) ed è il nostro target.
Regressione lineare semplice
Una regressione lineare semplice utilizza un’unica feature per costruire il modello, nel nostro caso utilizzeremo solo il numero di stanze, cioè la colonna che abbiamo denominato RM.
Estraiamo la feature RM e il target MEDV e salviamo i valori all’interno di due array numpy che ci serviranno per l’addestramento.
X = boston[['RM']].values # con l'attributo values ottieniamo l'array Y = boston["MEDV"].values
E’ buona pratica nel machine learning eseguire l’addestramento su un set di dati per poi verificare i risultati delle sue predizioni con un altro set contente dati non visti durante l’addestramento.
Questo garantisce che il nostro modello è in grado di generalizzare su dati sconosciuti e quindi ha realmente “imparato” dai dati, piuttosto che limitarsi a memorizzare il set di addestramento, condizione conosciuta come overfitting.
Per fare questo dobbiamo suddividere il nostro dataset in due set distinti, uno da utilizzare unicamente per l’addestramento e un altro da utilizzare per il test, possiamo farlo utilizzando il metodo train_test_split di scikit-learn.
from sklearn.model_selection import train_test_split X_train, X_test, Y_train, Y_test = train_test_split(X,Y, test_size=0.3, random_state=0)
Adesso abbiamo tutto ciò che ci serve per costruire il modello, facciamolo utilizzando la classe LinearRegression di scikit-learn:
- con il metodo fit eseguiamo l’addestramento
- con il metodo predict eseguiamo la predizione
Queste due semplici API sono comuni tra tutti i modelli definiti in scikit-learn, il ché rende il sostituire un modello con un altro estremamente semplice.
from sklearn.linear_model import LinearRegression ll = LinearRegression() ll.fit(X_train, Y_train) # passiamo i set di addestramento Y_pred_train = ll.predict(X_train) # eseguiamo la predizione sul train set Y_pred_test = ll.predict(X_test) # eseguiamo la predizione anche sul test set
Congratulazione ! Hai costruito il tuo primo modello di regressione e hai eseguito la tua prima predizione. Adesso bisogna vedere quanto buono è questo modello.
Per farlo dobbiamo definire una metrica che ci permetterà di misurare quanto le predizioni del modello si avvicinano ai valori corretti.
Una metrica semplice che fa proprio questo è l’errore quadratico medio (mean squadred error – MSE)
from sklearn.metrics import mean_squared_error print("MSE train: %f" % mean_squared_error(Y_train, Y_pred_train)) print("MSE test: %f" % mean_squared_error(Y_test, Y_pred_test))
Il risultato dovrebbe essere circa 42.16 per il train set e 47.03 per il test set, come vanno interpretati ?
Estraendo la radice quadrata otteniamo un valore che indica mediamente di quanto si è sbagliato il modello. Nel nostro caso, il test set ha ottenuto un MSE di 47 la cui radice quadrata è circa 6.9, considerando che i prezzi delle case sono rappresentati in $1000 questo ci dice che in media il modello ha fatto cilecca per 6900$.
Una metrica più intuitiva è il coefficiente di indeterminazione (anche conosciuto come R-squared score), può essere inteso come una versione standardizzata del MSE e ritorna un punteggio compreso tra 0 e 1 che può essere letto in questo modo:
- R2_score < 0.3 il modello è inutile.
- 0.3 < R2_score < 0.5 il modello è scarso.
- 0.5 < R2_score < 0.7 il modello è discreto.
- 0.7 < R2_score < 0.9 il modello è buono.
- 0.9 < R2_score < 1 il modello è ottimo.
- R2_score = 1 molto probabilmente c’è un errore nel modello.
from sklearn.metrics import r2_score print("R2 train: %f" % r2_score(Y_train, Y_pred_train)) print("R2 test: %f" % r2_score(Y_test, Y_pred_test))
Se hai fatto tutto correttamente dovresti ottenere un punteggio R-squared di circa 0.50 sul train set e 0.435 sul test set.
Il risultato non è così male considerando le poche informazioni che abbiamo utilizzato (solamente il numero di stanze), ma possiamo fare meglio !
Regressione lineare multipla
Una regressione lineare multipla è un modello che utilizza due o più features per l’addestramento.
Proviamo a predire il valore delle abitazioni utilizzando tutte le 13 features presenti nel nostro dataset, ricreiamo i nostri array con features e target, questa volta selezionando tutte le proprietà.
X = boston.drop("MEDV", axis=1).values # utilizzando drop rimuoviamo la colonna del prezzo e selezioniamo tutte le altre Y = boston["MEDV"].values X_train, X_test, Y_train, Y_test = train_test_split(X,Y, test_size=0.3, random_state=0)
Quando lavoriamo con più features dobbiamo essere sicuri che queste siano comprese in un range di valori comune, per far questo abbiamo due possibilità:
- Normalizzazione: portiamo tutti i dati in un range compreso tra 0 e 1
- Standardizzazione: convertiamo i dati in una distribuzione normale con media 0 e deviazione standard 1.
Standardizziamo l’array con le features utilizzando la classe StandardScaler di scikit-learn
from sklearn.preprocessing import StandardScaler ss = StandardScaler() X_train_std = ss.fit_transform(X_train) X_test_std = ss.transform(X_test)
Adesso possiamo creare il modello esattamente come fatto prima…
from sklearn.linear_model import LinearRegression ll = LinearRegression() ll.fit(X_train_std, Y_train) Y_pred_train = ll.predict(X_train_std) Y_pred_test = ll.predict(X_test_std)
… e calcolare le metriche
print("MSE train: %f" % mean_squared_error(Y_train, Y_pred_train)) print("MSE test: %f" % mean_squared_error(Y_test, Y_pred_test)) print("R2 train: %f" % r2_score(Y_train, Y_pred_train)) print("R2 test: %f" % r2_score(Y_test, Y_pred_test))
Dovresti ottenere i seguenti risultati:
- MSE del train set ≈ 19.96
- MSE del test set ≈ 27.16
- R2 del train set ≈ 0.76
- R2 del test set ≈ 0.67
Come vedi il risultato è nettamente superiore rispetto a quanto ottenuto con un’unica feature, avendo più informazioni a disposizione l’algoritmo di machine learning è riuscito a individuare pattern migliori e quindi a eseguire predizioni migliori.
Infatti una cosa da tenere sempre a mente è che, spesso, nel machine learning la quantità ma soprattutto la qualità dei dati riveste un ruolo anche superiore a quello degli algoritmi stessi.
Mettiamo all’opera il modello¶
Adesso che abbiamo un modello funzionante, vediamo come possiamo sfruttarlo. Vediamo assunti dalla Paperon Real Estate, che vuole avere una stima del valore di 5 abitazioni che vuole acquisire. Ci viene fornito un file csv con dentro le varie misurazioni e informazioni richieste dal nostro modello.
houses = pd.read_csv("https://raw.githubusercontent.com/ProfAI/tutorials/master/La%20tua%20prima%20regressione/houses.csv") houses.head()
Come vedi questa volta non abbiamo la colonna MEDV, che rappresenta il valore dell’abitazione, ed è ovvio, perché se avessimo già questa informazione il nostro lavoro sarebbe superfluo. Creiamo l’array numpy ed eseguiamo la standardizzazione.
X_paperon = houses.values X_paperon_std = ss.transform(X_paperon)
Adesso che i nostri dati sono pronti, usiamo il metodo predict del nostro modello per ottenere una stima del valore
Y_paperon = ll.predict(X_paperon_std) Y_paperon
Ecco le predizioni del modello !
Creiamo un file CSV aggiungendo quest’informazione.
houses["MEDV"]=Y_paperon houses.to_csv("houses.csv") houses.head()
Adesso inviamo il CSV alla Paperon Real Estate per incassare il nostro compenso 🙂
Trovi questo articolo in versione notebook eseguibile sulla repository Github dei tutorial di ProfessionAI.