×
COME FUNZIONA IL PERCEPTRON IN MACHINE LEARNING

INDICE

1. Implementazione Perceptron con Python

2. Training del modello di Perpectron

3. Visualizzare la classificazione

Uno dei primi algoritmi descritti agli albori del Machine Learning, utilizzato per compiti di classificazione, fu il Perceptron.

Warren McCullock e Walter Pitts pubblicarono nel 1943 un primo schema di cellula del cervello, il neurone di McCullock-Pitts.

I neuroni sono le nostre cellule nervose le quali, attraverso segnali chimici ed elettrici, permettono il passaggio e l'elaborazione di "dati" all'interno del nostro cervello.

Il neurone di McCullock-Pitts presenta un gate logico con output binario; ai dendriti arrivano i segnali, entrano nel nucleo della cellula e se il segnale supera una certa soglia di attivazione, allora la cellula produce un segnale di output passato tramite l'assone che viene propagato come segnale di output dai terminali dell'assone.

Pochi anni dopo Frank Rosenblatt pubblicò il primo concetto di apprendimento basato su Perceptron. Rosenblatt propose un algoritmo che avrebbe appreso i coefficienti di peso ottimali da moltiplicare con i segnali di input, in maniera tale da decretare se il neurone si dovesse attivare oppure no.

Nel contesto del Machine Learning un algoritmo di questo tipo potrebbe assegnare l'appartenenza di un campione ad una determinata classe.

Immagine neurone
Immagine neurone

Questo problema può essere classificato in maniera binaria, dove due classi, una positiva 1, ed una negativa -1, possono essere decretate attraverso una funzione di attivazione 𝝓(z). Tale funzione può prendere una combinazione lineare di valori di input x e un corrispondente vettore di pesi w, dove z rappresenta l'input della rete.

$$(z = w_{1} x_{1} + ... + w_{m} x_{m})$$

$ w = left[
egin{matrix}
w_{1} \
vdots \
w_{m}
end{matrix}
ight] $

$ x = left[
egin{matrix}
x_{1} \
vdots \
x_{m}
end{matrix}
ight] $

Se un campione $x^{i}$ è maggiore della soglia di attivazione determinata con 𝝓, allora il campione rientrerà nella classe 1, altrimenti nel caso fosse minore alla soglia verrebbe classificato in  -1.

Nell'algoritmo di Perceptron, la funzione di attivazione è una semplice funzione di passo unitario, chiamata a volte funzione di passo Heaviside.

$$ 𝝓(z) =
egin{cases}
1  & ext{if $z$ is ≥ θ} \
-1 & ext{altrimenti}
end{cases} $$

Input della rete
Input della rete

L'input della rete $z = w^{T} x$ passando per la funzione di attivazione del Perceptron, viene classificato come output binario in -1 o 1. Questo algoritmo permette di discriminare tra due classi separabili linearmente.

Convergenza Perceptron
Convergenza Perceptron

La convergenza del Perceptron avviene solo nei casi in cui le due classi risultino separabili linearmente e se il tasso di apprendimento è ridotto. Si può definire eventualmente un numero di passi sul dataset di apprendimento (epoch) e una soglia di classificazioni errate. In caso contrario il Perceptron continuerebbe sempre a correggere i pesi.

Schema funzionamento Perceptron
Schema funzionamento Perceptron

Durante la fase di apprendimento l'output serve per calcolare l'errore nella previsione e per aggiornare di conseguenza i pesi.

Implementazione Perceptron con Python

Il seguente codice addestra un modello del Perceptron sul dataset Iris. Le due caratteristiche che verranno considerate nella visualizzazione sono la lunghezza del sepalo (sepal length) e del petalo (petal length).

Il seguente codice è dato dalla classificazione attraverso l'algoritmo classico del Perceptron che troviamo in "Python Machine Learning by Sebastian Raschka, 2015".

Per comodità consiglio l'utilizzo dello strumento Colab per l'implementazione del seguente codice.

import pandas as pd
df = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data', header=None)
df.tail()
Vengono estratte le prime 100 etichette delle classi di fiori corrispondenti a 50 Iris Setosa e 50 Iris Versicolor
Vengono estratte le prime 100 etichette delle classi di fiori corrispondenti a 50 Iris Setosa e 50 Iris Versicolor
import matplotlib.pyplot as plt
import numpy as np
y = df.iloc[0:100, 4].values
y

#Risultato
array(['Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-setosa', 'Iris-setosa',
       'Iris-setosa', 'Iris-setosa', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor',
       'Iris-versicolor', 'Iris-versicolor', 'Iris-versicolor'],
      dtype=object)


Vengono convertite le due etichette delle classi Setosa e Versicolor in due classi intere 1 e -1, le quali vengono assegnate ad un vettore y dove il metodo dei valori di un padas DataFrame fornisce la corrispondente rappresentazione NumPy.

y = np.where(y == 'Iris-setosa', -1, 1)
y

#Risultato
array([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
       -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  1,
        1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
        1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
        1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1])

Vengono estratte la prima colonna delle caratteristiche (sepal length) e la terza colonna di caratteristiche (petal length) dei 100 campioni di addestramento. Vengono poi assegnati tali valori ad una matrice X che viene rappresentata tramite un diagramma a dispersione bidimensionale.

X = df.iloc[0:100, [0, 2]].values
X

#Risultato
array([[5.1, 1.4],
       [4.9, 1.4],
       [4.7, 1.3],
        ........
       [5.1, 3. ],
       [5.7, 4.1]])

Attraverso matplotlib viene visualizzato il diagramma a dispersione.

plt.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('petal length')
plt.ylabel('sepal length')
plt.legend(loc='upper left')
plt.show()
Diagramma a dispersione
Diagramma a dispersione

Training del modello di Perpectron

Viene addestrato l'algoritmo di Perceptron sul sottoinsieme dei dati del dataset Iris. Ad ogni epoca viene tracciato l'errore di mancata classificazione, al fine di controllare che l'algoritmo converga e riesca a trovare un confine decisionale per le due classi di fiori Iris.

Scarica il file perceptron.py
from google.colab import files
src = list(files.upload().values())[0]
open('perceptron.py','wb').write(src)
import perceptron
from perceptron import Perceptron

pn = Perceptron(0.1, 10)
pn.fit(X, y)
plt.plot(range(1, len(pn.errors) + 1), pn.errors, marker='o')
plt.xlabel('Epoche')
plt.ylabel('Numero errori classificazione')
plt.show()
Grafico degli errori di classificazione rispetto al numero di epoche.
Grafico degli errori di classificazione rispetto al numero di epoche.

Il Perceptron converge dopo 6 epoche (iterazioni). Adesso è possibile fare una classificazione.

Visualizzare la classificazione

Viene implementata una funzione per rappresentare il confine decisionale.

from matplotlib.colors import ListedColormap

def 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.4, cmap=cmap)
   plt.xlim(xx1.min(), xx1.max())
   plt.ylim(xx2.min(), xx2.max())

   # plot class samples
   for idx, cl in enumerate(np.unique(y)):
      plt.scatter(x=X[y == cl, 0], y=X[y == cl, 1],
      alpha=0.8, c=cmap(idx),
      marker=markers[idx], label=cl)

Nel codice sopra, definiamo un numero di colori e marcatori e creiamo una mappa dei colori dall'elenco dei colori tramite ListedColormap.

Determiniamo i valori minimo e massimo per le due caratteristiche e utilizziamo questi vettori per creare una coppia di matrici della griglia xx1 e xx2 tramite la funzione meshgrid di NumPy.

Poiché abbiamo addestrato il nostro classificatore perceptron su due dimensioni di caratteristiche, dobbiamo appiattire gli array a griglia e creare una matrice che abbia lo stesso numero di colonne del sottoinsieme di training set Iris in modo da poter utilizzare il metodo predict per prevedere le etichette di classe Z del punti sulla griglia.

Dopo aver rimodellato le etichette della classe prevista Z in una griglia con le stesse dimensioni di xx1 e xx2, possiamo disegnare un diagramma di contorno tramite la funzione di contorno di matplotlib che mappa le diverse regioni di decisione su colori diversi per ogni classe prevista nell'array a griglia:

plot_decision_regions(X, y, classifier=pn)
plt.xlabel('sepal length [cm]')
plt.ylabel('petal length [cm]')
plt.legend(loc='upper left')
plt.show()
Grafico regioni decisionali per la classificazione del sottoinsieme di addestramento Iris
Grafico regioni decisionali per la classificazione del sottoinsieme di addestramento Iris

Conclusione

In questo breve articolo abbiamo visto come implementare l'algoritmo del Perceptron attraverso l'utilizzo di Python.

Cerca

Vuoi scrivere per noi? Guadagna scrivendo articoli per il nostro blog!