web-dev-qa-db-fra.com

L'algorithme d'apprentissage Perceptron ne converge pas vers 0

Voici mon implémentation perceptron en ANSI C:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

float randomFloat()
{
    srand(time(NULL));
    float r = (float)Rand() / (float)Rand_MAX;
    return r;
}

int calculateOutput(float weights[], float x, float y)
{
    float sum = x * weights[0] + y * weights[1];
    return (sum >= 0) ? 1 : -1;
}

int main(int argc, char *argv[])
{
    // X, Y coordinates of the training set.
    float x[208], y[208];

    // Training set outputs.
    int outputs[208];

    int i = 0; // iterator

    FILE *fp;

    if ((fp = fopen("test1.txt", "r")) == NULL)
    {
        printf("Cannot open file.\n");
    }
    else
    {
        while (fscanf(fp, "%f %f %d", &x[i], &y[i], &outputs[i]) != EOF)
        {
            if (outputs[i] == 0)
            {
                outputs[i] = -1;
            }
            printf("%f   %f   %d\n", x[i], y[i], outputs[i]);
            i++;
        }
    }

    system("PAUSE");

    int patternCount = sizeof(x) / sizeof(int);

    float weights[2];
    weights[0] = randomFloat();
    weights[1] = randomFloat();

    float learningRate = 0.1;

    int iteration = 0;
    float globalError;

    do {
        globalError = 0;
        int p = 0; // iterator
        for (p = 0; p < patternCount; p++)
        {
            // Calculate output.
            int output = calculateOutput(weights, x[p], y[p]);

            // Calculate error.
            float localError = outputs[p] - output;

            if (localError != 0)
            {
                // Update weights.
                for (i = 0; i < 2; i++)
                {
                    float add = learningRate * localError;
                    if (i == 0)
                    {
                        add *= x[p];
                    }
                    else if (i == 1)
                    {
                        add *= y[p];
                    }
                    weights[i] +=  add;
                }
            }

            // Convert error to absolute value.
            globalError += fabs(localError);

            printf("Iteration %d Error %.2f %.2f\n", iteration, globalError, localError);

            iteration++;
        }

        system("PAUSE");

    } while (globalError != 0);

    system("PAUSE");
    return 0;
}

L'ensemble de formation que j'utilise: Data Set

J'ai supprimé tout le code non pertinent. Fondamentalement, ce qu'il fait maintenant, il lit test1.txt fichier et en charge les valeurs dans trois tableaux: x, y, outputs.

Ensuite, il y a un algorithme d'apprentissage perceptron qui, pour une raison quelconque, ne converge pas vers 0 (globalError devrait converger vers 0) et donc j'obtiens une boucle do infinie.

Lorsque j'utilise un ensemble d'entraînement plus petit (comme 5 points), cela fonctionne plutôt bien. Des idées où pourrait être le problème?

J'ai écrit cet algorithme très similaire à cela algorithme C # Perceptron :


MODIFIER:

Voici un exemple avec un ensemble d'entraînement plus petit:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

float randomFloat()
{
    float r = (float)Rand() / (float)Rand_MAX;
    return r;
}

int calculateOutput(float weights[], float x, float y)
{
    float sum = x * weights[0] + y * weights[1];
    return (sum >= 0) ? 1 : -1;
}

int main(int argc, char *argv[])
{
    srand(time(NULL));

    // X coordinates of the training set.
    float x[] = { -3.2, 1.1, 2.7, -1 };

    // Y coordinates of the training set.
    float y[] = { 1.5, 3.3, 5.12, 2.1 };

    // The training set outputs.
    int outputs[] = { 1, -1, -1, 1 };

    int i = 0; // iterator

    FILE *fp;

    system("PAUSE");

    int patternCount = sizeof(x) / sizeof(int);

    float weights[2];
    weights[0] = randomFloat();
    weights[1] = randomFloat();

    float learningRate = 0.1;

    int iteration = 0;
    float globalError;

    do {
        globalError = 0;
        int p = 0; // iterator
        for (p = 0; p < patternCount; p++)
        {
            // Calculate output.
            int output = calculateOutput(weights, x[p], y[p]);

            // Calculate error.
            float localError = outputs[p] - output;

            if (localError != 0)
            {
                // Update weights.
                for (i = 0; i < 2; i++)
                {
                    float add = learningRate * localError;
                    if (i == 0)
                    {
                        add *= x[p];
                    }
                    else if (i == 1)
                    {
                        add *= y[p];
                    }
                    weights[i] +=  add;
                }
            }

            // Convert error to absolute value.
            globalError += fabs(localError);

            printf("Iteration %d Error %.2f\n", iteration, globalError);          
        }

        iteration++;

    } while (globalError != 0);

    // Display network generalisation.
    printf("X       Y     Output\n");
    float j, k;
    for (j = -1; j <= 1; j += .5)
    {
        for (j = -1; j <= 1; j += .5)
        {
            // Calculate output.
            int output = calculateOutput(weights, j, k);
            printf("%.2f  %.2f  %s\n", j, k, (output == 1) ? "Blue" : "Red");
        }
    }

    // Display modified weights.
    printf("Modified weights: %.2f %.2f\n", weights[0], weights[1]);

    system("PAUSE");
    return 0;
}
59
Richard Knop

Dans votre code actuel, le perceptron apprend avec succès la direction de la frontière de décision MAIS est incapable de traduire.

 y y 
 ^ ^ 
 | - + \\ + | - \\ + + 
 | - + \\ + + | - \\ + + + 
 | - - \\ + | - - \\ + 
 | - - + \\ + | - - \\ + + 
 ---------------------> x ---------------- ----> x 
 coincé comme ça doit devenir comme ça 

(comme quelqu'un l'a souligné, voici une version plus précise )

Le problème réside dans le fait que votre perceptron n'a pas de terme de biais , c'est-à-dire une troisième composante de poids connectée à une entrée de valeur 1.

 w0 ----- 
 x ----> | | 
 | f | ----> sortie (+ 1/-1) 
 y ----> | | 
 w1 ----- 
 ^ w2 
 1(bias)  

Voici comment j'ai corrigé le problème:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#define LEARNING_RATE    0.1
#define MAX_ITERATION    100

float randomFloat()
{
    return (float)Rand() / (float)Rand_MAX;
}

int calculateOutput(float weights[], float x, float y)
{
    float sum = x * weights[0] + y * weights[1] + weights[2];
    return (sum >= 0) ? 1 : -1;
}

int main(int argc, char *argv[])
{
    srand(time(NULL));

    float x[208], y[208], weights[3], localError, globalError;
    int outputs[208], patternCount, i, p, iteration, output;

    FILE *fp;
    if ((fp = fopen("test1.txt", "r")) == NULL) {
        printf("Cannot open file.\n");
        exit(1);
    }

    i = 0;
    while (fscanf(fp, "%f %f %d", &x[i], &y[i], &outputs[i]) != EOF) {
        if (outputs[i] == 0) {
            outputs[i] = -1;
        }
        i++;
    }
    patternCount = i;

    weights[0] = randomFloat();
    weights[1] = randomFloat();
    weights[2] = randomFloat();

    iteration = 0;
    do {
        iteration++;
        globalError = 0;
        for (p = 0; p < patternCount; p++) {
            output = calculateOutput(weights, x[p], y[p]);

            localError = outputs[p] - output;
            weights[0] += LEARNING_RATE * localError * x[p];
            weights[1] += LEARNING_RATE * localError * y[p];
            weights[2] += LEARNING_RATE * localError;

            globalError += (localError*localError);
        }

        /* Root Mean Squared Error */
        printf("Iteration %d : RMSE = %.4f\n",
            iteration, sqrt(globalError/patternCount));
    } while (globalError > 0 && iteration <= MAX_ITERATION);

    printf("\nDecision boundary (line) equation: %.2f*x + %.2f*y + %.2f = 0\n",
        weights[0], weights[1], weights[2]);

    return 0;
}

... avec la sortie suivante:

Iteration 1 : RMSE = 0.7206
Iteration 2 : RMSE = 0.5189
Iteration 3 : RMSE = 0.4804
Iteration 4 : RMSE = 0.4804
Iteration 5 : RMSE = 0.3101
Iteration 6 : RMSE = 0.4160
Iteration 7 : RMSE = 0.4599
Iteration 8 : RMSE = 0.3922
Iteration 9 : RMSE = 0.0000

Decision boundary (line) equation: -2.37*x + -2.51*y + -7.55 = 0

Et voici une courte animation du code ci-dessus en utilisant MATLAB, montrant le --- (limite de décision à chaque itération:

screenshot

158
Amro

Cela pourrait aider si vous placez l'amorçage du générateur aléatoire au début de votre main au lieu de réamorcer à chaque appel à randomFloat, c'est-à-dire.

float randomFloat()
{
    float r = (float)Rand() / (float)Rand_MAX;
    return r;
}

// ...

int main(int argc, char *argv[])
{
    srand(time(NULL));

    // X, Y coordinates of the training set.
    float x[208], y[208];
6
rsp

Quelques petites erreurs que j'ai repérées dans votre code source:

int patternCount = sizeof(x) / sizeof(int);

Mieux vaut changer cela en

int patternCount = i;

vous n'avez donc pas besoin de compter sur votre tableau x pour avoir la bonne taille.

Vous augmentez les itérations à l'intérieur de la boucle p, tandis que le code C # d'origine le fait à l'extérieur de la boucle p. Mieux vaut déplacer le printf et l'itération ++ en dehors de la boucle p avant l'instruction PAUSE - aussi je supprimerais l'instruction PAUSE ou la changerais en

if ((iteration % 25) == 0) system("PAUSE");

Même en faisant tous ces changements, votre programme ne se termine toujours pas en utilisant votre ensemble de données, mais la sortie est plus cohérente, donnant une erreur oscillant entre 56 et 60.

La dernière chose que vous pourriez essayer est de tester le programme C # d'origine sur cet ensemble de données, s'il ne se termine pas également, il y a un problème avec l'algorithme (car votre ensemble de données semble correct, voir mon commentaire de visualisation).

3
schnaader

globalError ne deviendra pas zéro, il convergera vers zéro comme vous l'avez dit, c'est-à-dire qu'il deviendra très petit.

Changez votre boucle comme ceci:

int maxIterations = 1000000; //stop after one million iterations regardless
float maxError = 0.001; //one in thousand points in wrong class

do {
    //loop stuff here

    //convert to fractional error
    globalError = globalError/((float)patternCount);

} while ((globalError > maxError) && (i<maxIterations));

Donnez les valeurs maxIterations et maxError applicables à votre problème.

1
jilles de wit