Adaline in Python
Dopo aver analizzato in maniera scrupolosa il concetto di Adaline e la possibilità di ottimizzare l’algoritmo attraverso l’utilizzo della discesa del gradiente, implementiamo attraverso il linguaggio Python proprio l’algoritmo trattato in precedenza utilizzando buona parte del codice del Perceptron adattando il gradiente all’interno del metodo fit.
class AdalineGD(object):
"""ADAptive LInear NEuron classifier.Parameters
------------
eta : float
Learning rate (between 0.0 and 1.0)
n_iter : int
Passes over the training dataset.
random_state : int
Random number generator seed for random weight
initialization.Attributes
-----------
w_ : 1d-array
Weights after fitting.
cost_ : list
Sum-of-squares cost function value in each epoch."""
def __init__(self, eta=0.01, n_iter=50, random_state=1):
self.eta = eta
self.n_iter = n_iter
self.random_state = random_statedef fit(self, X, y):
""" Fit training data.Parameters
----------
X : {array-like}, shape = [n_examples, n_features]
Training vectors, where n_examples is the number of examples and
n_features is the number of features.
y : array-like, shape = [n_examples]
Target values.Returns
-------
self : object"""
rgen = np.random.RandomState(self.random_state)
self.w_ = rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1])
self.cost_ = []for i in range(self.n_iter):
net_input = self.net_input(X)
output = self.activation(net_input)
errors = (y - output)
self.w_[1:] += self.eta * X.T.dot(errors)
self.w_[0] += self.eta * errors.sum()
cost = (errors**2).sum() / 2.0
self.cost_.append(cost)
return selfdef net_input(self, X):
"""Calculate net input"""
return np.dot(X, self.w_[1:]) + self.w_[0]def activation(self, X):
"""Compute linear activation"""
return Xdef predict(self, X):
"""Return class label after unit step"""
return np.where(self.activation(self.net_input(X)) >= 0.0, 1, -1)
Nel Perceptron aggiorniamo i pesi dopo aver valutato ogni singolo esempio di addestramento. In Adaline invece calcoliamo il gradiente sull’intero dataset di addestramento tramite self.eta*errors.sum() per la bias unit (peso zero) mentre per i pesi da 1 a m self.eta*X.T.dot(errors), dove X.T.dot(errors) è una moltiplicazione matrice-vettore fra la nostra matrice delle caratteristiche e il vettore degli errori.
Get Manuelenzo’s stories in your inbox
Join Medium for free to get updates from this writer.
Notiamo inoltre che il metodo activation è una semplice funzione identità e quindi non comporta alcun effetto al codice. Vediamo come abbiamo rispettato il concetto di neurone adattivo lineare, infatti le caratteristiche vengono passate all’input della rete, successivamente alla funzione di attivazione e solamente alla fine viene mostrato l’output.
Per controllare che l’algoritmo converga in maniera ottimale andiamo a inserire i valori di costo in una lista self.cost_ , come per il nostro vecchio Perceptron.
Utilizziamo due tassi di apprendimento relativamente bassi, ovvero 0.1 e 0.0001. In questo modo riusciamo a capire se l’algoritmo Adaline impara dai dati di addestramento.
Risultati finali e Convergenza in Adaline ?
Tracciamo ora il grafico che rappresenta il costo rispetto al numero di epoche per i due tassi di apprendimento:
fig, ax = plt.subplots(nrows=1, ncols=2, figsize=(10, 4))ada1 = AdalineGD(n_iter=10, eta=0.01).fit(X, y)
ax[0].plot(range(1, len(ada1.cost_) + 1), np.log10(ada1.cost_), marker='o')
ax[0].set_xlabel('Epochs')
ax[0].set_ylabel('log(Sum-squared-error)')
ax[0].set_title('Adaline - Learning rate 0.01')ada2 = AdalineGD(n_iter=10, eta=0.0001).fit(X, y)
ax[1].plot(range(1, len(ada2.cost_) + 1), ada2.cost_, marker='o')
ax[1].set_xlabel('Epochs')
ax[1].set_ylabel('Sum-squared-error')
ax[1].set_title('Adaline - Learning rate 0.0001')plt.show()
Questo è il risultato che ci viene fornito.
Il grafico a sinistra mostra come l’algoritmo non riesca a convergere e anzi, a causa dell’ overshoot, l’errore diviene sempre più grande e la funzione non viene minimizzata.
Il grafico a destra invece mostra ciò che accade con un tasso di apprendimento molto piccolo. L’algoritmo Adaline converge al minimo globale del costo ma solo dopo troppe epoche (10).
L’immagine ci mostra come, nella parte sinistra l’algoritmo converga in maniera ottimale con l’utilizzo di un buon tasso di apprendimento, mentre, nella parte destra manchiamo sempre il minimo globale a causa della scelta di tasso di apprendimento troppo ampio.
Originally published at https://www.codinglife.it.







