machine-learning
Perceptron
Ricerca…
Cos'è esattamente un perceptron?
Al suo interno un modello perceptron è uno dei più semplici algoritmi di apprendimento supervisionato per la classificazione binaria . È un tipo di classificatore lineare , ovvero un algoritmo di classificazione che fa le sue previsioni basate su una funzione di predittore lineare che combina un insieme di pesi con il vettore di caratteristiche. Un modo più intuitivo di pensare è come una rete neurale con un solo neurone .
Il modo in cui funziona è molto semplice. Ottiene un vettore di valori di input x di cui ogni elemento è una caratteristica del nostro set di dati.
Un esempio:
Diciamo che vogliamo classificare se un oggetto è una bicicletta o una macchina. Per il gusto di questo esempio, diciamo che vogliamo selezionare 2 funzioni. L'altezza e la larghezza dell'oggetto. In questo caso x = [x1, x2] dove x1 è l'altezza e x2 è la larghezza.
Quindi, una volta ottenuto il vettore di input x , vogliamo moltiplicare ogni elemento di quel vettore con un peso. Di solito più alto è il valore del peso, più importante è la caratteristica. Se per esempio abbiamo usato il colore come caratteristica x3 e c'è una bicicletta rossa e una rossa, il percettore imposterà un peso molto basso in modo che il colore non influenzi la previsione finale.
Bene, abbiamo moltiplicato i 2 vettori x e w e abbiamo ottenuto un vettore. Ora abbiamo bisogno di sommare gli elementi di questo vettore. Un modo intelligente per farlo è invece di moltiplicare semplicemente x per w possiamo moltiplicare x per wT dove T sta per transpose. Possiamo immaginare la trasposizione di un vettore come una versione ruotata del vettore. Per maggiori informazioni puoi leggere la pagina di Wikipedia . Essenzialmente prendendo la trasposizione del vettore w otteniamo un vettore Nx1 invece di un 1xN . Quindi, se ora moltiplichiamo il nostro vettore di input con dimensione 1xN con questo vettore di peso Nx1 otterremo un vettore 1x1 (o semplicemente un singolo valore) che sarà uguale a x1 * w1 + x2 * w2 + ... + xn * wn . Fatto ciò, ora abbiamo la nostra previsione. Ma c'è un'ultima cosa. Questa previsione probabilmente non sarà una semplice 1 o -1 per essere in grado di classificare un nuovo campione. Quindi quello che possiamo fare è semplicemente dire quanto segue: Se la nostra previsione è maggiore di 0, allora diciamo che il campione appartiene alla classe 1, altrimenti se la previsione è minore di zero diciamo che il campione appartiene alla classe -1. Questa è chiamata una funzione di passaggio .
Ma come possiamo ottenere i pesi corretti in modo da correggere le previsioni? In altre parole, come possiamo addestrare il nostro modello perceptron?
Bene, nel caso del perceptron non abbiamo bisogno di fantasiose equazioni matematiche per addestrare il nostro modello. I nostri pesi possono essere regolati con la seguente equazione:
Δw = eta * (y - predizione) * x (i)
dove x (i) è la nostra caratteristica (x1 per esempio per peso 1, x2 per w2 e così via ...).
Inoltre ho notato che c'è una variabile chiamata eta che è il tasso di apprendimento. Potete immaginare il tasso di apprendimento quanto grande vogliamo il cambiamento dei pesi. Un buon tasso di apprendimento si traduce in un algoritmo di apprendimento veloce. Un valore troppo elevato di eta può comportare una quantità crescente di errori ad ogni epoca e comporta che il modello faccia delle previsioni davvero sbagliate e non converga mai. Troppo basso di un tasso di apprendimento può avere come risultato che il modello impiega troppo tempo per convergere. (Di solito un buon valore per impostare eta su per il modello perceptron è 0.1 ma può differire da caso a caso).
Infine alcuni di voi potrebbero aver notato che il primo input è una costante (1) e viene moltiplicato per w0. Quindi cos'è esattamente? Per ottenere una buona previsione, dobbiamo aggiungere un pregiudizio. E questo è esattamente ciò che è costante.
Per modificare il peso del termine di bias usiamo la stessa equazione che abbiamo fatto per gli altri pesi, ma in questo caso non lo moltiplichiamo per l'input (perché l'input è una costante 1 e quindi non dobbiamo):
Δw = eta * (y - previsione)
Quindi questo è fondamentalmente come funziona un semplice modello perceptron! Una volta addestrati i nostri pesi, possiamo dargli nuovi dati e avere le nostre previsioni.
NOTA:
Il modello Perceptron ha un importante svantaggio! Non convergerà mai (e troverà i pesi perfetti) se i dati non sono linearmente separabili , il che significa che è in grado di separare le 2 classi in uno spazio di caratteristiche da una linea retta. Quindi, per evitare che sia una buona pratica aggiungere un numero fisso di iterazioni in modo che il modello non sia bloccato a regolare i pesi che non saranno mai perfettamente sintonizzati.
Implementazione di un modello Perceptron in C ++
In questo esempio passerò attraverso l'implementazione del modello perceptron in C ++ in modo che tu possa avere un'idea migliore di come funziona.
Per prima cosa è una buona pratica scrivere un semplice algoritmo di ciò che vogliamo fare.
Algoritmo:
- Crea il vettore per i pesi e inizializza a 0 (Non dimenticare di aggiungere il termine di bias)
- Continua a regolare i pesi finché non riceviamo 0 errori o un conteggio di errori basso.
- Fai previsioni su dati invisibili.
Avendo scritto un algoritmo semplicissimo, scriviamo ora alcune delle funzioni di cui avremo bisogno.
- Avremo bisogno di una funzione per calcolare l'input della rete (ei * x wT moltiplicando gli input tempo i pesi)
- Una funzione di passaggio in modo da ottenere una previsione di 1 o -1
- E una funzione che trova i valori ideali per i pesi.
Quindi, senza ulteriori indugi, andiamo subito al suo interno.
Iniziamo semplice creando una classe perceptron:
class perceptron
{
public:
private:
};
Ora aggiungiamo le funzioni di cui avremo bisogno.
class perceptron
{
public:
perceptron(float eta,int epochs);
float netInput(vector<float> X);
int predict(vector<float> X);
void fit(vector< vector<float> > X, vector<float> y);
private:
};
Si noti come la funzione adatta prende come argomento un vettore di vettore <float>. Questo perché il nostro set di dati di formazione è una matrice di input. Essenzialmente possiamo immaginare che la matrice come una coppia di vettori x impilasse l'una sopra l'altra e ogni colonna di quella matrice fosse una caratteristica.
Infine aggiungiamo i valori che la nostra classe deve avere. Come il vettore w per contenere i pesi, il numero di epoche che indica il numero di passaggi che faremo sopra il set di dati di addestramento. E l' eta costante che è il tasso di apprendimento di cui moltiplicheremo ogni aggiornamento di peso per rendere più veloce la procedura di allenamento componendo questo valore o se eta è troppo alto possiamo comporlo per ottenere il risultato ideale (per la maggior parte delle applicazioni del perceptron suggerirei un valore eta di 0,1).
class perceptron
{
public:
perceptron(float eta,int epochs);
float netInput(vector<float> X);
int predict(vector<float> X);
void fit(vector< vector<float> > X, vector<float> y);
private:
float m_eta;
int m_epochs;
vector < float > m_w;
};
Ora con il nostro set di lezioni. È tempo di scrivere ognuna delle funzioni.
Inizieremo dal costruttore ( perceptron (float eta, int epochs); )
perceptron::perceptron(float eta, int epochs)
{
m_epochs = epochs; // We set the private variable m_epochs to the user selected value
m_eta = eta; // We do the same thing for eta
}
Come puoi vedere, quello che faremo è roba molto semplice. Quindi passiamo ad un'altra semplice funzione. La funzione di previsione ( int prevedere (vettore X); ). Ricordare che ciò che tutto predire funzione fa sta prendendo l'ingresso rete e restituisce un valore 1 se il netInput è maggiore di 0 e -1 otherwhise.
int perceptron::predict(vector<float> X)
{
return netInput(X) > 0 ? 1 : -1; //Step Function
}
Si noti che abbiamo usato un'istruzione inline se per semplificarci la vita. Ecco come funziona la dichiarazione inline if:
condizione? if_true: else
Fin qui tutto bene. Passiamo ad implementare la funzione netInput ( float netInput (vector X); )
Il netInput fa quanto segue; moltiplica il vettore di input per la trasposizione del vettore di pesi
x * wT
In altre parole, moltiplica ciascun elemento del vettore di input x dall'elemento corrispondente del vettore di pesi w, quindi prende la loro somma e aggiunge il bias.
(x1 * w1 + x2 * w2 + ... + xn * wn) + bias
bias = 1 * w0
float perceptron::netInput(vector<float> X)
{
// Sum(Vector of weights * Input vector) + bias
float probabilities = m_w[0]; // In this example I am adding the perceptron first
for (int i = 0; i < X.size(); i++)
{
probabilities += X[i] * m_w[i + 1]; // Notice that for the weights I am counting
// from the 2nd element since w0 is the bias and I already added it first.
}
return probabilities;
}
Bene, ora siamo praticamente all'ultima cosa che dobbiamo fare è scrivere la funzione di adattamento che modifica i pesi.
void perceptron::fit(vector< vector<float> > X, vector<float> y)
{
for (int i = 0; i < X[0].size() + 1; i++) // X[0].size() + 1 -> I am using +1 to add the bias term
{
m_w.push_back(0); // Setting each weight to 0 and making the size of the vector
// The same as the number of features (X[0].size()) + 1 for the bias term
}
for (int i = 0; i < m_epochs; i++) // Iterating through each epoch
{
for (int j = 0; j < X.size(); j++) // Iterating though each vector in our training Matrix
{
float update = m_eta * (y[j] - predict(X[j])); //we calculate the change for the weights
for (int w = 1; w < m_w.size(); w++){ m_w[w] += update * X[j][w - 1]; } // we update each weight by the update * the training sample
m_w[0] = update; // We update the Bias term and setting it equal to the update
}
}
}
Quindi era essenzialmente questo. Con solo 3 funzioni ora abbiamo una classe perceptron funzionante che possiamo usare per fare previsioni!
Nel caso in cui si desideri copiare e incollare il codice e provarlo. Ecco l'intera classe (ho aggiunto alcune funzionalità extra come la stampa del vettore di pesi e gli errori in ogni epoca e ho aggiunto l'opzione per importare / esportare pesi).
Ecco il codice:
L'intestazione della classe:
class perceptron
{
public:
perceptron(float eta,int epochs);
float netInput(vector<float> X);
int predict(vector<float> X);
void fit(vector< vector<float> > X, vector<float> y);
void printErrors();
void exportWeights(string filename);
void importWeights(string filename);
void printWeights();
private:
float m_eta;
int m_epochs;
vector < float > m_w;
vector < float > m_errors;
};
Il file class.cpp con le funzioni:
perceptron::perceptron(float eta, int epochs)
{
m_epochs = epochs;
m_eta = eta;
}
void perceptron::fit(vector< vector<float> > X, vector<float> y)
{
for (int i = 0; i < X[0].size() + 1; i++) // X[0].size() + 1 -> I am using +1 to add the bias term
{
m_w.push_back(0);
}
for (int i = 0; i < m_epochs; i++)
{
int errors = 0;
for (int j = 0; j < X.size(); j++)
{
float update = m_eta * (y[j] - predict(X[j]));
for (int w = 1; w < m_w.size(); w++){ m_w[w] += update * X[j][w - 1]; }
m_w[0] = update;
errors += update != 0 ? 1 : 0;
}
m_errors.push_back(errors);
}
}
float perceptron::netInput(vector<float> X)
{
// Sum(Vector of weights * Input vector) + bias
float probabilities = m_w[0];
for (int i = 0; i < X.size(); i++)
{
probabilities += X[i] * m_w[i + 1];
}
return probabilities;
}
int perceptron::predict(vector<float> X)
{
return netInput(X) > 0 ? 1 : -1; //Step Function
}
void perceptron::printErrors()
{
printVector(m_errors);
}
void perceptron::exportWeights(string filename)
{
ofstream outFile;
outFile.open(filename);
for (int i = 0; i < m_w.size(); i++)
{
outFile << m_w[i] << endl;
}
outFile.close();
}
void perceptron::importWeights(string filename)
{
ifstream inFile;
inFile.open(filename);
for (int i = 0; i < m_w.size(); i++)
{
inFile >> m_w[i];
}
}
void perceptron::printWeights()
{
cout << "weights: ";
for (int i = 0; i < m_w.size(); i++)
{
cout << m_w[i] << " ";
}
cout << endl;
}
Anche se vuoi provare un esempio qui è un esempio che ho fatto:
main.cpp:
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
#include <string>
#include <math.h>
#include "MachineLearning.h"
using namespace std;
using namespace MachineLearning;
vector< vector<float> > getIrisX();
vector<float> getIrisy();
int main()
{
vector< vector<float> > X = getIrisX();
vector<float> y = getIrisy();
vector<float> test1;
test1.push_back(5.0);
test1.push_back(3.3);
test1.push_back(1.4);
test1.push_back(0.2);
vector<float> test2;
test2.push_back(6.0);
test2.push_back(2.2);
test2.push_back(5.0);
test2.push_back(1.5);
//printVector(X);
//for (int i = 0; i < y.size(); i++){ cout << y[i] << " "; }cout << endl;
perceptron clf(0.1, 14);
clf.fit(X, y);
clf.printErrors();
cout << "Now Predicting: 5.0,3.3,1.4,0.2(CorrectClass=-1,Iris-setosa) -> " << clf.predict(test1) << endl;
cout << "Now Predicting: 6.0,2.2,5.0,1.5(CorrectClass=1,Iris-virginica) -> " << clf.predict(test2) << endl;
system("PAUSE");
return 0;
}
vector<float> getIrisy()
{
vector<float> y;
ifstream inFile;
inFile.open("y.data");
string sampleClass;
for (int i = 0; i < 100; i++)
{
inFile >> sampleClass;
if (sampleClass == "Iris-setosa")
{
y.push_back(-1);
}
else
{
y.push_back(1);
}
}
return y;
}
vector< vector<float> > getIrisX()
{
ifstream af;
ifstream bf;
ifstream cf;
ifstream df;
af.open("a.data");
bf.open("b.data");
cf.open("c.data");
df.open("d.data");
vector< vector<float> > X;
for (int i = 0; i < 100; i++)
{
char scrap;
int scrapN;
af >> scrapN;
bf >> scrapN;
cf >> scrapN;
df >> scrapN;
af >> scrap;
bf >> scrap;
cf >> scrap;
df >> scrap;
float a, b, c, d;
af >> a;
bf >> b;
cf >> c;
df >> d;
X.push_back(vector < float > {a, b, c, d});
}
af.close();
bf.close();
cf.close();
df.close();
return X;
}
Il modo in cui ho importato il set di dati dell'iride non è proprio l'ideale, ma volevo solo qualcosa che funzionasse.
I file di dati possono essere trovati qui.
Spero che tu l'abbia trovato utile!
Qual è il pregiudizio
Qual è il pregiudizio
Un perceptron può essere visto come una funzione che mappa un vettore di input (valore reale) x su un valore di uscita f (x) (valore binario):
dove w è un vettore di pesi a valori reali e b è un nostro valore di bias . Il bias è un valore che sposta il limite di decisione lontano dall'origine (0,0) e che non dipende da alcun valore di input.
Pensando al pregiudizio in modo spaziale, il pregiudizio altera la posizione (sebbene non l'orientamento) del confine della decisione. Possiamo vedere sotto un esempio della stessa curva spostata dal bias: