感知器学习算法不收敛到 0
Posted
技术标签:
【中文标题】感知器学习算法不收敛到 0【英文标题】:Perceptron learning algorithm not converging to 0 【发布时间】:2010-12-14 09:52:27 【问题描述】:这是我在 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;
我正在使用的训练集:Data Set
我已经删除了所有不相关的代码。基本上它现在所做的是读取test1.txt
文件并将值从其中加载到三个数组:x
、y
、outputs
。
然后有一个 perceptron learning algorithm,由于某种原因,它没有收敛到 0(globalError
应该收敛到 0),因此我得到了一个无限的 do while 循环。
当我使用较小的训练集(例如 5 分)时,效果非常好。有什么想法可能是问题所在?
我写的这个算法和C# Perceptron algorithm很相似:
编辑:
这是一个训练集较小的示例:
#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;
【问题讨论】:
小建议:在“无法打开文件”后退出,或者在这种情况下至少用一些东西初始化数组。 顺便说一句,数据集似乎有效 - 上传了一个快速的'n'dirty POV-Ray 可视化:img175.imageshack.us/img175/7135/pointtest.png 为什么你会假设错误为 0?现在 globalError 被计算为日志损失,应该最小化但不是零。如果您的数据在设计上是可分离的,那么 0-1 损失可能会达到 0(尽管这再次不确定,因为梯度下降的随机性)。 @Jonathan:我的数学不是很好,但如果两组点是线性可分的,它应该收敛到 0。我还查看了一篇关于感知器的***文章,我的算法似乎是正确的。我在下面添加了一个带有小型训练集的示例,您可以检查它应该如何工作。 C/C++ 感知器:sourceforge.net/projects/ccperceptron 【参考方案1】:在您当前的代码中,perceptron 成功地学习了决策边界的方向,但无法翻译它。
是的 ^ ^ | - + \\ + | - \\ + + | - +\\ + + | - \\ + + + | - - \\ + | - - \\ + | - - + \\ + | - - \\ + + ---------------------> x --------------------> x 像这样卡住需要像这样(正如有人指出的,这里是more accurate version)
问题在于您的感知器没有偏置项,即第三个权重分量连接到值 1 的输入。
w0 ----- x ---->| | | f |----> 输出 (+1/-1) 是---->| | w1 ----- ^ w2 1(偏差)---|以下是我纠正问题的方法:
#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:
【讨论】:
我应该如何绘制分隔线?如果y = ax + c
是分隔线的方程。如何从学习感知器的权重中获取 a
和 c
常量?
@Buksy:线的方程是:w0*x + w1*y + w2 = 0
其中w_i
是学习的权重(连接到 x/y 输入的权重分量 + 偏差;请参阅开头的图表的帖子)。显然,您可以将术语重新排序为 y=ax+b
去掉if (outputs[i] == 0) outputs[i] = -1;
语句为什么不收敛?
@MathuSumMut 使用的激活函数calculateOutput
返回-1 或+1,这是我从原始代码中保留的。原始dataset file 中的类目标编码为 0/1,因此需要将 0 替换为 -1。【参考方案2】:
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
值。
【讨论】:
感谢帮助,问题是训练集是线性可分的,因此,错误应该收敛到 0 并可能变为 0,并且 do while 循环应该结束。我在 Perceptron 算法的实现中一定有一些错误。【参考方案3】:我在您的源代码中发现了一些小错误:
int patternCount = sizeof(x) / sizeof(int);
最好改成
int patternCount = i;
因此您不必依赖 x 数组来获得正确的大小。
您在 p 循环内增加迭代,而原始 C# 代码在 p 循环外执行此操作。最好在 PAUSE 语句之前将 printf 和迭代++移到 p 循环之外 - 我也会删除 PAUSE 语句或将其更改为
if ((iteration % 25) == 0) system("PAUSE");
即使进行了所有这些更改,您的程序仍然不会使用您的数据集终止,但输出更加一致,给出了一个在 56 到 60 之间波动的错误。
您可以尝试的最后一件事是在此数据集上测试原始 C# 程序,如果它也没有终止,则算法有问题(因为您的数据集看起来正确,请参阅我的可视化评论)。
【讨论】:
我在帖子末尾添加了一个训练集较小的示例。您可以尝试编译它以查看它应该如何工作。我不知道为什么它在更大的训练集上会失败。【参考方案4】:如果您将随机生成器的种子放在您的 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];
【讨论】:
这是一个非常好的建议,尽管它没有帮助(在此处运行它会导致 >= 100 万次迭代而看不到尽头)。我认为这里的算法或假设它应该收敛到 0 仍然存在一些问题。以上是关于感知器学习算法不收敛到 0的主要内容,如果未能解决你的问题,请参考以下文章