Perceptron in ANSI C
One of my school assignments was to write a simple ANSI C Perceptron algorithm that would be able to separate points on a two dimensional plane into two sets (-1 and 1). Fortunately, while reading a Wikipedia article about Perceptron, I have found a great external link at the bottom of it: C# implementation of a Perceptron. This helped me a lot to understand how the Perceptron works and how to implement it programatically.
What I have done is rewrite the code snippet from John Wakefield in the C language. Instead of data type double I used two integer arrays (one denotes x and the other one y coordinates of points from a training set). Plus I have added a simple code do draw a nice graph of the training set and its linear separation. I used GD to draw the image. I would also like to thank Amro for helping me finish this algorithm.

Here’s the entire code:
-
#include <stdio.h>
-
#include <stdlib.h>
-
#include <gd.h>
-
#include <math.h>
-
-
#define NUMEL 208
-
#define LEARNING_RATE 0.1
-
#define MAX_ITERATION 100
-
-
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] + weights[2];
-
return (sum >= 0) ? 1 : -1;
-
}
-
-
int main(int argc, char *argv[])
-
{
-
srand(time(NULL));
-
-
float x[NUMEL], y[NUMEL], weights[3], localError, globalError, a, b;
-
int outputs[NUMEL], patternCount, i, p, iteration, output;
-
-
FILE *fp;
-
if ((fp = fopen("training-set.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;
-
}
-
printf("%.4f %.4f %d\n", x[i], y[i], outputs[i]);
-
i++;
-
}
-
patternCount = i;
-
-
system("PAUSE");
-
-
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;
-
-
// Update weights.
-
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);
-
-
// 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("%.4f %.4f %s\n", j, k, (output == 1) ? "Blue" : "Red");
-
}
-
}
-
-
// Display modified weights.
-
printf("Modified weights: %.2f %.2f\n", weights[0], weights[1]);
-
-
// Create image representation.
-
gdImagePtr im;
-
im = gdImageCreateTrueColor(600, 600);
-
if (im != 0)
-
{
-
// Allocate colors.
-
int white = gdImageColorAllocate(im, 255, 255, 255);
-
int lightGrey = gdImageColorAllocate(im, 220, 220, 220);
-
int black = gdImageColorAllocate(im, 0, 0, 0);
-
int blue = gdImageColorAllocate(im, 0, 0, 255);
-
int red = gdImageColorAllocate(im, 255, 0, 0);
-
int green = gdImageColorAllocate(im, 0, 200, 50);
-
-
// White flood fill.
-
gdImageFill(im, 0, 0, lightGrey);
-
-
// Points.
-
float cx, cy;
-
for (i = 0; i < patternCount; i++)
-
{
-
// Calculate output.
-
int output = calculateOutput(weights, x[i], y[i]);
-
-
cx = floor(300 + 30*x[i] + 0.5);
-
cy = floor(300 - 30*y[i] + 0.5);
-
-
int color = (output == 1) ? blue : red;
-
-
gdImageFilledEllipse(im, (int)cx, (int)cy, 5, 5, color);
-
}
-
-
// Linear separation
-
a = -weights[0] / weights[1];
-
b = -weights[2] / weights[1];
-
printf("Decision boundary (line) equation: y = %.4fx + %.4f\n", a, b);
-
// x = -10 => y = -10a+b
-
// x = 10 => y = 10*a + b
-
-
gdImageLine(im, 0, (int)(300 + 300*a - 30*b), 600, (300 - 300*a - 30*b), green);
-
-
// X coordinate.
-
gdImageLine(im, 0, 300, 600, 300, black);
-
-
// Y coordinate.
-
gdImageLine(im, 300, 0, 300, 600, black);
-
-
fp = fopen("training.png", "wb");
-
if (fp != 0)
-
{
-
gdImagePng(im, fp);
-
fclose(fp);
-
}
-
}
-
gdImageDestroy(im);
-
-
system("PAUSE");
-
return 0;
-
}
I used the Dev C++ to write the program. Here is how to use GD in Dev C++.
#include <stdlib.h>
#include <gd.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 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 patternCount = sizeof(x) / sizeof(int);
float weights[2];
weights[0] = randomFloat();
weights[1] = randomFloat();
float learningRate = 0.01;
int iteration = 0;
int i, p;
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 %5.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(“%5.2f %5.2f %s\n”, j, k, (output == 1) ? “Blue” : “Red”);
}
}
// Display modified weights.
printf(“Modified weights: %5.2f %5.2f\n”, weights[0], weights[1]);
// Create image representation.
gdImagePtr im;
FILE *fp;
im = gdImageCreateTrueColor(600, 600);
if (im != 0)
{
// So the points are further from each other
// and the graph is more readable.
int multiplier = 50;
// Allocate colors.
int white = gdImageColorAllocate(im, 255, 255, 255);
int lightGrey = gdImageColorAllocate(im, 220, 220, 220);
int black = gdImageColorAllocate(im, 0, 0, 0);
int blue = gdImageColorAllocate(im, 0, 0, 255);
int red = gdImageColorAllocate(im, 255, 0, 0);
int green = gdImageColorAllocate(im, 0, 200, 50);
// White flood fill.
gdImageFill(im, 0, 0, lightGrey);
// Points.
float cx, cy;
for (i = 0; i < patternCount; i++)
{
// Calculate output.
int output = calculateOutput(weights, x[i], y[i]);
cx = floor(300 + multiplier*x[i] + 0.5);
cy = floor(300 – multiplier*y[i] + 0.5);
int color = (output == 1) ? blue : red;
gdImageFilledEllipse(im, (int)cx, (int)cy, 10, 10, color);
}
// Linear separation.
float a = 0, b = 0;
for (i = 0; i < patternCount; i++)
{
int fx = (a > 0 && b > 0) ? 1 : 0;
a += learningRate * (y[i] – fx) * x[i];
b += learningRate * (y[i] – fx);
}
printf(“y = %5.2fx + %5.2f\n”, a, b);
// x = -300 => y = -300*a + b
// x = 300 => y = 300*a + b
gdImageLine(im, -2, (int)(300 + multiplier*300*a + b), 598, (int)(300 – multiplier*300*a + b), green);
gdImageLine(im, -1, (int)(300 + multiplier*300*a + b), 599, 300 – (int)(multiplier*300*a + b), green);
gdImageLine(im, 0, (int)(300 + multiplier*300*a + b), 600, 300 – (int)(multiplier*300*a + b), green);
gdImageLine(im, 1, (int)(300 + multiplier*300*a + b), 601, 300 – (int)(multiplier*300*a + b), green);
gdImageLine(im, 2, (int)(300 + multiplier*300*a + b), 602, 300 – (int)(multiplier*300*a + b), green);
// X coordinate.
gdImageLine(im, 0, 300, 600, 300, black);
// Y coordinate.
gdImageLine(im, 300, 0, 300, 600, black);
fp = fopen(“training.png”, “wb”);
if (fp != 0)
{
gdImagePng(im, fp);
fclose(fp);
}
}
gdImageDestroy(im);
system(“PAUSE”);
return 0;
}
