Esplorazione degli algoritmi di apprendimento automatico supervisionato

Pubblicato: 2022-03-11

L'obiettivo principale di questa lettura è comprendere una metodologia statistica sufficiente per essere in grado di sfruttare gli algoritmi di apprendimento automatico nella libreria scikit-learn di Python e quindi applicare questa conoscenza per risolvere un classico problema di apprendimento automatico.

La prima tappa del nostro viaggio ci porterà attraverso una breve storia dell'apprendimento automatico. Quindi ci immergeremo in diversi algoritmi. Nella nostra ultima tappa, useremo ciò che abbiamo imparato per risolvere il problema della previsione del tasso di sopravvivenza del Titanic.

Alcuni disclaimer:

  • Sono un ingegnere del software completo, non un esperto di algoritmi di apprendimento automatico.
  • Presumo che tu conosca un po' di Python di base.
  • Questo è esplorativo, quindi non tutti i dettagli sono spiegati come in un tutorial.

Detto questo, tuffiamoci!

Una rapida introduzione agli algoritmi di apprendimento automatico

Non appena ti avventuri in questo campo, ti rendi conto che l'apprendimento automatico è meno romantico di quanto potresti pensare. Inizialmente, ero pieno di speranze che dopo aver appreso di più sarei stato in grado di costruire la mia IA Jarvis, che avrebbe trascorso tutto il giorno a programmare software e fare soldi per me, così avrei potuto trascorrere intere giornate all'aperto leggendo libri, guidando una moto, e godermi uno stile di vita sconsiderato mentre il mio Jarvis personale mi rende le tasche più profonde. Tuttavia, mi sono presto reso conto che il fondamento degli algoritmi di apprendimento automatico è la statistica, che personalmente trovo noiosa e poco interessante. Fortunatamente, si è scoperto che le statistiche "noiose" hanno alcune applicazioni molto affascinanti.

Scoprirai presto che per accedere a quelle affascinanti applicazioni è necessario comprendere molto bene le statistiche. Uno degli obiettivi degli algoritmi di apprendimento automatico è trovare dipendenze statistiche nei dati forniti.

I dati forniti potrebbero essere qualsiasi cosa, dal controllo della pressione sanguigna rispetto all'età alla ricerca di testo scritto a mano in base al colore di vari pixel.

Detto questo, ero curioso di vedere se potevo usare algoritmi di apprendimento automatico per trovare dipendenze nelle funzioni hash crittografiche (SHA, MD5, ecc.), Tuttavia, non puoi davvero farlo perché le primitive crittografiche corrette sono costruite in questo modo che eliminano le dipendenze e producono un output significativamente difficile da prevedere. Credo che, dato un tempo infinito, gli algoritmi di apprendimento automatico potrebbero decifrare qualsiasi modello crittografico.

Sfortunatamente, non abbiamo molto tempo, quindi dobbiamo trovare un altro modo per estrarre in modo efficiente la criptovaluta. Fino a che punto siamo arrivati ​​fino ad ora?

Una breve storia degli algoritmi di apprendimento automatico

Le radici degli algoritmi di apprendimento automatico provengono da Thomas Bayes, uno statistico inglese vissuto nel 18° secolo. Il suo articolo An Essay Towards Solving a Problem in the Doctrine of Chances è alla base del teorema di Bayes, che è ampiamente applicato nel campo della statistica.

Nel 19° secolo, Pierre-Simon Laplace pubblicò Theorie analytique des probabilites , ampliando il lavoro di Bayes e definendo quello che oggi conosciamo come Teorema di Bayes. Poco prima, Adrien-Marie Legendre aveva descritto il metodo dei "minimi quadrati", ampiamente utilizzato oggi anche nell'apprendimento supervisionato.

Il 20° secolo è il periodo in cui la maggior parte delle scoperte pubblicamente note sono state fatte in questo campo. Andrey Markov ha inventato le catene di Markov, che ha usato per analizzare le poesie. Alan Turing ha proposto una macchina di apprendimento che potrebbe diventare artificialmente intelligente, prefigurando sostanzialmente algoritmi genetici. Frank Rosenblatt ha inventato il Perceptron , suscitando grande entusiasmo e grande copertura nei media.

Ma poi gli anni '70 hanno visto molto pessimismo sull'idea dell'IA e, quindi, finanziamenti ridotti, quindi questo periodo è chiamato inverno dell'IA . La riscoperta della backpropagation negli anni '80 ha causato una rinascita nella ricerca sull'apprendimento automatico. E oggi, è di nuovo un argomento caldo.

Il compianto Leo Breiman ha distinto tra due paradigmi di modellazione statistica: modellazione dei dati e modellazione algoritmica. "Modellazione algoritmica" indica più o meno algoritmi di apprendimento automatico come la foresta casuale .

L'apprendimento automatico e la statistica sono campi strettamente correlati. Secondo Michael I. Jordan, le idee sull'apprendimento automatico, dai principi metodologici agli strumenti teorici, hanno avuto una lunga preistoria nella statistica. Ha anche suggerito la scienza dei dati come termine segnaposto per il problema generale su cui gli specialisti dell'apprendimento automatico e gli statistici stanno entrambi lavorando implicitamente.

Categorie di algoritmi di apprendimento automatico

Il campo dell'apprendimento automatico si basa su due pilastri principali chiamati apprendimento supervisionato e apprendimento non supervisionato . Alcune persone considerano anche un nuovo campo di studio, l'apprendimento profondo , separato dalla questione dell'apprendimento supervisionato rispetto a quello non supervisionato.

L'apprendimento supervisionato è quando a un computer vengono presentati esempi di input e i loro output desiderati. L'obiettivo del computer è imparare una formula generale che associa gli input agli output. Questo può essere ulteriormente suddiviso in:

  • Apprendimento semi-supervisionato , ovvero quando al computer viene fornito un set di formazione incompleto con alcuni output mancanti
  • Apprendimento attivo , ovvero quando il computer può ottenere etichette di formazione solo per un insieme molto limitato di istanze. Se utilizzati in modo interattivo, i loro set di formazione possono essere presentati all'utente per l'etichettatura.
  • Apprendimento per rinforzo , ovvero quando i dati dell'allenamento vengono forniti solo come feedback sulle azioni del programma nell'ambiente dinamico, come guidare un veicolo o giocare contro un avversario

Al contrario, l'apprendimento non supervisionato avviene quando non vengono fornite etichette e spetta all'algoritmo trovare la struttura nel suo input. L'apprendimento senza supervisione può essere un obiettivo in sé quando abbiamo solo bisogno di scoprire schemi nascosti.

Il deep learning è un nuovo campo di studio che si ispira alla struttura e alla funzione del cervello umano e si basa su reti neurali artificiali piuttosto che su semplici concetti statistici. Il deep learning può essere utilizzato sia in approcci supervisionati che non supervisionati.

In questo articolo, esamineremo solo alcuni dei più semplici algoritmi di apprendimento automatico supervisionati e li utilizzeremo per calcolare le possibilità di sopravvivenza di un individuo nel tragico affondamento del Titanic. Ma in generale, se non sei sicuro di quale algoritmo usare, un buon punto di partenza è il cheat sheet dell'algoritmo di apprendimento automatico di scikit-learn.

Modelli di base di apprendimento automatico supervisionato

Forse l'algoritmo più semplice possibile è la regressione lineare. A volte questa può essere rappresentata graficamente come una linea retta, ma nonostante il nome, se esiste un'ipotesi polinomiale, questa linea potrebbe invece essere una curva. In entrambi i casi, modella le relazioni tra la variabile scalare dipendente $y$ e uno o più valori esplicativi indicati con $x$.

In parole povere, questo significa che la regressione lineare è l'algoritmo che apprende la dipendenza tra ogni noto $x$ e $y$, in modo tale che in seguito possiamo usarlo per prevedere $y$ per un campione sconosciuto di $x$.

Nel nostro primo esempio di apprendimento supervisionato, utilizzeremo un modello di regressione lineare di base per prevedere la pressione sanguigna di una persona data la sua età. Questo è un set di dati molto semplice con due caratteristiche significative: età e pressione sanguigna.

Come già accennato in precedenza, la maggior parte degli algoritmi di apprendimento automatico funziona trovando una dipendenza statistica nei dati forniti loro. Questa dipendenza è chiamata ipotesi e di solito è indicata con $h(\theta)$.

Per capire l'ipotesi, iniziamo caricando ed esplorando i dati.

 import matplotlib.pyplot as plt from pandas import read_csv import os # Load data data_path = os.path.join(os.getcwd(), "data/blood-pressure.txt") dataset = read_csv(data_path, delim_whitespace=True) # We have 30 entries in our dataset and four features. The first feature is the ID of the entry. # The second feature is always 1. The third feature is the age and the last feature is the blood pressure. # We will now drop the ID and One feature for now, as this is not important. dataset = dataset.drop(['ID', 'One'], axis=1) # And we will display this graph %matplotlib inline dataset.plot.scatter(x='Age', y='Pressure') # Now, we will assume that we already know the hypothesis and it looks like a straight line h = lambda x: 84 + 1.24 * x # Let's add this line on the chart now ages = range(18, 85) estimated = [] for i in ages: estimated.append(h(i)) plt.plot(ages, estimated, 'b')

[<matplotlib.lines.Line2D at 0x11424b828>]

Un'ipotesi lineare mostrata su un grafico età vs pressione sanguigna.

Nel grafico sopra, ogni punto blu rappresenta il nostro campione di dati e la linea blu è l'ipotesi che il nostro algoritmo deve imparare. Quindi qual è esattamente questa ipotesi comunque?

Per risolvere questo problema, dobbiamo imparare la dipendenza tra $x$ e $y$, che è indicato da $y = f(x)$. Pertanto $f(x)$ è la funzione target ideale. L'algoritmo di apprendimento automatico cercherà di indovinare la funzione di ipotesi $h(x)$ che è l'approssimazione più vicina dell'incognita $f(x)$.

La forma più semplice possibile di ipotesi per il problema della regressione lineare è la seguente: $h_\theta(x) = \theta_0 + \theta_1 * x$. Abbiamo una singola variabile scalare di input $x$ che emette una singola variabile scalare $y$, dove $\theta_0$ e $\theta_1$ sono parametri che dobbiamo imparare. Il processo di adattamento di questa linea blu nei dati è chiamato regressione lineare. È importante capire che abbiamo un solo parametro di input $x_1$; tuttavia, molte funzioni di ipotesi includeranno anche l'unità di polarizzazione ($x_0$). Quindi la nostra ipotesi risultante ha una forma di $h_\theta(x) = \theta_0 * x_0 + \theta_1 * x_1$. Ma possiamo evitare di scrivere $x_0$ perché è quasi sempre uguale a 1.

Tornando alla linea blu. La nostra ipotesi sembra $h(x) = 84 + 1.24x$, il che significa che $\theta_0 = 84$ e $\theta_1 = 1.24$. Come possiamo derivare automaticamente quei valori $\theta$?

Dobbiamo definire una funzione di costo . In sostanza, ciò che fa la funzione di costo è semplicemente calcolare l'errore quadratico medio della radice tra la previsione del modello e l'output effettivo.

\[J(\theta) = \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)})^2\ ]

Ad esempio, la nostra ipotesi prevede che per una persona di 48 anni, la sua pressione sanguigna dovrebbe essere $h(48) = 84 + 1,24 * 48 = 143mmHg$; tuttavia, nel nostro campione di addestramento, abbiamo il valore di $ 130 mmHg$. Pertanto l'errore è $(143 - 130)^2 = 169$. Ora dobbiamo calcolare questo errore per ogni singola voce nel nostro set di dati di addestramento, quindi sommarlo insieme ($\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i )})^2$) e prendine il valore medio.

Questo ci dà un singolo numero scalare che rappresenta il costo della funzione. Il nostro obiettivo è trovare valori $\theta$ tali che la funzione di costo sia la più bassa; in altre parole, vogliamo minimizzare la funzione di costo. Si spera che questo sembri intuitivo: se abbiamo un valore della funzione di costo piccolo, significa che anche l'errore di previsione è piccolo.

 import numpy as np # Let's calculate the cost for the hypothesis above h = lambda x, theta_0, theta_1: theta_0 + theta_1 * x def cost(X, y, t0, t1): m = len(X) # the number of the training samples c = np.power(np.subtract(h(X, t0, t1), y), 2) return (1 / (2 * m)) * sum(c) X = dataset.values[:, 0] y = dataset.values[:, 1] print('J(Theta) = %2.2f' % cost(X, y, 84, 1.24))

J(Theta) = 1901.95

Ora, dobbiamo trovare tali valori di $\theta$ tali che il nostro valore della funzione di costo sia minimo. Ma come lo facciamo?

\[minJ(\theta) = \frac{1}{2m}\sum_{i=1}^m(h_\theta(x^{(i)}) - y^{(i)})^2\ ]

Esistono diversi algoritmi possibili, ma il più popolare è la discesa del gradiente . Per comprendere l'intuizione alla base del metodo di discesa del gradiente, tracciamola prima sul grafico. Per semplicità, assumiamo un'ipotesi più semplice $h(\theta) = \theta_1 * x$. Successivamente, tracciamo un semplice grafico 2D in cui $x$ è il valore di $\theta$ e $y$ è la funzione di costo a questo punto.

 import matplotlib.pyplot as plt fig = plt.figure() # Generate the data theta_1 = np.arange(-10, 14, 0.1) J_cost = [] for t1 in theta_1: J_cost += [ cost(X, y, 0, t1) ] plt.plot(theta_1, J_cost) plt.xlabel(r'$\theta_1$') plt.ylabel(r'$J(\theta)$') plt.show() 

Una funzione di costo convessa.

La funzione di costo è convessa, il che significa che sull'intervallo $[a, b]$ c'è solo un minimo. Il che significa ancora una volta che i migliori parametri $\theta$ sono nel punto in cui la funzione di costo è minima.

Fondamentalmente, la discesa del gradiente è un algoritmo che cerca di trovare l'insieme di parametri che riducono al minimo la funzione. Inizia con un set iniziale di parametri e procede iterativamente nella direzione negativa del gradiente della funzione.

Trovare il minimo per una funzione di costo.

Se calcoliamo la derivata di una funzione di ipotesi in un punto specifico, questo ci darà una pendenza della retta tangente alla curva in quel punto. Ciò significa che possiamo calcolare la pendenza in ogni singolo punto del grafico.

Il modo in cui funziona l'algoritmo è questo:

  1. Scegliamo un punto di partenza casuale (random $\theta$).
  2. Calcola la derivata della funzione di costo a questo punto.
  3. Fai il piccolo passo verso il pendio $\theta_j := \theta_j - \lambda * \frac{\partial}{\partial \theta_j} * J(\theta)$.
  4. Ripeti i passaggi 2-3 fino a convergere.

Ora, la condizione di convergenza dipende dall'implementazione dell'algoritmo. Potremmo fermarci dopo 50 passi, dopo qualche soglia o qualsiasi altra cosa.

 import math # Example of the simple gradient descent algorithm taken from Wikipedia cur_x = 2.5 # The algorithm starts at point x gamma = 0.005 # Step size multiplier precision = 0.00001 previous_step_size = cur_x df = lambda x: 2 * x * math.cos(x) # Remember the learning curve and plot it while previous_step_size > precision: prev_x = cur_x cur_x += -gamma * df(prev_x) previous_step_size = abs(cur_x - prev_x) print("The local minimum occurs at %f" % cur_x)

The local minimum occurs at 4.712194

Non implementeremo tali algoritmi in questo articolo. Utilizzeremo invece scikit-learn , una libreria open source di apprendimento automatico in Python. Fornisce molte API molto utili per diversi problemi di data mining e apprendimento automatico.

 from sklearn.linear_model import LinearRegression # LinearRegression uses the gradient descent method # Our data X = dataset[['Age']] y = dataset[['Pressure']] regr = LinearRegression() regr.fit(X, y) # Plot outputs plt.xlabel('Age') plt.ylabel('Blood pressure') plt.scatter(X, y, color='black') plt.plot(X, regr.predict(X), color='blue') plt.show() plt.gcf().clear() 

Un'ipotesi lineare appresa sul grafico della pressione sanguigna rispetto all'età

<matplotlib.figure.Figure at 0x120fae1d0>

 print( 'Predicted blood pressure at 25 yo = ', regr.predict(25) ) print( 'Predicted blood pressure at 45 yo = ', regr.predict(45) ) print( 'Predicted blood pressure at 27 yo = ', regr.predict(27) ) print( 'Predicted blood pressure at 34.5 yo = ', regr.predict(34.5) ) print( 'Predicted blood pressure at 78 yo = ', regr.predict(78) )
 Predicted blood pressure at 25 yo = [[ 122.98647692]] Predicted blood pressure at 45 yo = [[ 142.40388395]] Predicted blood pressure at 27 yo = [[ 124.92821763]] Predicted blood pressure at 34.5 yo = [[ 132.20974526]] Predicted blood pressure at 78 yo = [[ 174.44260555]]

Tipi di dati statistici

Quando si lavora con i dati per problemi di apprendimento automatico, è importante riconoscere diversi tipi di dati. Potremmo avere dati numerici (continuo o discreti), categoriali o ordinali.

I dati numerici hanno significato come misura. Ad esempio, età, peso, numero di bitcoin che una persona possiede o quanti articoli la persona può scrivere al mese. I dati numerici possono essere ulteriormente suddivisi in tipi discreti e continui.

  • I dati discreti rappresentano i dati che possono essere contati con numeri interi, ad esempio il numero di stanze in un appartamento o il numero di lanci di monete.
  • I dati continui non possono essere necessariamente rappresentati con numeri interi. Ad esempio, se stai misurando la distanza che puoi saltare, potrebbe essere 2 metri o 1,5 metri o 1,652245 metri.

I dati categoriali rappresentano valori come il sesso della persona, lo stato civile, il paese, ecc. Questi dati possono assumere un valore numerico, ma quei numeri non hanno alcun significato matematico. Non puoi sommarli insieme.

I dati ordinali possono essere un mix degli altri due tipi, in quanto le categorie possono essere numerate in modo matematicamente significativo. Un esempio comune sono le valutazioni: spesso ci viene chiesto di valutare le cose su una scala da uno a dieci e sono consentiti solo numeri interi. Sebbene possiamo utilizzarlo numericamente, ad esempio per trovare una valutazione media per qualcosa, spesso trattiamo i dati come se fossero categorici quando si tratta di applicare metodi di apprendimento automatico.

Regressione logistica

La regressione lineare è un fantastico algoritmo che ci aiuta a prevedere valori numerici, ad esempio il prezzo della casa con le dimensioni e il numero di stanze specifici. Tuttavia, a volte, potremmo anche voler prevedere dati categoriali, per ottenere risposte a domande come:

  • Questo è un cane o un gatto?
  • Questo tumore è maligno o benigno?
  • Questo vino è buono o cattivo?
  • Questa email è spam o no?

O anche:

  • Quale numero è nella foto?
  • A quale categoria appartiene questa email?

Tutte queste domande sono specifiche del problema di classificazione . E l'algoritmo di classificazione più semplice è chiamato regressione logistica , che alla fine è lo stesso della regressione lineare tranne per il fatto che ha un'ipotesi diversa.

Innanzitutto, possiamo riutilizzare la stessa ipotesi lineare $h_\theta(x) = \theta^TX$ (questa è in forma vettoriale). Mentre la regressione lineare può produrre qualsiasi numero nell'intervallo $[a, b]$, la regressione logistica può produrre solo valori in $[−1, 1]$, che è la probabilità che l'oggetto rientri o meno in una determinata categoria.

Usando una funzione sigmoide , possiamo convertire qualsiasi valore numerico per rappresentare un valore sull'intervallo $[−1, 1]$.

\[f(x) = \frac{1}{1 + e^x}\]

Ora, invece di $x$, dobbiamo passare un'ipotesi esistente e quindi otterremo:

\[f(x) = \frac{1}{1 + e^{\theta_0 + \theta_1 * x_1 + ... + \theta_n * x_n}}\]

Dopodiché, possiamo applicare una semplice soglia dicendo che se l'ipotesi è maggiore di zero, questo è un valore vero, altrimenti falso.

\[h_\theta(x) = \begin{cases} 1 & \mbox{if } \theta^TX > 0 \\ 0 & \mbox{else} \end{cases}\]

Ciò significa che possiamo utilizzare la stessa funzione di costo e lo stesso algoritmo di discesa del gradiente per apprendere un'ipotesi di regressione logistica.

Nel nostro prossimo esempio di algoritmo di apprendimento automatico, consiglieremo ai piloti dello space shuttle se utilizzare o meno il controllo dell'atterraggio automatico o manuale. Abbiamo un set di dati molto piccolo, 15 campioni, che consiste in sei caratteristiche e la verità fondamentale .

Negli algoritmi di apprendimento automatico, il termine " verità fondamentale " si riferisce all'accuratezza della classificazione del set di addestramento per le tecniche di apprendimento supervisionato.

Il nostro set di dati è completo, il che significa che non ci sono funzionalità mancanti; tuttavia, alcune delle funzioni hanno un "*" invece della categoria, il che significa che questa funzione non ha importanza. Sostituiremo tutti questi asterischi con zeri.

 from sklearn.linear_model import LogisticRegression # Data data_path = os.path.join(os.getcwd(), "data/shuttle-landing-control.csv") dataset = read_csv(data_path, header=None, names=['Auto', 'Stability', 'Error', 'Sign', 'Wind', 'Magnitude', 'Visibility'], na_values='*').fillna(0) # Prepare features X = dataset[['Stability', 'Error', 'Sign', 'Wind', 'Magnitude', 'Visibility']] y = dataset[['Auto']].values.reshape(1, -1)[0] model = LogisticRegression() model.fit(X, y) # For now, we're missing one important concept. We don't know how well our model # works, and because of that, we cannot really improve the performance of our hypothesis. # There are a lot of useful metrics, but for now, we will validate how well # our algorithm performs on the dataset it learned from. "Score of our model is %2.2f%%" % (model.score(X, y) * 100)

Score of our model is 73.33%

Convalida?

Nell'esempio precedente, abbiamo convalidato le prestazioni del nostro modello utilizzando i dati di apprendimento. Tuttavia, questa ora è una buona opzione, dato che il nostro algoritmo può sia underfit che overfit dei dati? Diamo un'occhiata all'esempio più semplice quando abbiamo una caratteristica che rappresenta le dimensioni di una casa e un'altra che rappresenta il suo prezzo.

 from sklearn.pipeline import make_pipeline from sklearn.preprocessing import PolynomialFeatures from sklearn.linear_model import LinearRegression from sklearn.model_selection import cross_val_score # Ground truth function ground_truth = lambda X: np.cos(15 + np.pi * X) # Generate random observations around the ground truth function n_samples = 15 degrees = [1, 4, 30] X = np.linspace(-1, 1, n_samples) y = ground_truth(X) + np.random.randn(n_samples) * 0.1 plt.figure(figsize=(14, 5)) models = {} # Plot all machine learning algorithm models for idx, degree in enumerate(degrees): ax = plt.subplot(1, len(degrees), idx + 1) plt.setp(ax, xticks=(), yticks=()) # Define the model polynomial_features = PolynomialFeatures(degree=degree) model = make_pipeline(polynomial_features, LinearRegression()) models[degree] = model # Train the model model.fit(X[:, np.newaxis], y) # Evaluate the model using cross-validation scores = cross_val_score(model, X[:, np.newaxis], y) X_test = X plt.plot(X_test, model.predict(X_test[:, np.newaxis]), label="Model") plt.scatter(X, y, edgecolor='b', s=20, label="Observations") plt.xlabel("x") plt.ylabel("y") plt.ylim((-2, 2)) plt.title("Degree {}\nMSE = {:.2e}".format( degree, -scores.mean())) plt.show() 

Gli stessi dati modellati da polinomi di primo, quarto e 30° grado, per dimostrare underfitting e overfitting.

Il modello dell'algoritmo di apprendimento automatico è inadeguato se non può generalizzare né i dati di addestramento né nuove osservazioni. Nell'esempio sopra, utilizziamo una semplice ipotesi lineare che non rappresenta realmente il set di dati di allenamento effettivo e avrà prestazioni molto scarse. Di solito, l'underfitting non viene discusso in quanto può essere facilmente rilevato con una buona metrica.

Se il nostro algoritmo ricorda ogni singola osservazione che è stata mostrata, avrà prestazioni scarse su nuove osservazioni al di fuori del set di dati di addestramento. Questo si chiama overfitting . Ad esempio, un modello polinomiale di 30° grado passa attraverso la maggior parte dei punti e ha un punteggio molto buono nel training set, ma qualsiasi cosa al di fuori di questo funzionerebbe male.

Il nostro set di dati è costituito da una caratteristica ed è semplice da tracciare nello spazio 2D; tuttavia, nella vita reale, potremmo avere set di dati con centinaia di caratteristiche, il che li rende impossibili da tracciare visivamente nello spazio euclideo. Quali altre opzioni abbiamo per vedere se il modello è underfitting o overfitting?

È tempo di introdurti al concetto di curva di apprendimento . Questo è un semplice grafico che traccia l'errore quadratico medio sul numero di campioni di addestramento.

Nei materiali didattici di solito vedrai grafici simili a questi:

Variazioni teoriche della curva di apprendimento in base al grado polinomiale.

Tuttavia, nella vita reale, potresti non ottenere un'immagine così perfetta. Tracciamo la curva di apprendimento per ciascuno dei nostri modelli.

 from sklearn.model_selection import learning_curve, ShuffleSplit # Plot learning curves plt.figure(figsize=(20, 5)) for idx, degree in enumerate(models): ax = plt.subplot(1, len(degrees), idx + 1) plt.title("Degree {}".format(degree)) plt.grid() plt.xlabel("Training examples") plt.ylabel("Score") train_sizes = np.linspace(.6, 1.0, 6) # Cross-validation with 100 iterations to get a smoother mean test and training # score curves, each time with 20% of the data randomly selected as a validation set. cv = ShuffleSplit(n_splits=100, test_size=0.2, random_state=0) model = models[degree] train_sizes, train_scores, test_scores = learning_curve( model, X[:, np.newaxis], y, cv=cv, train_sizes=train_sizes, n_jobs=4) train_scores_mean = np.mean(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) plt.plot(train_sizes, train_scores_mean, 'o-', color="r", label="Training score") plt.plot(train_sizes, test_scores_mean, 'o-', color="g", label="Test score") plt.legend(loc = "best") plt.show() 

Punteggi di formazione rispetto ai punteggi dei test per tre grafici con dati modellati da polinomi di primo, quarto e 30° grado.

Nel nostro scenario simulato, la linea blu, che rappresenta il punteggio dell'allenamento, sembra una linea retta. In realtà, diminuisce ancora leggermente: puoi effettivamente vederlo nel grafico polinomiale di primo grado, ma negli altri è troppo sottile per dirlo a questa risoluzione. Almeno vediamo chiaramente che c'è un enorme divario tra le curve di apprendimento per la formazione e le osservazioni dei test con uno scenario "ad alta distorsione".

Sul grafico del tasso di apprendimento "normale" al centro, puoi vedere come il punteggio dell'allenamento e le linee del punteggio del test si uniscono.

E sul grafico "alta varianza", puoi vedere che con un basso numero di campioni, i punteggi del test e dell'allenamento sono molto simili; tuttavia, quando si aumenta il numero di campioni, il punteggio di allenamento rimane quasi perfetto mentre il punteggio del test si allontana da esso.


Possiamo correggere modelli underfitting (chiamati anche modelli con alta distorsione ) se utilizziamo un'ipotesi non lineare, ad esempio, l'ipotesi con più caratteristiche polinomiali.

Il nostro modello di overfitting ( alta varianza ) passa attraverso ogni singolo esempio che viene mostrato; tuttavia, quando introduciamo i dati dei test, il divario tra le curve di apprendimento si allarga. Possiamo utilizzare la regolarizzazione, la convalida incrociata e più campioni di dati per correggere i modelli di overfitting.

Convalida incrociata

Una delle pratiche comuni per evitare l'overfitting è conservare parte dei dati disponibili e utilizzarli come set di test. Tuttavia, quando valutiamo diverse impostazioni del modello, come il numero di caratteristiche polinomiali, corriamo ancora il rischio di sovradimensionare il set di test perché i parametri possono essere modificati per ottenere le prestazioni ottimali dello stimatore e, per questo motivo, la nostra conoscenza del set di test può perdita nel modello. Per risolvere questo problema, dobbiamo mantenere un'altra parte del set di dati, che viene chiamato "set di convalida". Il training procede sul training set e, quando pensiamo di aver raggiunto le prestazioni ottimali del modello, possiamo effettuare una valutazione finale utilizzando il set di validazione.

Tuttavia, suddividendo i dati disponibili in tre set, riduciamo drasticamente il numero di campioni che possono essere utilizzati per l'addestramento dei modelli e i risultati possono dipendere da una particolare scelta casuale per la coppia di insiemi di addestramento-validazione.

Una soluzione a questo problema è una procedura chiamata convalida incrociata. Nella convalida incrociata standard di $k$-fold, partizioniamo i dati in sottoinsiemi di $k$, chiamati fold. Quindi, formiamo l'algoritmo in modo iterativo su $k-1$ fold usando il fold rimanente come set di test (chiamato "holdout fold").

Una griglia che mostra la posizione delle pieghe di controllo nella convalida incrociata k-fold.

La convalida incrociata consente di ottimizzare i parametri solo con il set di allenamento originale. Ciò ti consente di mantenere il set di test come un set di dati davvero invisibile per la selezione del modello finale.

Esistono molte più tecniche di convalida incrociata, come leave P out , stratificato $k$-fold , shuffle and split , ecc. ma vanno oltre lo scopo di questo articolo.

Regolarizzazione

Questa è un'altra tecnica che può aiutare a risolvere il problema dell'overfitting del modello. La maggior parte dei set di dati ha uno schema e del rumore. L'obiettivo della regolarizzazione è ridurre l'influenza del rumore sul modello.

Un grafico che giustappone una funzione originale e la sua controparte regolarizzata.

Esistono tre principali tecniche di regolarizzazione: Lazo, Tikhonov e rete elastica.

La regolarizzazione L1 (o regolarizzazione Lasso ) selezionerà alcune caratteristiche da ridurre a zero, in modo tale che non abbiano alcun ruolo nel modello finale. L1 può essere visto come un metodo per selezionare caratteristiche importanti.

La regolarizzazione L2 (o regolarizzazione Tikhonov ) costringerà tutte le caratteristiche a essere relativamente piccole, in modo tale da fornire una minore influenza sul modello.

La rete elastica è la combinazione di L1 e L2.

Normalizzazione (ridimensionamento delle funzionalità)

Anche il ridimensionamento delle funzionalità è un passaggio importante durante la preelaborazione dei dati. Il nostro set di dati potrebbe avere caratteristiche con valori $[-\infty, \infty]$ e altre caratteristiche con una scala diversa. Questo è un metodo per standardizzare gli intervalli di valori indipendenti.

Il ridimensionamento delle funzionalità è anche un processo importante per migliorare le prestazioni dei modelli di apprendimento. Prima di tutto, la discesa del gradiente convergerà molto più velocemente se tutte le caratteristiche vengono ridimensionate alla stessa norma. Inoltre, molti algoritmi, ad esempio le macchine vettoriali di supporto (SVM), funzionano calcolando la distanza tra due punti e se una delle caratteristiche ha valori ampi, la distanza sarà fortemente influenzata da questa funzione.

Supporta le macchine vettoriali

SVM è un altro algoritmo di apprendimento automatico ampiamente popolare che può essere utilizzato per problemi di classificazione e regressione. In SVM, tracciamo ogni osservazione come un punto nello spazio $n$-dimensionale dove $n$ è il numero di caratteristiche che abbiamo. Il valore di ogni caratteristica è il valore di coordinate particolari. Quindi, proviamo a trovare un iperpiano che separi abbastanza bene due classi.

Un grafico che mostra un iperpiano che separa due classi di punti dati, con anche illustrati alcuni dei loro vettori di supporto.

Dopo aver identificato il miglior iperpiano, vogliamo aggiungere dei margini, che separerebbero ulteriormente le due classi.

Un grafico che mostra un iperpiano con margini.

SVM è molto efficace quando il numero di funzionalità è molto elevato o se il numero di funzionalità è maggiore del numero di campioni di dati. Tuttavia, poiché SVM opera su base vettoriale, è fondamentale normalizzare i dati prima dell'utilizzo.

Reti neurali

Gli algoritmi delle reti neurali sono probabilmente il campo più interessante degli studi sull'apprendimento automatico. Le reti neurali cercano di imitare il modo in cui i neuroni del cervello sono collegati tra loro.

Un'illustrazione di una rete neurale, che mostra vari input mappati su valori temporanei, che a loro volta sono mappati su un singolo output.

Ecco come appare una rete neurale. Combiniamo molti nodi insieme in cui ogni nodo prende una serie di input, applichiamo alcuni calcoli su di essi e genera un valore.

Esiste un'enorme varietà di algoritmi di rete neurale per l'apprendimento sia supervisionato che non supervisionato. Le reti neurali possono essere utilizzate per guidare auto autonome, giocare, far atterrare aeroplani, classificare immagini e altro ancora.

Il famigerato Titanic

L'RMS Titanic era una nave passeggeri britannica affondata nell'Oceano Atlantico settentrionale il 15 aprile 1912 dopo la collisione con un iceberg. C'erano circa 2.224 membri dell'equipaggio e passeggeri e più di 1.500 morirono, rendendolo uno dei disastri marittimi commerciali più mortali di tutti i tempi.

Ora, poiché comprendiamo l'intuizione alla base dei più elementari algoritmi di apprendimento automatico utilizzati per problemi di classificazione, possiamo applicare le nostre conoscenze per prevedere l'esito della sopravvivenza per coloro che si trovano a bordo del Titanic.

Il nostro set di dati sarà preso in prestito dalla piattaforma di gare di data science Kaggle.

 import os from pandas import read_csv, concat # Load data data_path = os.path.join(os.getcwd(), "data/titanic.csv") dataset = read_csv(data_path, skipinitialspace=True) dataset.head(5)
Id Passeggero Sopravvissuto Classe P Nome Sesso Età SibSp Parch Biglietto Tariffa Cabina Imbarcato
0 1 0 3 Braund, signor Owen Harris maschio 22.0 1 0 A/5 21171 7.2500 Na N S
1 2 1 1 Cumings, la signora John Bradley (Florence Briggs Th... femmina 38.0 1 0 PC 17599 71.2833 C85 C
2 3 1 3 Heikkinen, signorina Laina femmina 26.0 0 0 STON/O2. 3101282 7.9250 Na N S
3 4 1 1 Futrelle, la signora Jacques Heath (Lily May Peel) femmina 35.0 1 0 113803 53.1000 C123 S
4 5 0 3 Allen, il signor William Henry maschio 35.0 0 0 373450 8.0500 Na N S

Il nostro primo passo sarebbe caricare ed esplorare i dati. Abbiamo 891 record di test; ogni record ha la seguente struttura:

  • passeggeroId – ID del passeggero a bordo
  • sopravvivenza - Indipendentemente dal fatto che la persona sia sopravvissuta o meno all'incidente
  • pclass – Classe del biglietto, ad es. 1a, 2a, 3a
  • genere – Sesso del passeggero: maschio o femmina
  • nome – Titolo incluso
  • età – Età in anni
  • sibsp – Numero di fratelli/coniugi a bordo del Titanic
  • parch – Numero di genitori/figli a bordo del Titanic
  • biglietto – Numero del biglietto
  • tariffa – Tariffa passeggeri
  • cabina – Numero cabina
  • imbarcato – Porto di imbarco

Questo set di dati contiene dati sia numerici che categoriali. Di solito, è una buona idea approfondire i dati e, sulla base di ciò, elaborare ipotesi. Tuttavia, in questo caso, salteremo questo passaggio e andremo direttamente alle previsioni.

 import pandas as pd # We need to drop some insignificant features and map the others. # Ticket number and fare should not contribute much to the performance of our models. # Name feature has titles (eg, Mr., Miss, Doctor) included. # Gender is definitely important. # Port of embarkation may contribute some value. # Using port of embarkation may sound counter-intuitive; however, there may # be a higher survival rate for passengers who boarded in the same port. dataset['Title'] = dataset.Name.str.extract(' ([A-Za-z]+)\.', expand=False) dataset = dataset.drop(['PassengerId', 'Ticket', 'Cabin', 'Name'], axis=1) pd.crosstab(dataset['Title'], dataset['Sex'])
Title \ Sex female male
Capt 0 1
Col 0 2
Countess 1 0
Don 0 1
Dr 1 6
Jonkheer 0 1
signora 1 0
Maggiore 0 2
Master 0 40
Mancare 182 0
Mlle 2 0
Mme 1 0
Mr 0 517
Mrs 125 0
SM 1 0
Rev 0 6
Sir 0 1
 # We will replace many titles with a more common name, English equivalent, # or reclassification dataset['Title'] = dataset['Title'].replace(['Lady', 'Countess','Capt', 'Col',\ 'Don', 'Dr', 'Major', 'Rev', 'Sir', 'Jonkheer', 'Dona'], 'Other') dataset['Title'] = dataset['Title'].replace('Mlle', 'Miss') dataset['Title'] = dataset['Title'].replace('Ms', 'Miss') dataset['Title'] = dataset['Title'].replace('Mme', 'Mrs') dataset[['Title', 'Survived']].groupby(['Title'], as_index=False).mean()
Titolo Survived
0 Master 0.575000
1 Mancare 0.702703
2 Mr 0.156673
3 Mrs 0.793651
4 Altro 0.347826
 # Now we will map alphanumerical categories to numbers title_mapping = { 'Mr': 1, 'Miss': 2, 'Mrs': 3, 'Master': 4, 'Other': 5 } gender_mapping = { 'female': 1, 'male': 0 } port_mapping = { 'S': 0, 'C': 1, 'Q': 2 } # Map title dataset['Title'] = dataset['Title'].map(title_mapping).astype(int) # Map gender dataset['Sex'] = dataset['Sex'].map(gender_mapping).astype(int) # Map port freq_port = dataset.Embarked.dropna().mode()[0] dataset['Embarked'] = dataset['Embarked'].fillna(freq_port) dataset['Embarked'] = dataset['Embarked'].map(port_mapping).astype(int) # Fix missing age values dataset['Age'] = dataset['Age'].fillna(dataset['Age'].dropna().median()) dataset.head()
Survived Pclass Sesso Età SibSp Parch Tariffa Embarked Titolo
0 0 3 0 22.0 1 0 7.2500 0 1
1 1 1 1 38.0 1 0 71.2833 1 3
2 1 3 1 26.0 0 0 7.9250 0 2
3 1 1 1 35.0 1 0 53.1000 0 3
4 0 3 0 35.0 0 0 8.0500 0 1

At this point, we will rank different types of machine learning algorithms in Python by using scikit-learn to create a set of different models. It will then be easy to see which one performs the best.

  • Logistic regression with varying numbers of polynomials
  • Support vector machine with a linear kernel
  • Support vector machine with a polynomial kernel
  • Neural network

For every single model, we will use $k$-fold validation.

 from sklearn.model_selection import KFold, train_test_split from sklearn.pipeline import make_pipeline from sklearn.preprocessing import PolynomialFeatures, StandardScaler from sklearn.neural_network import MLPClassifier from sklearn.svm import SVC # Prepare the data X = dataset.drop(['Survived'], axis = 1).values y = dataset[['Survived']].values X = StandardScaler().fit_transform(X) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state = None) # Prepare cross-validation (cv) cv = KFold(n_splits = 5, random_state = None) # Performance p_score = lambda model, score: print('Performance of the %s model is %0.2f%%' % (model, score * 100)) # Classifiers names = [ "Logistic Regression", "Logistic Regression with Polynomial Hypotheses", "Linear SVM", "RBF SVM", "Neural Net", ] classifiers = [ LogisticRegression(), make_pipeline(PolynomialFeatures(3), LogisticRegression()), SVC(kernel="linear", C=0.025), SVC(gamma=2, C=1), MLPClassifier(alpha=1), ]
 # iterate over classifiers models = [] trained_classifiers = [] for name, clf in zip(names, classifiers): scores = [] for train_indices, test_indices in cv.split(X): clf.fit(X[train_indices], y[train_indices].ravel()) scores.append( clf.score(X_test, y_test.ravel()) ) min_score = min(scores) max_score = max(scores) avg_score = sum(scores) / len(scores) trained_classifiers.append(clf) models.append((name, min_score, max_score, avg_score)) fin_models = pd.DataFrame(models, columns = ['Name', 'Min Score', 'Max Score', 'Mean Score'])
 fin_models.sort_values(['Mean Score']).head()
Nome Min Score Max Score Mean Score
2 Linear SVM 0.793296 0.821229 0.803352
0 Logistic Regression 0.826816 0.860335 0.846927
4 Neural Net 0.826816 0.860335 0.849162
1 Logistic Regression with Polynomial Hypotheses 0.854749 0.882682 0.869274
3 RBF SVM 0.843575 0.888268 0.869274

Ok, so our experimental research says that the SVM classifier with a radial basis function (RBF) kernel performs the best. Now, we can serialize our model and re-use it in production applications.

 import pickle svm_model = trained_classifiers[3] data_path = os.path.join(os.getcwd(), "best-titanic-model.pkl") pickle.dump(svm_model, open(data_path, 'wb'))

Machine learning is not complicated, but it's a very broad field of study, and it requires knowledge of math and statistics in order to grasp all of its concepts.

Right now, machine learning and deep learning are among the hottest topics of discussion in Silicon Valley, and are the bread and butter of almost every data science company, mainly because they can automate many repetitive tasks including speech recognition, driving vehicles, financial trading, caring for patients, cooking, marketing, and so on.

Now you can take this knowledge and solve challenges on Kaggle.

This was a very brief introduction to supervised machine learning algorithms. Luckily, there are a lot of online courses and information about machine learning algorithms. I personally would recommend starting with Andrew Ng's course on Coursera.

Risorse

  • Andrew Ng's course on Coursera
  • Kaggle datasets
  • A deep learning reading list
  • A list of free books on machine learning algorithms, data mining, deep learning, and related topics
  • Un'introduzione alla teoria dell'apprendimento automatico e alle sue applicazioni: un tutorial visivo con esempi
Related: Machines and Trust: How to Mitigate AI Bias