machine-learning
Perzeptron
Suche…
Was ist genau ein Perzeptron?
Im Kern ist ein Perzeptron-Modell einer der einfachsten überwachten Lernalgorithmen für die binäre Klassifikation . Es ist eine Art linearer Klassifizierer , dh ein Klassifizierungsalgorithmus, der seine Vorhersagen auf der Grundlage einer linearen Prädiktorfunktion macht, die einen Satz von Gewichtungen mit dem Merkmalsvektor kombiniert. Eine intuitivere Art zu denken ist wie ein neuronales Netzwerk mit nur einem Neuron .
Die Funktionsweise ist sehr einfach. Es erhält einen Vektor von Eingabewerten x, von denen jedes Element ein Merkmal unseres Datensatzes ist.
Ein Beispiel:
Angenommen, wir möchten klassifizieren, ob ein Objekt ein Fahrrad oder ein Auto ist. Angenommen, wir möchten zwei Funktionen auswählen. Die Höhe und Breite des Objekts. In diesem Fall ist x = [x1, x2], wobei x1 die Höhe und x2 die Breite ist.
Sobald wir unseren Eingabevektor x haben, möchten wir jedes Element in diesem Vektor mit einer Gewichtung multiplizieren. Je höher der Wert des Gewichts, desto wichtiger ist das Merkmal. Wenn wir zum Beispiel Farbe als Merkmal x3 verwendet haben und es ein rotes Fahrrad und ein rotes Auto gibt, wird das Perzeptron ein sehr geringes Gewicht darauf legen, so dass die Farbe die endgültige Vorhersage nicht beeinflusst.
Okay, wir haben also die 2 Vektoren x und w multipliziert und einen Vektor zurückbekommen. Nun müssen wir die Elemente dieses Vektors summieren. Eine intelligente Art und Weise , dies zu tun ist , anstatt einfach Vervielfachungs x durch w wir x durch wT vermehren können , wobei T für transponieren steht. Wir können uns die Transponierte eines Vektors als gedrehte Version des Vektors vorstellen. Für weitere Informationen können Sie die Wikipedia-Seite lesen. Im Wesentlichen erhalten wir durch Transponieren des Vektors w einen Nx1- Vektor anstelle von 1xN . Wenn wir nun unseren Eingangsvektor mit der Größe 1xN mit diesem Nx1- Gewichtsvektor multiplizieren , erhalten wir einen 1x1- Vektor (oder einfach einen einzelnen Wert), der gleich x1 * w1 + x2 * w2 + ... + xn * wn ist . Nachdem wir das getan haben, haben wir jetzt unsere Vorhersage. Aber es gibt noch eine letzte Sache. Diese Vorhersage wird wahrscheinlich keine einfache 1 oder -1 sein, um eine neue Probe klassifizieren zu können. Wir können also einfach folgendes sagen: Wenn unsere Vorhersage größer als 0 ist, dann sagen wir, dass die Probe zur Klasse 1 gehört. Andernfalls, wenn die Vorhersage kleiner als Null ist, sagen wir, dass die Probe zur Klasse -1 gehört. Dies wird als Schrittfunktion bezeichnet .
Aber wie bekommen wir die richtigen Gewichte, damit wir richtige Vorhersagen treffen? In anderen Worten, wie trainieren wir unser Perzeptron-Modell?
Im Fall des Perzeptrons brauchen wir keine ausgefallenen mathematischen Gleichungen, um unser Modell zu trainieren . Unsere Gewichte können durch die folgende Gleichung eingestellt werden:
Δw = eta * (y - Vorhersage) * x (i)
Dabei ist x (i) unser Merkmal (x1 zum Beispiel für Gewicht 1, x2 für w2 usw.).
Es wurde auch bemerkt, dass es eine Variable namens eta gibt, die die Lernrate darstellt. Sie können sich die Lernrate vorstellen, wie groß die Änderung der Gewichte sein soll. Eine gute Lernrate führt zu einem schnellen Lernalgorithmus. Ein zu hoher Wert von eta kann zu einer zunehmenden Anzahl von Fehlern in jeder Epoche führen und dazu führen, dass das Modell wirklich schlechte Vorhersagen macht und niemals konvergiert. Eine zu niedrige Lernrate kann dazu führen, dass das Modell zu lange Konvergenz benötigt. (Normalerweise ist eta für das Perzeptron-Modell ein guter Wert von 0,1, er kann jedoch von Fall zu Fall unterschiedlich sein).
Schließlich haben einige von Ihnen vielleicht bemerkt, dass die erste Eingabe eine Konstante (1) ist und mit w0 multipliziert wird. Also was genau ist das? Um eine gute Vorhersage zu erhalten, müssen wir eine Verzerrung hinzufügen. Und genau das ist diese Konstante.
Um die Gewichtung des Vorspannungsausdrucks zu ändern, verwenden wir dieselbe Gleichung wie für die anderen Gewichte. In diesem Fall multiplizieren wir sie jedoch nicht mit der Eingabe (da die Eingabe eine Konstante 1 ist und wir daher nicht müssen):
Δw = eta * (y - Vorhersage)
So funktioniert ein einfaches Perzeptron-Modell! Sobald wir unsere Gewichte trainieren, können wir neue Daten angeben und unsere Vorhersagen treffen.
HINWEIS:
Das Perceptron-Modell hat einen wichtigen Nachteil! Es wird niemals konvergieren (dh die perfekten Gewichtungen finden), wenn die Daten nicht linear trennbar sind. Dies bedeutet, dass die 2 Klassen in einem Feature-Space durch eine gerade Linie getrennt werden können. Um zu vermeiden, dass es eine gute Praxis ist, eine feste Anzahl von Iterationen hinzuzufügen, damit das Modell nicht beim Anpassen von Gewichten hängen bleibt, die niemals perfekt abgestimmt werden.
Ein Perceptron-Modell in C ++ implementieren
In diesem Beispiel werde ich die Implementierung des Perzeptron-Modells in C ++ durchgehen, damit Sie eine bessere Vorstellung davon bekommen, wie es funktioniert.
Zuallererst ist es eine gute Praxis, einen einfachen Algorithmus über das, was wir tun wollen, aufzuschreiben.
Algorithmus:
- Machen Sie einen Vektor für die Gewichte und initialisieren Sie ihn auf 0 (nicht vergessen, den Bias-Term hinzuzufügen).
- Passen Sie die Gewichte weiter an, bis wir 0 Fehler oder eine niedrige Fehleranzahl erhalten.
- Machen Sie Vorhersagen über ungesehene Daten.
Nachdem wir einen sehr einfachen Algorithmus geschrieben haben, schreiben wir nun einige Funktionen, die wir benötigen.
- Wir benötigen eine Funktion zur Berechnung der Netzeingabe (ei x * wT multipliziert die Eingaben mit der Gewichtung)
- Eine Schrittfunktion, mit der wir eine Vorhersage von 1 oder -1 erhalten
- Und eine Funktion, die die idealen Werte für die Gewichte ermittelt.
Also lass uns ohne weiteres einsteigen.
Beginnen wir einfach mit der Erstellung einer Perzeptron-Klasse:
class perceptron
{
public:
private:
};
Nun fügen wir die Funktionen hinzu, die wir benötigen.
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:
};
Beachten Sie, dass die Funktion fit einen Vektor des Vektors <float> als Argument verwendet. Das liegt daran, dass unser Trainingsdatensatz eine Matrix von Eingaben ist. Im Wesentlichen können wir uns vorstellen, dass diese Matrix ein Paar von Vektoren x ist, die den einen übereinander stapeln und jede Spalte dieser Matrix ein Merkmal ist.
Schließlich fügen wir die Werte hinzu, die unsere Klasse haben muss. Wie der Vektor w für die Gewichte, die Anzahl der Epochen, die die Anzahl der Durchgänge angibt, die wir über den Trainingsdatensatz ausführen werden. Und die konstante Eta , dh die Lernrate, mit der wir jede Gewichtsaktualisierung multiplizieren, um den Trainingsvorgang durch Anwählen dieses Werts zu beschleunigen. Wenn Eta zu hoch ist, können wir sie heruntersetzen, um das ideale Ergebnis zu erzielen (für die meisten Anwendungen) des Perzeptrons würde ich einen Eta- Wert von 0,1 vorschlagen.
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;
};
Jetzt mit unserem Klassensatz. Es ist Zeit, jede der Funktionen zu schreiben.
Wir beginnen beim Konstruktor ( Perzeptron (Float eta, int. Epochen); )
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
}
Sie sehen, was wir tun werden, ist sehr einfaches Zeug. Gehen wir also zu einer anderen einfachen Funktion über. Die Vorhersagefunktion ( int pred (Vektor X); ). Denken Sie daran, dass die Funktion "alle Vorhersagen" die Netto-Eingabe übernimmt und einen Wert von 1 zurückgibt, wenn der NetInput größer als 0 ist und andernfalls -1.
int perceptron::predict(vector<float> X)
{
return netInput(X) > 0 ? 1 : -1; //Step Function
}
Beachten Sie, dass wir eine Inline-If-Anweisung verwendet haben, um unser Leben zu erleichtern. So funktioniert die Inline-Anweisung if:
Bedingung ? if_true: sonst
So weit, ist es gut. Fahren wir mit der Implementierung der netInput- Funktion fort ( float netInput (vector X); )
Der netInput führt Folgendes aus: multipliziert den Eingangsvektor mit der Transponierten des Gewichtungsvektors
x * wT
Mit anderen Worten, es multipliziert jedes Element des Eingangsvektors x mit dem entsprechenden Element des Vektors der Gewichte w und nimmt dann ihre Summe und addiert die Vorspannung.
(x1 * w1 + x2 * w2 + ... + xn * wn) + Vorspannung
Vorspannung = 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;
}
Okay, wir sind jetzt ziemlich fertig, was wir als letztes tun müssen, ist die Fit- Funktion zu schreiben, die die Gewichte modifiziert.
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
}
}
}
Das war es also im Wesentlichen. Mit nur drei Funktionen verfügen wir jetzt über eine funktionierende Perzeptron-Klasse, mit der wir Vorhersagen treffen können!
Wenn Sie den Code kopieren, einfügen und ausprobieren möchten. Hier ist die gesamte Klasse (ich habe einige zusätzliche Funktionen hinzugefügt, wie das Drucken des Gewichtungsvektors und der Fehler in jeder Epoche sowie die Option zum Importieren / Exportieren von Gewichtungen.)
Hier ist der Code:
Der Klassenheader:
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;
};
Die Klasse .cpp-Datei mit den Funktionen:
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;
}
Auch wenn Sie ein Beispiel ausprobieren möchten, ist ein Beispiel, das ich gemacht habe:
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;
}
Die Art und Weise, wie ich das Iris-Dataset importierte, ist nicht wirklich ideal, aber ich wollte nur etwas, das funktioniert.
Die Dateien finden Sie hier.
Ich hoffe, dass Sie das hilfreich fanden!
Was ist die Vorurteile?
Was ist die Vorurteile?
Ein Perzeptron kann als eine Funktion betrachtet werden, die einen Eingangsvektor (realwertig) x einem Ausgabewert f (x) (Binärwert) zuordnet:
Dabei ist w ein Vektor von reellen Gewichtungen und b ist unser Vorgabewert . Der Bias ist ein Wert, der die Entscheidungsgrenze vom Ursprung (0,0) wegschiebt und nicht von einem Eingabewert abhängt.
Wenn Sie an die Verzerrung in räumlicher Hinsicht denken, ändert sich die Position (aber nicht die Orientierung) der Entscheidungsgrenze. Nachfolgend sehen Sie ein Beispiel für dieselbe Kurve, die durch die Abweichung verschoben wurde: