Perceptron in Python
In questo progetto andiamo a implementare l’algoritmo Perceptron attraverso l’utilizzo del linguaggio di programmazione Python, applicandola al dataset Iris che consiste in 150 istanze di fiore Iris classificate secondo tre specie:
- Iris setosa
- Iris virginica
- Iris versicolor
Le quattro variabili considerate sono la lunghezza e la larghezza sia del sepalo che del petalo .
Dettagli dell’implementazione
Per l’implementazione dell’interfaccia del Perceptron utilizzeremo un approccio ojected-oriented, in modo tale da creare due metodi:
- fit — che permette all’oggetto Perceptron di imparare dai dati;
- predict — che permette all’oggetto Perceptron di fare delle predizioni;
import numpy as npclass Perceptron(object):
"""Perceptron 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.
errors_ : list
Number of misclassifications (updates) 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.errors_ = []for _ in range(self.n_iter):
errors = 0
for xi, target in zip(X, y):
update = self.eta * (target - self.predict(xi))
self.w_[1:] += update * xi
self.w_[0] += update
errors += int(update != 0.0)
self.errors_.append(errors)
return selfdef net_input(self, X):
"""Calculate net input"""
return np.dot(X, self.w_[1:]) + self.w_[0]def predict(self, X):
"""Return class label after unit step"""
return np.where(self.net_input(X) >= 0.0, 1, -1)
Questa è l’implementazione del nostro Perceptron in Python, attraverso la quale andiamo ad inizializzare un nuovo oggetto Perceptron con un tasso di apprendimento eta (η) e un determinato numero di epoche, n_iter.
Attraverso il metodo fit inizializziamo i pesi in self.w_ con un vettore R⁽ᵐ⁺¹⁾, dove m è il numero di caratteristiche presenti nel dataset con l’aggiunta di 1 per il primo elemento del vettore che rappresenta il bias unit.
Il primo elemento del vettore, self.w_[0], rappresenta la bias unit ovvero la soglia negativa, o peso, wₒ = -θ.
Esso contiene piccoli numeri casuali tratti da una distribuzione normale con deviazione standard 0,01 tramite rgen.normal(loc=0.0, scale=0.01, size=1 + X.shape[1]), dove rgen è un generatore di numeri casuali di NumPy.
Detto ciò, è importante capire che non vanno inizializzati a 0 i pesi, perchè il tasso di appredimento (η) ha effetto sul risultato della classificazione solo se i pesi sono inizializzati ad un numero diverso da 0. In caso contrario infatti, se tutti i pesi fossero inizializzati a 0, eta agirebbe solo solo sulla scala del vettore dei pesi, non sulla direzione.
Parlando di trigonometria
Consideriamo:
- vettore v1 = [1 2 3]
- l’angolo fra v1 e v2 = 0,5 * v1
- il risultato esattamente 0.
v1 = np.array([1, 2, 3])
v2 = 0.5 * v1
np.arccos(v1.dot(v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))La funzione np.arcos è l’inversa trigonometrica del coseno ovvero la funzione arcseno, mentre la funzione np.linalg.norm permette di calcolare la lunghezza di un vettore.
Parlando di numeri :
Get Manuelenzo’s stories in your inbox
Join Medium for free to get updates from this writer.
np.arccos(4.5 / (3 x (0.5 x 3))) = np.arccos(1) = 0.0
Fit e Predict
Dopo che i pesi vengono inizializzati, il metodo fit che serve per addestrare il modello grazie ai dati, agisce su tutto l’intero dataset di addestramento. Una volta terminato il loop vengono riaggiornati i pesi.
Le etichette delle classi vengono predette dalla funzione predict che è richiamata all’interno di fit in modo da ottenere l’etichetta della classe per aggiornare i pesi.
In self.errors_ inseriamo tutte le classificazioni errate in modo da valutare l’efficacia del nostro Perceptron e attraverso la funzione np.dot andiamo a eseguire il prodotto scalare tra vettori.
Addestriamo il Perceptron sul dataset Iris
In questo esempio, dove utilizziamo il dataset Iris, andremo a prendere in considerazione solo due caratteristiche, la lunghezza del petalo e del sepalo per le due sole classi di fiori Versicolor e Setosa. In questo modo possiamo rappresentare i dati in maniera più pulita a grafico ed essendo un esempio pratico dove non vi è richiesto alcun obbiettivo particolare, possiamo approfondire meglio il concetto di Perceptron e quello di algoritmo di apprendimento.
import os
import pandas as pds = 'https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data'
print('URL:', s)df = pd.read_csv(s,
header=None,
encoding='utf-8')df.tail()
L’utilizzo della libreria Pandas , scaricabile tramite il comando pip/pip3 install pandas , è necessaria per caricare il dataset Iris in un oggetto Dataframe e attraverso il comando tail andiamo ad estrapolare le ultime 5 righe del dataset.
Estraiamo le prime 100 etichette delle classi che corrispondono a 50 fiori Iris-setosa e 50 Iris-versicolor e convertiamo le etichette delle classi in due etichette intere 1 versicolor e -1 setosa . Esse vengono assegnate ad un vettore y. A questo punto, estraiamo la prima colonna di caratteristiche (lunghezza del sepalo) e la terza colonna (lunghezza del petalo) dei primi 100 esempi e le assegniamo a una matrice X che visualizziamo tramite un grafico a dispersione bidimensionale:
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as npy = df.iloc[0:100, 4].values
y = np.where(y == 'Iris-setosa', -1, 1)X = df.iloc[0:100, [0, 2]].valuesplt.scatter(X[:50, 0], X[:50, 1],
color='red', marker='o', label='setosa')
plt.scatter(X[50:100, 0], X[50:100, 1],
color='blue', marker='x', label='versicolor')plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')plt.show()
Dal grafico qui sopra notiamo come basti un solo confine decisionale per separare le due classi, Setosa e Versicolor. Detto ciò non ci resta che adddestrare l’algoritmo Perceptron sulla base del dataset estratto, tracciando l’errore di classificazione per ciascuna epoca in modo da vedere se l’algoritmo converge e riesca effettivamente a trovare il confine decisionale che divida le due classi di fiori Iris:
ppn = Perceptron(eta=0.1, n_iter=10)ppn.fit(X, y)plt.plot(range(1, len(ppn.errors_) + 1), ppn.errors_, marker='o')
plt.xlabel('Epochs')
plt.ylabel('Number of updates')plt.show()
L’addestramento ci permette di vedere come la convergenza del Perceptron avvenga dopo la sesta epoch. A questo punto, integrando una funzione per il tracciamento del confine decisionale su dataset binari:
from matplotlib.colors import ListedColormapdef plot_decision_regions(X, y, classifier, resolution=0.02):# setup marker generator and color map
markers = ('s', 'x', 'o', '^', 'v')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan')
cmap = ListedColormap(colors[:len(np.unique(y))])# plot the decision surface
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution))
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T)
Z = Z.reshape(xx1.shape)
plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap)
plt.xlim(xx1.min(), xx1.max())
plt.ylim(xx2.min(), xx2.max())# plot class examples
for idx, cl in enumerate(np.unique(y)):
plt.scatter(x=X[y == cl, 0],
y=X[y == cl, 1],
alpha=0.8,
c=colors[idx],
marker=markers[idx],
label=cl,
edgecolor='black')plot_decision_regions(X, y, classifier=ppn)
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')
plt.show()
Come possiamo vedere dal codice, andiamo a definire una ListedColormap con un certo numero di colors e markers . Successivamente determiniamo i valori minimo e massimo per le due caratteristiche per creare una coppia di array a griglia, xx1 e xx2 tramite funzione meshgrid . L’algoritmo Perceptron è addestrato su due dimensioni di caratteristiche quindi creiamo una matrice che abbia lo stesso numero di colonne del sottoinsieme di addestramento in modo da impiegare il metodo predict per predire le etichette delle classi.
Le etichette vengono inserite all’interno di una griglia avente le stesse dimensioni di xx1 e xx2 e a questo punto possiamo tracciare un grafico a contorni tramite la funzione Matplotlib contourf che mappa le varie regioni decisionali.
✅ Vediamo così dal grafico che il Perceptron ha appreso un confine decisionale tale da separare in maniera corretta le due classi di fiori.
Originally published at https://www.codinglife.it.






