2009-11-08 5 views
53

Вот моя реализация персептрона в ANSI C:Perceptron алгоритм обучения не сходящийся к 0

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

Обучения комплект Я использую: Data Set

Я удалил весь ненужный код. В основном, что он делает сейчас, он читает файл test1.txt и загружает значения из него в три массива: x, y, outputs.

Тогда есть perceptron learning algorithm, который по какой-то причине не сходится к 0 (globalError должен сходиться к 0), и поэтому я получаю бесконечный цикл while.

Когда я использую меньший набор тренировок (например, 5 баллов), он работает очень хорошо. Какие-нибудь идеи могут быть проблемой?

Я написал этот алгоритм очень похож на этот C# Perceptron algorithm:


EDIT:

Вот пример с меньшим набором обучения:

#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; 
} 
+1

небольшого предложение : Выйти после «Не удается открыть файл» или хотя бы инициализировать массивы чем-то в этом случае. – schnaader

+4

BTW, набор данных кажется действительным - загрузите визуальную визуализацию POV-Ray quick'n'dirty: http://img175.imageshack.us/img175/7135/pointtest.png – schnaader

+2

Зачем вы предполагаете ошибку, чтобы перейти к 0? В настоящее время globalError вычисляется как потеря журнала, которая должна быть минимизирована, но не равна нулю. Если ваши данные по дизайну разделимы, то потеря 0-1 может достигать 0 (хотя это опять-таки не точно из-за стохастичности градиентного спуска). –

ответ

144

В вашем текущем коде perceptron успешно изучает направление границы решения BUT не может перевести it.

 
    y        y 
    ^       ^
    | - + \\ +     | - \\ + + 
    | - +\\ + +    | - \\ + + + 
    | - - \\ +     | - - \\ + 
    | - - + \\ +    | - - \\ + + 
    ---------------------> x  --------------------> x 
     stuck like this   need to get like this 

(а кто-то указал, здесь more accurate version)

Проблема заключается в том, что ваш персептроне не имеет срока смещения, то есть третий компонент веса, подключенный к модулю ввод значения 1.

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

Ниже показано, как я исправил проблему:

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

... со следующим выходом:

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 

А вот короткая анимация выше с использованием MATLAB коды, показывающий decision boundary на каждую итерацию:

screenshot

+40

Это очень хороший ответ. ASCII графики ... видео ... человек. –

+1

+1 для иллюстраций и рисунка. – Isaac

+0

Как рисовать разделительную линию? Если 'y = ax + c' - это уравнение для разделительной линии. Как мне получить константы 'a' и' c' от веса научного персептрона? – Buksy

5

Это может помочь, если вы помещаете затравку случайного генератора в начало main, вместо того, чтобы пересаживаться при каждом вызове randomFloat, т.е.

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]; 
+0

Это очень хороший совет, хотя это не помогает (запуск здесь приводит к> 1 миллиону итераций без конца). Я думаю, что все еще есть какая-то проблема с алгоритмом здесь или с предположением, что он должен сходиться к 0. – schnaader

2

Некоторые небольшие ошибки, я заметил в исходном коде:

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

Лучше изменить это к

int patternCount = i; 

так что вы не должны полагаться на х массив иметь нужный размер.

Вы увеличиваете итерации внутри цикла p, тогда как исходный код C# делает это за пределами цикла p. Лучше двигаться Printf и итерация ++ вне р цикла перед оператором PAUSE - и я бы удалить заявление ПАУЗЫ или изменить его на

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

Даже делать все эти изменения, программа по-прежнему не прекращается с помощью вашего набор данных, но выход более согласован, давая ошибку колебания где-то между 56 и 60.

Последнее, что вы могли бы попробовать, это проверить исходную программу C# на этом наборе данных, если она также не прекращается, есть что-то не так с алгоритмом (потому что ваш набор данных выглядит правильно, см. мой комментарий к визуализации).

+0

Я добавил пример с небольшим учебным комплектом в конце моего сообщения. Вы можете попытаться скомпилировать это, чтобы увидеть, как он должен работать. Я понятия не имею, почему он терпит неудачу с большими наборами тренировок. –

0

globalError не станет равным нулю, будет сходятся к нулю, как вы сказали, то это будет очень мало.

Изменение цикла, как, например:

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

Дайте maxIterations и maxError значения, применимые к вашей проблеме.

+1

Спасибо за помощь, проблема в том, что набор для обучения является линейно разделяемым, и поэтому ошибка должна сходиться к 0 и потенциально становиться 0, а цикл while должен заканчиваться. Должна быть какая-то ошибка в моей реализации алгоритма Перцептрона. –

Смежные вопросы