Recherche…


Qu'est-ce qu'un perceptron exactement?

Visualisation d'un perceptron

À la base, un modèle de perceptron est l'un des algorithmes d' apprentissage supervisé les plus simples pour la classification binaire . C'est un type de classificateur linéaire , c'est-à-dire un algorithme de classification qui fait ses prédictions sur la base d'une fonction de prédicteur linéaire combinant un ensemble de poids avec le vecteur de caractéristiques. Une manière plus intuitive de penser est comme un réseau neuronal avec un seul neurone .

Le fonctionnement est très simple. Il obtient un vecteur de valeurs d'entrée x dont chaque élément est une caractéristique de notre ensemble de données.

Un exemple:

Dites que nous voulons classer si un objet est un vélo ou une voiture. Par souci de cet exemple, supposons que nous souhaitons sélectionner 2 fonctionnalités. La hauteur et la largeur de l'objet. Dans ce cas, x = [x1, x2]x1 est la hauteur et x2 la largeur.

Ensuite, une fois que nous avons notre vecteur d'entrée x, nous voulons multiplier chaque élément de ce vecteur par un poids. Plus la valeur du poids est élevée, plus la caractéristique est importante. Si, par exemple, nous utilisions la couleur comme caractéristique x3 et qu'il y a un vélo rouge et une voiture rouge, le perceptron lui attribue un poids très faible, de sorte que la couleur n'affecte pas la prévision finale.

Bon donc nous avons multiplié les 2 vecteurs x et w et nous avons récupéré un vecteur. Maintenant, nous devons additionner les éléments de ce vecteur. Une manière intelligente de le faire est de multiplier x par w en multipliant x par wTT signifie transposer. Nous pouvons imaginer la transposition d'un vecteur en tant que version pivotée du vecteur. Pour plus d'informations, vous pouvez lire la page Wikipedia . Essentiellement, en prenant la transposition du vecteur w, nous obtenons un vecteur Nx1 au lieu d'un vecteur 1xN . Ainsi, si nous multiplions maintenant notre vecteur d'entrée de taille 1xN avec ce vecteur de poids Nx1, nous obtiendrons un vecteur 1x1 (ou simplement une valeur unique) qui sera égal à x1 * w1 + x2 * w2 + ... + xn * wn . Cela fait, nous avons maintenant notre prédiction. Mais il y a une dernière chose. Cette prédiction ne sera probablement pas simple 1 ou -1 pour pouvoir classer un nouvel échantillon. Nous pouvons donc simplement dire ce qui suit: Si notre prédiction est supérieure à 0, nous disons que l'échantillon appartient à la classe 1, sinon si la prédiction est inférieure à zéro, nous disons que l'échantillon appartient à la classe -1. Cela s'appelle une fonction pas à pas .

Mais comment pouvons-nous avoir les bons poids pour que nous puissions corriger les prédictions? En d' autres termes, comment pouvons-nous entraînons notre modèle perceptron?

Dans le cas du perceptron, nous n'avons pas besoin d'équations mathématiques sophistiquées pour former notre modèle. Nos poids peuvent être ajustés par l'équation suivante:

Δw = eta * (y - prédiction) * x (i)

x (i) est notre fonctionnalité (x1 par exemple pour le poids 1, x2 pour w2 et ainsi de suite ...).

On a également remarqué qu'il y a une variable appelée eta qui est le taux d'apprentissage. Vous pouvez imaginer le taux d’apprentissage comme étant la taille que nous voulons que le changement de poids soit. Un bon taux d'apprentissage permet d'obtenir un algorithme d'apprentissage rapide. Une valeur trop élevée de eta peut entraîner une augmentation du nombre d'erreurs à chaque époque et aboutir à des prédictions vraiment mauvaises pour le modèle et ne jamais converger. Un taux d'apprentissage trop faible peut avoir pour conséquence que le modèle met trop de temps à converger. (Habituellement, une valeur intéressante pour définir eta pour le modèle perceptron est de 0,1 mais elle peut différer d'un cas à l'autre).

Enfin, certains d'entre vous ont peut-être remarqué que la première entrée est une constante (1) et qu'elle est multipliée par w0. Alors, c'est quoi exactement? Pour avoir une bonne prédiction, nous devons ajouter un biais. Et c'est exactement ce que cette constante est.

Pour modifier le poids du terme de biais, nous utilisons la même équation que pour les autres poids, mais dans ce cas, nous ne le multiplions pas par l’entrée (car l’entrée est une constante 1 et nous n’avons pas à le faire):

Δw = eta * (y - prédiction)

Voilà donc comment fonctionne un modèle simple de perceptron! Une fois que nous formons nos poids, nous pouvons lui donner de nouvelles données et avoir nos prévisions.

REMARQUE:

Le modèle Perceptron a un inconvénient important! Il ne convergera jamais (ei trouvera les poids parfaits) si les données ne sont pas linéairement séparables , ce qui signifie être capable de séparer les 2 classes d'un espace d'entités par une ligne droite. Donc, pour éviter cela, il est recommandé d'ajouter un nombre fixe d'itérations afin que le modèle ne soit pas bloqué par le réglage des poids qui ne sera jamais parfaitement ajusté.

Implémentation d'un modèle Perceptron en C ++

Dans cet exemple, je vais passer en revue l'implémentation du modèle perceptron en C ++ afin que vous puissiez avoir une meilleure idée de son fonctionnement.

Tout d'abord, il est recommandé de noter un algorithme simple de ce que nous voulons faire.

Algorithme:

  1. Faites un vecteur pour les poids et initialisez-le à 0 (n'oubliez pas d'ajouter le terme de biais)
  2. Continuez à ajuster les poids jusqu'à ce que nous obtenions 0 erreurs ou un faible nombre d'erreurs.
  3. Faites des prédictions sur des données inédites.

Après avoir écrit un algorithme très simple, écrivons maintenant certaines des fonctions dont nous aurons besoin.

  • Nous aurons besoin d'une fonction pour calculer l'entrée du réseau (ei x * wT multipliant le temps d'entrée des poids)
  • Une fonction pas à pas pour obtenir une prédiction de 1 ou -1
  • Et une fonction qui trouve les valeurs idéales pour les poids.

Donc, sans plus tarder, allons droit au but.

Commençons simple en créant une classe perceptron:

class perceptron
{
public:
    
private:

};

Ajoutons maintenant les fonctions dont nous aurons besoin.

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:

};

Remarquez comment la fonction fit prendre en argument un vecteur de vecteur <float>. En effet, notre ensemble de données de formation est une matrice d’intrants. Essentiellement, nous pouvons imaginer cette matrice comme un couple de vecteurs x empilés les uns sur les autres et chaque colonne de cette matrice étant une caractéristique.

Enfin ajoutons les valeurs que notre classe doit avoir. Comme le vecteur w pour contenir les poids, le nombre d' époques qui indique le nombre de passages que nous allons effectuer sur l'ensemble de données d'apprentissage. Et la constante eta qui est le taux d'apprentissage dont nous multiplierons chaque mise à jour afin de rendre la procédure plus rapide en composant cette valeur ou si eta est trop élevé, nous pouvons le composer pour obtenir le résultat idéal (pour la plupart des applications). du perceptron je suggérerais une valeur d' eta de 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;
};

Maintenant, avec notre ensemble de classes. Il est temps d'écrire chacune des fonctions.

Nous allons partir du constructeur ( 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
}

Comme vous pouvez voir ce que nous allons faire, ce sont des choses très simples. Passons donc à une autre fonction simple. La fonction de prédiction ( int prédire (vecteur X); ). Rappelez-vous que la fonction toutes les prédictions prend l'entrée réseau et renvoie une valeur de 1 si netInput est supérieur à 0 et -1 otherwhise.

int perceptron::predict(vector<float> X)
{
    return netInput(X) > 0 ? 1 : -1; //Step Function
}

Notez que nous avons utilisé une instruction en ligne pour rendre nos vies plus faciles. Voici comment fonctionne la déclaration en ligne:

condition? if_true: sinon

Jusqu'ici tout va bien. Passons à l'implémentation de la fonction netInput ( float netInput (vector X); )

Le netInput effectue les opérations suivantes: multiplie le vecteur d'entrée par la transposition du vecteur de poids

x * wT

En d'autres termes, il multiplie chaque élément du vecteur d'entrée x par l'élément correspondant du vecteur de poids w et prend ensuite leur somme et ajoute le biais.

(x1 * w1 + x2 * w2 + ... + xn * wn) + biais

biais = 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;
}

Bon, nous sommes maintenant à peu près terminés. La dernière chose à faire est d'écrire la fonction d' ajustement qui modifie les poids.

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
        }
    }
}

Donc c'était essentiellement ça. Avec seulement 3 fonctions, nous avons maintenant une classe de perceptron qui peut être utilisée pour faire des prédictions!

Si vous souhaitez copier-coller le code et l'essayer. Voici la classe entière (j'ai ajouté des fonctionnalités supplémentaires telles que l'impression du vecteur de poids et les erreurs de chaque époque, ainsi que l'option permettant d'importer / exporter des poids).

Voici le code:

L'en-tête de 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;
};

Le fichier de classe .cpp avec les fonctions:

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;
}

Aussi, si vous voulez essayer un exemple, voici un exemple que j'ai fait:

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;
}

La manière dont j'ai importé le jeu de données iris n'est pas vraiment idéale, mais je voulais juste quelque chose qui fonctionne.

Les fichiers de données peuvent être trouvés ici.

J'espère que vous avez trouvé cela utile!

Quel est le biais


Quel est le biais

Un perceptron peut être vu comme une fonction mappant un vecteur d'entrée (valeur réelle) x à une valeur de sortie f (x) (valeur binaire):

entrer la description de l'image ici

w est un vecteur de poids réels et b est notre valeur de biais . Le biais est une valeur qui décale la limite de décision de l'origine (0,0) et qui ne dépend d'aucune valeur d'entrée.

En considérant le biais de manière spatiale, le biais altère la position (mais pas l'orientation) de la limite de décision. Nous pouvons voir ci-dessous un exemple de la même courbe déplacée par le biais:

entrer la description de l'image ici



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow