感知器学习algorithm不会收敛到0
这是我在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; }
我正在使用的训练集: 数据集
我已经删除了所有不相关的代码。 基本上它现在所做的就是读取test1.txt
文件,并将其值加载到三个数组: x
, y
, outputs
。
然后有一个感知器学习algorithm ,由于某种原因,不会收敛到0( globalError
应收敛到0),因此我得到一个无限的while while循环。
当我使用一个较小的训练集(如5分),它工作得很好。 任何想法可能是什么问题?
我写这个algorithm非常类似于这个C#Perceptronalgorithm :
编辑:
下面是一个较小的训练集的例子:
#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("XY 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; }
在你当前的代码中, 感知器成功地学习了决定边界的方向,但是不能翻译它。
YY ^ ^ | - + \\ + | - \\ +++ | - + \\ + + | - \\ + ++ | - - \\ + | - - \\ + | - - + \\ + | - - \\ +++ ---------------------> x --------------------> x 卡住这样的需要得到这样的
(正如有人指出,这是一个更准确的版本 )
问题在于你的感知器没有偏置项 ,也就是连接到值为1的input的第三个权重分量。
w0 ----- x ----> | | | f | ---->输出(+ 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的上面的代码的一个简短的animation,显示在每个迭代的决策边界 :
如果你把随机发生器的种子放在你主体的开始处,而不是每次调用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];
我在你的源代码中发现了一些小错误:
int patternCount = sizeof(x) / sizeof(int);
更好地改变这个
int patternCount = i;
所以你不必依靠你的x数组来获得合适的大小。
你增加了p循环内的迭代,而原始的C#代码在p循环之外。 在PAUSE语句之前,更好地将printf和iteration ++移到p循环之外 – 我也将删除PAUSE语句或将其更改为
if ((iteration % 25) == 0) system("PAUSE");
即使做了所有这些改变,你的程序仍然不会终止使用你的数据集,但是输出更加一致,给出了一个56到60之间的错误。
你可以尝试的最后一件事是在这个数据集上testing原始的C#程序,如果它还没有终止的话,algorithm出错了(因为你的数据集看起来是正确的,请参阅我的可视化注释)。
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
值适用于您的问题。