Python per il machine learning - NumPy, pandas e Matplotlib
In questa pagina 6
Gli strumenti del corso: Python con le librerie NumPy, pandas, Matplotlib, seaborn e scikit-learn. Non si insegna la programmazione, ma si fissano le abitudini che servono nei laboratori e all'esame; i calcoli statistici corrispondenti sono in Statistica per il machine learningI dati di un problema ML si organizzano nella matrice di progetto $X$ ($n$ osservazioni, $p$ variabili). La statistica serve a capirli, ripulirli e prepararli: i momenti (media $\mu$, varianza $\sigma^2$, asimmetria, curtosi), i quartili con lo scarto interquartile $\mathrm{IQR}=Q_3-Q_1$ (all'esame senza interpolazione), la moda per i dati categorici. Con queste quantità si imputano i dati mancanti (media o mediana), si eliminano le variabili costanti e si standardizza con lo z-score $z=(x-\mu)/\sigma$, usando sempre media e deviazione standard del solo training set.Statistica per il machine learning → e Correlazione e visualizzazione dei datiLa correlazione di Pearson $r=\sum(X_i-\bar X)(Y_i-\bar Y)/\big(\sqrt{\sum(X_i-\bar X)^2}\sqrt{\sum(Y_i-\bar Y)^2}\big)\in[-1,1]$ misura la relazione lineare tra due variabili (covarianza divisa per le deviazioni standard); correlazione non implica causalità. Serve a capire quali variabili contano per il target e a eliminare quelle quasi duplicate (|r| molto alto). Gli indicatori di sintesi non bastano (quartetto di Anscombe, Datasaurus): vanno affiancati ai grafici: istogramma, KDE, box plot, violin plot, heatmap di correlazione, scatter plot e matrice di scatter plot.Correlazione e visualizzazione dei dati →. Esercizio: Esercizio - Statistica descrittiva, quartili e standardizzazione di un dataset.
Ambiente
- Notebook (
.ipynb) contro script (.py): il notebook alterna celle di codice e di testo, si esegue una cella alla volta e mostra subito i risultati (adatto a studio, esperimenti, analisi dati); lo script contiene solo codice, si esegue tutto insieme (programmi completi). - Google Colab: ambiente di esecuzione per notebook nel cloud: niente da installare, gratuito, buona potenza di calcolo. Si accede con l'account Google; i notebook si possono caricare o aprire da GitHub; i file si gestiscono dal pannello laterale (caricamento, salvataggio, download) e si può montare Google Drive.
- Percorsi con
pathlib.Path("ML_resources", "titanic.csv"); se i file non ci sono, nel laboratorio si clona il repository congit clone.
Linguaggio
- Tipi e variabili: assegnazione senza dichiarare il tipo (
x = 10int,y = 3.14float,z = "hello"str,w = Truebool);type(x); divisione/(reale),//(intera),%resto,**potenza;str * 3ripete; i tipi diversi non si sommano (10 + "a"è un errore: convertire constr(x)). - Condizioni:
if / elif / else, conand,or,not. L'indentazione è sintassi: niente graffe, i due punti, niente punto e virgola. - Cicli:
for i in range(1, 6)(1, 2, 3, 4, 5: l'estremo finale è escluso) ewhile cond:. - Funzioni con
defereturn. I tipi immutabili (int, float, str, bool) sono passati per copia: una funzione che fanumber = number + 10non cambia la variabile del chiamante; per cambiarla bisogna restituire il valore e assegnarlo (x = add_ten(x)); una funzione senzareturnrestituisceNone. - Classi: raggruppano attributi (variabili) e metodi (funzioni) in modo riusabile; ogni cliente creato dalla classe è un oggetto. Nei laboratori si usano per modelli (
fit,predict), nodi di alberi, criteri di impurità.
| Struttura | Ordinata | Modificabile | Duplicati | Indicizzata |
|---|---|---|---|---|
list [1, 2.5, "a"] |
sì | sì | sì | sì |
tuple (1, 2) |
sì | no | sì | sì |
set {1, 2} |
no | sì | no | no |
dict {"a": 1} |
no | sì | chiavi uniche | per chiave |
Gli indici partono da ; lista[-1] è l'ultimo elemento; set(lista) dà gli elementi unici.
NumPy
import numpy as np
x = np.linspace(-3, 3, 1000) # 1000 punti equispaziati
X = np.hstack([np.ones((n, 1)), X]) # aggiunge la colonna di uni a sinistra
beta = np.linalg.inv(X.T @ X) @ X.T @ y # @ = prodotto tra matrici
np.mean(A, axis=0) # axis=0: una media per colonna; axis=1: per riga
A[A[:, 0] > 3] # maschera booleana: righe con prima colonna > 3
np.cumsum(v); np.argmax(v >= 5) # somma cumulata; indice del primo valore >= 5
np.argsort(v)[::-1] # indici in ordine decrescenteChe cosa calcola la riga beta = ... (con i numeri). È la soluzione dei minimi quadratiQuando un sistema AX = b non ha soluzioni si cerca X che rende minima la norma di AX − b: AX è la proiezione ortogonale di b su Im A, e X si trova risolvendo le equazioni normali AᵀA X = Aᵀb; applicazione alla retta di regressione.Metodo dei minimi quadrati → della regressione lineare (Regressione lineareNell'apprendimento supervisionato si impara una funzione $F(x)$ dagli esempi $(x,y)$: regressione se $y$ è continua, classificazione se è categorica. Il modello lineare è $F_\beta(x)=\beta_0+\beta_1x_1+\dots+\beta_px_p=X\beta$ (con una colonna di uni per $\beta_0$) e i parametri si scelgono minimizzando l'errore quadratico medio $\mathrm{MSE}=\frac1n\sum_i(y_i-F_\beta(x_i))^2$, funzione convessa dei parametri. Annullando il gradiente di $J(\beta)=|y-X\beta|^2$ si ottengono le equazioni normali $X^TX\beta=X^Ty$ e la soluzione dei minimi quadrati ordinari $\beta=(X^TX)^{-1}X^Ty$. Il coefficiente di determinazione $R^2=1-SS_{res}/SS_{tot}$ misura la qualità del fit (0 = come la media, negativo = peggio della media). Un modello va valutato su un test set mai usato per addestrare: l'errore sul training è ottimistico e un polinomio di grado alto lo azzera senza generalizzare.Regressione lineare →). Si cerca che minimizza : il gradiente rispetto a (Differenziabilità e gradientef è differenziabile in x0 se f(x) = f(x0) + ∇f(x0)·(x − x0) + o(‖x − x0‖): vicino a x0 il grafico si confonde con il piano tangente z = f(x0) + ∇f(x0)·(x − x0). Differenziabile ⇒ continua, derivabile e D_v f = ∇f·v; derivate parziali continue ⇒ differenziabile. Il gradiente indica la direzione di massima crescita (pendenza ‖∇f‖) ed è ortogonale alle curve di livello.Differenziabilità e gradiente →) è e si annulla quando (le equazioni normali); se è invertibile (Matrice inversaL'inversa di una matrice quadrata A è la matrice A⁻¹ con A A⁻¹ = A⁻¹ A = I; esiste se e solo se rango(A) = n e si calcola con Gauss-Jordan riducendo (A | I) fino a (I | A⁻¹).Matrice inversa →) la soluzione è , che in NumPy si scrive esattamente come sopra, con @ per il prodotto riga per colonna (Operazioni tra matriciLe matrici m×n formano uno spazio vettoriale (somma e prodotto per scalare elemento per elemento); il prodotto righe per colonne corrisponde alla composizione di funzioni lineari, è associativo ma non commutativo; la trasposta scambia righe e colonne e (AB)^T = B^T A^T.Operazioni tra matrici →) e .T per la trasposta. Esempio con i tre appartamenti di Introduzione al machine learningIl machine learning (ML) è la parte dell'intelligenza artificiale che costruisce soluzioni a partire dai dati e non da regole scritte a mano: un modello matematico con parametri liberi viene addestrato su esempi storici e poi usato su dati nuovi. Si distingue tra apprendimento supervisionato (dati $(x,y)$, si impara la mappa $x\mapsto y$: regressione se $y$ è un numero, classificazione se è una categoria), non supervisionato (solo $x$, si cercano struttura e gruppi) e per rinforzo (stato, azione, ricompensa). Un progetto ML non è «plug and play»: segue le fasi problema, raccolta, pulizia, modellazione, rilascio, e va valutato su dati mai visti; senza dati non c'è modello, alcuni fenomeni sono imprevedibili, la generalizzazione fuori dal dominio di addestramento non è garantita.Introduzione al machine learning →: ha righe (la colonna di uni è il bias) e . Allora (somme di prodotti tra colonne: , , ), , determinante , e il risultato è , i valori di intercetta e pendenza trovati nella nota introduttiva (verificato con np.linalg.inv). In pratica si preferisce np.linalg.lstsq o solve, numericamente più stabili dell'inversa esplicita.
- Assi:
axis=0opera lungo le righe (collassa le righe, un risultato per colonna),axis=1lungo le colonne. Senzaaxisla media è su tutti gli elementi. Esempio conA = [[1,2],[3,4],[5,6]](3 righe, 2 colonne):A.mean(axis=0)(una media per colonna, cioè per variabile se le righe sono osservazioni);A.mean(axis=1)(una media per riga);A.mean(). È il motivo per cui le statistiche di una variabile si calcolano conaxis=0sulla matrice di progetto (Statistica per il machine learningI dati di un problema ML si organizzano nella matrice di progetto $X$ ($n$ osservazioni, $p$ variabili). La statistica serve a capirli, ripulirli e prepararli: i momenti (media $\mu$, varianza $\sigma^2$, asimmetria, curtosi), i quartili con lo scarto interquartile $\mathrm{IQR}=Q_3-Q_1$ (all'esame senza interpolazione), la moda per i dati categorici. Con queste quantità si imputano i dati mancanti (media o mediana), si eliminano le variabili costanti e si standardizza con lo z-score $z=(x-\mu)/\sigma$, usando sempre media e deviazione standard del solo training set.Statistica per il machine learning →). - Forma (
shape): un vettore non è uguale a una colonna : sottrarre un array da uno dà per broadcasting una matrice senza errore. Controllare sempre le forme (.reshape(-1, 1)). Esempio:a = np.array([1,2,3])ha forma eb = np.array([[1],[2],[3]])forma ;a - bnon dà un errore ma una matrice con elemento , cioè[[0,1,2],[-1,0,1],[-2,-1,0]]: il broadcasting «stira» le due forme a . Se si voleva la differenza elemento per elemento, la forma giusta era la stessa per entrambi (a - b.ravel()dà[0,0,0]). In una funzione di costo cony_hateyquesto errore produce una somma di termini invece di , senza nessun messaggio. - Statistiche:
np.vardivide per (popolazione),ddof=1per (il motivo del divisore è in Statistica per il machine learningI dati di un problema ML si organizzano nella matrice di progetto $X$ ($n$ osservazioni, $p$ variabili). La statistica serve a capirli, ripulirli e prepararli: i momenti (media $\mu$, varianza $\sigma^2$, asimmetria, curtosi), i quartili con lo scarto interquartile $\mathrm{IQR}=Q_3-Q_1$ (all'esame senza interpolazione), la moda per i dati categorici. Con queste quantità si imputano i dati mancanti (media o mediana), si eliminano le variabili costanti e si standardizza con lo z-score $z=(x-\mu)/\sigma$, usando sempre media e deviazione standard del solo training set.Statistica per il machine learning →; con[10,12,14,16,18]: scarti quadratici in somma ,np.vardà enp.std, mentre conddof=1(e in pandas) e );np.covenp.corrcoefvogliono per default le variabili sulle righe: con le osservazioni sulle righe serverowvar=False.np.percentile(..., method="higher")per i quartili; curtosi, asimmetria e moda non sono in NumPy.
pandas
import pandas as pd
df = pd.read_csv("ML_resources/titanic.csv")
df.head(); df.shape; df.columns # prime righe, (righe, colonne), nomi
df["Sex"].value_counts() # conteggi per categoria
df["Age"].mean(); df["Age"].max()
df[df["Age"] > 30]["Sex"].value_counts() # filtro e conteggio
X = df.drop(columns=["target"]).to_numpy(); y = df["target"].to_numpy()pandas è come un foglio di calcolo: filtra righe, aggrega statistiche, legge e scrive CSV. Le statistiche di pandas usano (var, std), quelle di NumPy : per grande la differenza è trascurabile. Sul dataset Iris (150 righe, 5 colonne: 4 numeriche e la specie categorica) mean, var, std, skew, kurt, quantile(0.25) si calcolano sulle sole colonne numeriche, la moda sulla colonna categorica; corr() dà la matrice di correlazione di Pearson.
Grafici
- Matplotlib (
plt.plot,plt.scatter,plt.hist,plt.boxplot,plt.legend,plt.show): su si disegnano e con legenda.np.linspace(-3, 3, 1000)crea punti equispaziati tra e (passo ),x**2enp.sin(x)calcolano le due funzioni su tutti i punti insieme (vettorizzazione), eplt.plot(x, y)unisce i punti con segmenti: con abbastanza punti la spezzata sembra la curva. Il risultato è questo: è una funzione pari e dispari (Funzioni monotone, pari e dispariFunzioni crescenti e decrescenti, legame tra stretta monotonia e iniettività, e simmetrie delle funzioni pari e dispari.Funzioni monotone, pari e dispari →), quindi la prima è simmetrica rispetto all'asse e la seconda rispetto all'origine.
Grafico interattivo: Il grafico di plt.plot(x, x**2) e plt.plot(x, np.sin(x)) con x = np.linspace(-3, 3, 1000): la parabola x² (pari) e il seno (dispari)
- seaborn, costruita sopra Matplotlib, con interfaccia di più alto livello soprattutto per i DataFrame:
sns.scatterplot(data=df, x=..., y=..., hue=...),sns.heatmap(corr, annot=True, cmap="coolwarm"),sns.pairplot(df),sns.boxplot,sns.violinplot,sns.barplot. - Per tabelle grandi si campiona (
df.sample(1000)) prima del pair plot.
Trappole tipiche
- Dimenticare
rowvar=False, o l'asse giusto inmean/std. - Mescolare e nelle funzioni di costo e nei gradienti.
- Calcolare standardizzazione e statistiche sul dataset intero invece che sul solo training.
- Modificare un
DataFramefiltrato e non accorgersi che si lavora su una copia. - Fidarsi di numeri letti da un file con una colonna categorica: va codificata prima (Classificazione e k-nearest neighborsNella classificazione l'uscita $y$ è una categoria (con $C$ classi; $C=2$ è il caso binario). Il classificatore più semplice è il k-nearest neighbors: una nuova osservazione prende la classe più frequente (voto di maggioranza) tra i suoi $k$ vicini più prossimi nel training, con distanza euclidea $\sqrt{\sum(A_i-B_i)^2}$ o di Manhattan $\sum|A_i-B_i|$ (per la regressione si fa la media dei vicini). $k$ è un iperparametro: $k$ piccolo dà bordi frastagliati e overfitting, $k$ grande underfitting. È un metodo basato su istanze e «pigro» (nessun addestramento, costo alla predizione), sensibile a scala e feature irrilevanti e alla maledizione della dimensionalità; gli ingressi categorici si codificano con one-hot. Approfondimento: non nel programma di Telecomunicazioni.Classificazione e k-nearest neighbors →, one-hot).
Versione ripasso
Ambiente. Il notebook alterna celle di codice e testo e mostra subito i risultati; lo script contiene solo codice ed è eseguito tutto insieme. Google Colab esegue notebook nel cloud, gratis, con Drive montabile.
Linguaggio.
/è la divisione reale,//quella intera,%il resto,**la potenza.- L'indentazione è sintassi.
range(1, 6)esclude il . - I tipi immutabili (int, float, str, bool) sono passati per copia: una funzione deve fare
returnper cambiare un valore, e il chiamante deve assegnarlo. - Indici da ;
lista[-1]è l'ultimo elemento. - list (modificabile, con duplicati), tuple (immutabile), set (senza duplicati, non indicizzato), dict (per chiave).
NumPy.
axis=0calcola una statistica per colonna,axis=1per riga, senzaaxissu tutti gli elementi. Esempio. Con :A.mean(axis=0),A.mean(axis=1),A.mean().@è il prodotto riga per colonna;.Tla trasposta.- Forme. Un vettore non è una colonna : con il broadcasting la differenza tra e dà una matrice senza errore. Si controlla con
.reshape(-1, 1). - Varianza.
np.vardivide per ,ddof=1per . Esempio. Con e somma degli scarti quadrati :np.vardà , conddof=1si ha . np.covenp.corrcoefvogliono le variabili sulle righe: con le osservazioni sulle righe serverowvar=False.- Curtosi, asimmetria e moda non sono in NumPy.
Formula (minimi quadrati in NumPy). è la soluzione dei minimi quadratiQuando un sistema AX = b non ha soluzioni si cerca X che rende minima la norma di AX − b: AX è la proiezione ortogonale di b su Im A, e X si trova risolvendo le equazioni normali AᵀA X = Aᵀb; applicazione alla retta di regressione.Metodo dei minimi quadrati → (Regressione lineareNell'apprendimento supervisionato si impara una funzione $F(x)$ dagli esempi $(x,y)$: regressione se $y$ è continua, classificazione se è categorica. Il modello lineare è $F_\beta(x)=\beta_0+\beta_1x_1+\dots+\beta_px_p=X\beta$ (con una colonna di uni per $\beta_0$) e i parametri si scelgono minimizzando l'errore quadratico medio $\mathrm{MSE}=\frac1n\sum_i(y_i-F_\beta(x_i))^2$, funzione convessa dei parametri. Annullando il gradiente di $J(\beta)=|y-X\beta|^2$ si ottengono le equazioni normali $X^TX\beta=X^Ty$ e la soluzione dei minimi quadrati ordinari $\beta=(X^TX)^{-1}X^Ty$. Il coefficiente di determinazione $R^2=1-SS_{res}/SS_{tot}$ misura la qualità del fit (0 = come la media, negativo = peggio della media). Un modello va valutato su un test set mai usato per addestrare: l'errore sul training è ottimistico e un polinomio di grado alto lo azzera senza generalizzare.Regressione lineare →), con
beta = np.linalg.inv(X.T @ X) @ X.T @ y.
Esempio (tre appartamenti). ha righe e . Allora , , determinante : . In pratica si preferisce np.linalg.lstsq o solve, più stabili dell'inversa (Matrice inversaL'inversa di una matrice quadrata A è la matrice A⁻¹ con A A⁻¹ = A⁻¹ A = I; esiste se e solo se rango(A) = n e si calcola con Gauss-Jordan riducendo (A | I) fino a (I | A⁻¹).Matrice inversa →, Operazioni tra matriciLe matrici m×n formano uno spazio vettoriale (somma e prodotto per scalare elemento per elemento); il prodotto righe per colonne corrisponde alla composizione di funzioni lineari, è associativo ma non commutativo; la trasposta scambia righe e colonne e (AB)^T = B^T A^T.Operazioni tra matrici →).
pandas. È come un foglio di calcolo.
read_csv,head,shape(righe, colonne),columns.value_countsper i conteggi di una categoria; filtri booleani comedf[df["Age"] > 30].mean,var,std,skew,kurt,quantile(0.25),corr:varestdusano ;corrdà la correlazione di Pearson. Le statistiche si applicano alle sole colonne numeriche.
Grafici.
- Matplotlib:
plt.plot,plt.scatter,plt.hist,plt.boxplot,plt.legend,plt.show. Conx = np.linspace(-3, 3, 1000),plt.plot(x, x**2)eplt.plot(x, np.sin(x)): la parabola è pari e il seno dispari (Funzioni monotone, pari e dispariFunzioni crescenti e decrescenti, legame tra stretta monotonia e iniettività, e simmetrie delle funzioni pari e dispari.Funzioni monotone, pari e dispari →). - seaborn sopra Matplotlib, per i DataFrame:
sns.scatterplot(data=df, x=..., y=..., hue=...),sns.heatmap(corr, annot=True, cmap="coolwarm"),sns.pairplot(df),sns.boxplot,sns.violinplot,sns.barplot. Con tabelle grandi si campiona prima (df.sample(1000)).
Errori tipici:
- dimenticare
rowvar=False, o l'asse giusto inmeanestd; - mescolare e in una funzione di costo: la somma ha termini invece di , senza messaggi di errore;
- calcolare standardizzazione e statistiche sul dataset intero invece che sul solo training;
- modificare un DataFrame filtrato pensando che sia l'originale;
- usare una colonna categorica senza codificarla prima (Classificazione e k-nearest neighborsNella classificazione l'uscita $y$ è una categoria (con $C$ classi; $C=2$ è il caso binario). Il classificatore più semplice è il k-nearest neighbors: una nuova osservazione prende la classe più frequente (voto di maggioranza) tra i suoi $k$ vicini più prossimi nel training, con distanza euclidea $\sqrt{\sum(A_i-B_i)^2}$ o di Manhattan $\sum|A_i-B_i|$ (per la regressione si fa la media dei vicini). $k$ è un iperparametro: $k$ piccolo dà bordi frastagliati e overfitting, $k$ grande underfitting. È un metodo basato su istanze e «pigro» (nessun addestramento, costo alla predizione), sensibile a scala e feature irrilevanti e alla maledizione della dimensionalità; gli ingressi categorici si codificano con one-hot. Approfondimento: non nel programma di Telecomunicazioni.Classificazione e k-nearest neighbors →, one-hot).