A serious post?!

Credeteci signori, sto per scrivere un post serio su questo blog.

IL PERCETTRONE!

Dunque, saltiamo a pie’ pari l’introduzione alle reti neurali che fanno un po’ tutti i blog, fuffa di come effettivamente si utilizzi il modello del cervello. Dico io, fuck theory, we want facts!

Ma, ahimè, il modello del percettrone un po’, giusto un pochetto, lo devo descrivere.
Ordunque, il percettrone può essere considerato come il primo modello di rete neurale sviluppato (citazione necessaria).
Ha un sacco, un sacchissimo, un strasacco, un megaiperstrasacco di limitazioni, ma tant’è che come inizio per una rete neurale non è male.

Cosa è un percettrone?
È un modello di rete a più input, un solo output e con solo uno strato (su quanti layer siete miei dudi? 1! Te lo ho appena detto).

L’immagine, rubata dall’internet perché non ho voglia di farla, ne illustra in breve il modello.

Siori e siori: il percettrone!

Tentiamo di spiegare un attimo di cosa stiamo parlando riferendoci all’immagine qui sopra 🔝.

  • Le X sono i vari input.
  • Le W sono i vari pesi.
  • La Thietaata (θ) è la fit function
  • E infine la Fi di Filippo (φ) è l’activation function

Dunque, facciamo ordine, e facciamolo in breve:

  1. Il modello ha bisogno di apprendere, tale per cui, vi sarà un insieme di input designati a far ciò
  2. Una volta appreso, il modello ha bisogno di un test set, ovvero dati utili a convalidare il modello stesso
  3. Infine, a modello convalidato, il modello utilizzerà propri ciò che ha imparato per poter ottenere un risultato voluto su dati non noti.

Il punto 1 e 2 hanno bisogno di dati supervisionati, ovvero input con output noto. Il punto 2 è necessario per convalidare quanto fatto nel punto 1 e per evitare l’*OVER*fitting (in breve).

Dio, perché ho iniziato a scrivere sto post?

Comunque sia, credo che, facendo un esempio pratico, le cose possano rendersi più semplici da capire (C̵̗͕̤͍̱̒͂̏́͒̈́̽͛̽͘Ŗ̵̲̺̥̯̰͎̥̏̊̒̒͠͠Ė̷̡̨̘̩̯̲͔̺͈̾̽̃͛̍̀̌͢D̛̳͚͓͖̻̍̀́̅͂̕͡I̸̗̪̪̺̦̠̝͇̙̮͋̽̐̅͂̊́͛̚͠C̵̛̭̞̗͙͉̀͂͂̋͋͑̂͜͝Î̶̡̫̠̬̟̩̩̾̂͋́̔͡͞)

Dunque, ecco il problema.
Abbiamo un piano, una retta e un insieme di punti.
Vogliamo addestrare il percettrone a classificare autonomomonanmente l’insieme dei punti in due classi: quelli dell’uppertown e i poveri (ovvero al di sopra e al di sotto della retta stessa).
A inizio articolo vi dicevo quanti limiti ha il percettrone: ecco, il problema citato è risolvibile solo perché stiamo dividendo il piano in due: se già introducessimo la classificazione di punti in maniera diversa, che ne so io, a ciambella, il percettrone fallirebbe epico.

Step 1: il percettrone

In tutto quello che ho scritto, ancora non sono entrato nel merito di cosa vuol dire per il percettrone imparare. Che scemonji.
Dunque, in tutto sto popo’ (quello di Dragonball) di roba, il percettrone altro non fa che tentare di “sistemare” i propri pesi (le w).

L’algoritmo del percettrone si divide in 4 semplici Phazi:

  1. Ricevere gli input
  2. Pesare gli input
  3. Sommare gli input pesati
  4. Computare l’output

Troppe cose Antonio, troppe cose.
Allora bambini speciali, passo per passo.

Ricevere gli input

Immaginiamo avere solo due input, ovvero le coordinate del punto nello spazio, (x,y), che so io (3, -7).
Punto 1 fine. 3 e -7 sono gli input. Easy Beasy

Pesare gli input

Opla’. Cosa vuol dire pesare gli input, semplice, associare ad ogni input il proprio peso, ovvero:
w_x * x e w_y * y.
Se mettiamo che i pesi siano 0.5 e -0.3, i due input pesati diventano: 3*0.5=1.5 e -7*-0.3=2.1

Sommare gli input pesati

Evvabbè se ve devo far anche il calcolo.

Computare l’output

E qui, ciao 👋🏻. Qui è dove si inizia un po’ a far della SCIEEEENZA [Non è vero]. Comunque sia, fatemi passare per vero che per calcolare l’output bisogna ricorrere alla activate function. Esistono effettivamente diverse tipologie di activate functions, nel nostro caso, fidatevi che la funzione segno basta.
f(x) = \begin{cases} +1 \text{ if } x \ge 0 \\-1 \text{ otherwise} \end{cases}

Diciamo quindi che il nostro Percettrone si può riassumere in questi passi. Almeno, nel computo della sua “predizione”. Ma ora? Come fa ad imparere?

Step 2: The Training

Shalalala. Come già detto, ora il Percettrone deve imparare dai suoi errori. Ancora una volta, ripeto, di nuovo, che siamo in un ambiente supervisionato e quindi, ora, it’s time to learn.
Durante la fase di training, daremo come input al percettrone punti di cui sappiamo già la classificazione, ovvero che sappiamo essere già o fra i ricchi o fra i poveri. Per comodità e ragion di logica, individueremo come poveri la classe -1 e i ricchi come classe +1, motivo per il quale, oltre a dar la colpa ai poveri per il fatto che ci siano i poveri, utilizziamo la funzione segno prima citata.
Un punto siffatto (cit.) è facilmente generabile. Prendiamo la nostra linea retta (Fatto?) che divide il piano, ad exampla, y=4x-2 e prendiamo ad esempio il punto (1, 3): per poterlo classificare basta effettuare la sostituzione della X di Xilofono nell’equazione della retta: y=4*1-2=2. Is this 2 maggiore minore o uguale alla iepsilon del punto, ovvero 3? Fuck yeah bitches, this fucking point is in the upperclasses, poiché stal al di sopra della retta. (3>2)

Questo preambolo per poter farvi capire che la generazione di punti di Training può avvenire “autononomonomante”, utilizzando quanto detto sopra.

Ma torniamo alla fase di apprendimento. In breve l’apprendimento implica”aggiustare i pesi”. Cosa vuol dire?
Mettiamo dunque ancora una volta che la retta è y=4x-2 e che ancora una volta il punto che vogliamo categorizzare è (1, 3).
I pesi attuali (vedi STEPPONE) sono 0.5 e -0.3. Ricordiamo inoltre che il punto in questione sappiamo essere della classe ricca (+1).

Procediamo attraverso il nostro algoritmo per ottenere i seguenti risultati

  1. Input: x=1 e y=3
  2. Input pesati: 1*0.5=0.5 e 3*-0.3=-9
  3. Sommiamo gli input: 0.5+(-9)=-8.5
  4. Applichiamo la funzione segno a -8.5 ottenendo: -1

Ah, povero beota di un Percettrone, ha classificato un punto che sapevamo essere ricco (+1), in un punto povero (-1). Dunque?
AGGIORNIAMO I PESI.

E qui, vi chiederete come?
Introduciamo prima il concetto di Errore (oltre al fatto che siete capitati su questa pagina).
Cosa è l’errore in questo caso? Definiamolo come il risultato atteso MENO il risultato calcolato, ovvero
Error = Risultato Atteso – Risultato Calcolato = Ricco – Povero = +1 – (-1) = +2

Notiamo, che se il Percettone avesse classificato correttamente il punto, l’errore sarebbe stato zero (0_0)! Notiamo inoltre, perché non ho voglia di far un altro esempio, che il caso opposto di classificazione, avrebbe portato a un errore di -2 (ovvero punto povero classificato come ricco).

Bene, ora che sappiamo cosa è l’errore possiamo calcolare l’aggiornamento dei pesi.
La seguente formula è ciò che ci serve:
\Delta \text{weight}_{i} = \text{Error} \cdot \text{input}_{i}

E di conseguenza, il nuovo peso diverrà:

w_{i} = w_{i} + \Delta w_{i} = w_{i} + \text{Error} \cdot \text{input}_{i}

Ora. Tentiamo di capire cosa sta succedendo, oltre alla solita tettonica a placche.
Come mai stiamo aggiornando i pesi in questa maniera?
Stiamo passeggiando e ci passa di fianco un bono paura: ecco, lo scopo è di voltarsi a guardarlo. Bene. Ora, mettiamo che stiamo andando troppo veloci, cioè tipo, stiamo correndo: sto fatto ci impedisce di avere il tempo di voltarci ad osservarlo per bene, saremmo solo in grado di vederne, che so, il pettorale. Quindi, soluzione? Ovviamente rallentare. Ed è proprio quel che stiamo facendo, stiamo aggiornando il nostro peso per avere la possibilità di curvare il nostro viso per poter effettuare uno Scan sul nostro obbiettivo (HP: 1500, MP: 399, Berserk). L’aggiornamento del peso, in questa maniera, serve proprio a questo: a trovare il giusto “ritmo” di passo per avere la miglior visuale di cotanta bellezza. Notiamo ovviamente che il Delta dell’aggiornamento potrebbe essere benissimo negativo (di cui il rallentare).

Siamo giunti al termine.

Evergreen

Guarda che faccia, guarda che faccia, non se lo aspettava.
Perché già, manca ancora qualcosa.

Che succede, che so io, se mettiamo in training il punto G di codinate (0, 0). Un cazzo, ecco che succede. Non puo’ essere né classificato, né usato come training, poiché se comunque moltiplicato per i pesi, darebbe il nulla, nada, void.
Ecco quindi che dobbiamo introdurre il terzo incomodo, il bias. Altro non è che questo, un valore aggiuntivo per poter “sistemare” il problema dello zero.

Cosa comporta nel modello?
Che ora sarà presente sempre un input in più rispetto al solo (x, y), ovvero il bias, fisso a 1, di conseguenza ogni punto sarà formato dalla tupla (x, y, 1). Va da sé che se prima vi erano solo due pesi, per la x e per la y, dobbiamo ora introdurre anche il peso per il bias stesso.

Ultimissima cosa, ma proprio ultima e poi vi lascio alla vostra vita di gattini su youtube (ha chiamato il 2010?).
Introduciamo anche un ulteriore fattore, chiamato learning rate. Esso assume un valore da (0, 1.0].
A cosa serve? Torniamo all’esempio di noi che corriamo e del bono fermo. Se per l’aggiornamento di peso, improvvisamente, cambiamo di segno, ovvero, mentre corriamo, non è che ci fermiamo, ma proprio ci giriamo di 180°: cioè il bono lo noterebbe troppo sta cosa e non va bene, noi siamo discreti. Ecco dunque che il learning rate serve a questo, a “velocizzare” o “diminuire” la velocità di apprendimento.

Ciò modifica la formula di aggiornamento pesi come segue:

w_{i} = w_{i} + \Delta w_{i} \cdot LR = w_{i} + \text{Error} \cdot \text{input}_{i} \cdot LR

Va da sé che valori alti di LR fanno apprendere velocemente, ma col rischio di aumentare l’errore (la figura di merda), LR invece troppo bassi rischiano di rendere inutile l’apprendimento stesso.

A livello teorico, io avrei finito. Ma ovviamente, tutta sta roba è accompagnato da un bel codice.

IL CODICE

Allora, premessa, sono Junior (di Namek) in Python, ma mi è stato comodo usarlo per far qualche esperimento. Iniziamo con un po’ di roba.
Di seguito la fantomatica activate function, ovvero, la semplice funzione segno

Activate Function
#A simple sign(x) function
def activateFunction(self, n):
	if n >= 0:
		return 1
	else:
		return -1

Andiamo avanti, e vediamo la fit function:

Fit Function
#Calcs sum of weightX*pointX+weightY*pointY+weightB*bias and return sign of sum
    def fit(self, point):
        sum_ = 0.0
        for i,(w,c) in enumerate(zip(self.weights, point)):
            sum_ += w*c
        return self.activateFunction(sum_)

Come si puo’ vedere, utilizzo il point passato (ovvero gli input), moltiplico ogni input per il peso associato, e ne faccio la somma. Infine ritorno appunto il valore dell’ activate function.
Infine vi è la funzione di train, ovvero, colei che aggiorna i pesi attraverso quanto detto precedentemente.

Train Function
    #Train itself, adjusting owns weights
    def train(self, inputs, desired):
        guess = self.fit(inputs)
        error = desired - guess
        for i, (w,inp) in enumerate(zip(self.weights,inputs)):
            self.weights[i] += self.lr * error * inp

In realtà il Percettrone è tutto qua.

Ho inoltre creato una classe Trainer, ovvero i punti. La classe utilizza nel costruttore anche il parametro f: essa è la funzione della retta che passo al punto per autonomononmomamente decidere a quale classe appartiene

Trainer.py
"""
Author: Antonio Cali
Project: perceptron
---
Trainer Class:
    Simple point class that have a X and Y Coordinate.
    We pass also the function of the straight line that divide the plane:
    usefull to automatically calculate the expected output of classification
----
"""
class Trainer:
    def __init__(self, x, y, function):
        self.inputs = [x,y,1]
        self.output = 1
        if (y<function(x)):
            self.output = -1

L’intero codice, compreso di main, è hostato sul mio github.

Di seguito, invece, grazie a repl, vi è il percettrone in azione.
Da notare come, in questo caso, il percettrone prende in input, uno alla volta, i punti di training, e dopo ogni “train” cerca di analizzare il problema per vedere quanti punti effettivamente classifica correttamente.
Nella funzione main è possibile modificare alcuni parametri, tra cui il numero di punti di training, il valore di learning rate, e la funzione della retta.

BUON DIO, Ho finito.

Share This: