Adaline: ottimizzazione con la normalizzazione del dataset

Prendendo spunto dal paragrafo precedente per ottimizzare l’algoritmo di Adaline in modo che la funzione costo venga minimizzata in maniera più veloce allora è possibile standardizzare l’intero dataset con la normalizzazione delle caratteristiche.

In che modo possiamo normalizzare? usando la distribuzione normale standard. la distribuzione normale standard prevede

distribuzione normale standard

Per normalizzare il dataset basta prendere ogni caratteristica, sottrarla per la media della distribuzione normale μ e dividere tutto per la deviazione standard σ

Ad esempio, per standardizzare la caratteristica j-esima è necessario prima sottrarre la media μj poi dividere per σj .

xj = (xj – μj)/σj

Addestriamo ora lo stesso algoritmo Adaline utilizzato nel paragrafo precedente con il dataset normalizzato. La libreria numPy ha già funzioni per calcolare la deviazione standard e la media.

Anzitutto vediamo nel grafico i valori normalizzati,

dataset normalizzato

Addestriamo l’algoritmo con questi dati, dopo di che, osserviamo il grafico dei costi

pn = Adaline(0.01, 15)
pn.fit(X_std, y)

funzione dei costi

Il grafico mostra chiaramente la convergenza della funzione dei costi a fronte di un dataset normalizzato.

Giusto per fare un confronto mostriamo lo stesso grafico dei costi addestrando l’algoritmo con il dataset non normalizzato.

funzione di costo con dataset non normalizzato

Come volevasi dimostrare, la funzione di costo viene nettamente ottimizzata utilizzando il dataset normalizzato.

Adaline: MachineLearning con supervisione, algoritmo adattivo lineare

L’algoritmo Adaline (ADaptive LInear NEuron) è un algoritmo di convergenza dell’apprendimento. Ciò vuol dire che i pesi non si aggiornano ad ogni epoch sulla base di una funzione di attivazione binaria (come nel caso del Perceptron), ma si aggiornano sulla base di una funzione di attivazione lineare.

Come vediamo nel grafico sopra, la differenza con l’algoritmo Perceptron è che i pesi si aggiornano attraverso una funzione lineare ad ogni epoch. Questo consente di minimizzare la funzione di costo.

La funzione di costo è la funzione J che apprende i pesi in termini di somma dei quadrati degli errori (Sum Squared Errors – SSE) fra il risultato calcolato e la vera etichetta della classe.

J(w) = 1/2 Σi (y(i) – Φ (z(i)))2

Quali sono i vantaggi di questa funzione? Questa funzione di costo è differenziabile ed è convessa. Questo ci consente di utilizzare un algoritmo di discesa del gradiente per trovare i pesi che minimizzano la funzione di costo.

Come si può vedere nel grafico sopra, i pesi si aggiornano in base alla funzione di costo descritto in precedenza. A partire quindi da un peso iniziale (w), i pesi si aggiorneranno fino a raggiungere il minimo dei costi globale ed allontanandoci quindi dal gradJ(w)

Il cambiamento del peso è definito in questo modo:

Δ(w)= -η gradJ(w)

Per calcolare il gradiente è necessario fare la derivata parziale della funzione J ad ogni peso wi. Il vantaggio dell’algoritmo è che i pesi vengono aggiornati simultaneamente a differenza del Perceptron che venivano aggiornati ad ogni iterazione.

PERCEPTRON: come simulare un neurone

Immaginiamo di modellare un neurone artificiale come un elaboratore che può avere segnali in input e segnali di output. Il segnale entra attraverso i dendriti della cellula ed attraverso l’assone arriva fino ai terminali. Quando il segnale in input supera una determinata soglia allora si attiva il neurone e l’output è positivo. Al contrario l’output rimane negativo.

Il problema descritto sopra può essere rappresentato come un compito di classificazione binaria (+1; -1). I dati in ingresso entrano dentro l’elaboratore, che modelliamo con una funzione di attivazione Θ(z) e dove z è l’input della rete.

La funzione di attivazione che vogliamo utilizzare è a passo unitario, ovvero:

Avendo un serie di campioni che entrano nell’elaboratore (dataset) possiamo descrivere l’input in forma vettoriale:

z=w1 x1 + w2 x2 + w3 x3 + w4 x4 +…+ wm xm

wn-> pesi della funzione

x-> rappresentano le caratteristiche del campione e sono espresse in forma vettoriale

In linea teorica noi dovremmo trovare un algoritmo che dato in ingresso un serie di dati, classifica ogni campione sulla base delle caratteristiche. Il modello che intendiamo analizzare è un classificatore binario, quindi, preso in ingresso una serie di dati, in base alle caratteristiche, produce una classificazione binaria (1, -1).

Una volta “addestrato” il modello per produrre la classificazione binaria allora, ad ogni nuovo campione in ingresso, siamo in grado di predirne la classificazione (1, -1).

La classificazione è il risultato della funzione di attivazione che preso in ingresso l’input della rete si attiva soltanto se superiamo una determinata soglia θ:

Θ(z)=1 if z>=θ

Θ(z)=-1 altrimenti

oppure, in forma più compatta, portando la soglia alla sinistra dell’equazione:

Θ(z)=1 if z>=θ –> z-θ>=0

z-θ=w1 x1 + w2 x2 + w3 x3 + w4 x4 +…+ wm xm -> z= θ + w1 x1 + w2 x2 + w3 x3 + w4 x4 +…+ wm xm

L’ algoritmo perceptron che emula il neurone si comporterà come segue:

  1. inizializzazione dei pesi a 0 o numeri casuali piccoli
  2. calcolo del valore output y: etichetta della classe prevista
  3. aggiornamento dei pesi: wj = wj + Δwj, dove Δwj=η(y(i) – y-(i))x(i)j

Dalle formule è evidente che se una predizione risulta errata allora l’aggiornamento del peso avviene in modo proporzionale nella direzione della classe target corretta.

Ad esempio, se abbiamo un campione x(i)j = 2 e l’abbiamo predetto nella classe errata y-(i)= -1 ed il tasso di apprendimento η=1, allora, l’aggiornamento del peso risulta essere:

Δwj=η(y(i) – y-(i))x(i)j = Δwj=1(1 – -1)2 = 4, l’algoritmo aggiorna i pesi in maniera proporzionale verso la classe corretta.

In conclusione, il perceptron, è un algoritmo di classificazione binaria in grado di predire una determinata classe attraverso le caratteristiche del campione.