This commit is contained in:
Gustav Louw 2018-03-28 10:10:09 -07:00
parent ab10629287
commit 758a0bcbbd
3 changed files with 253 additions and 17 deletions

View File

@ -1,4 +1,4 @@
CC = g++
CC = gcc
NAME = shaper

195
main.c
View File

@ -1,19 +1,22 @@
// This program uses the Genann Neural Network Library to learn hand written digits.
// This program uses a modified version of the Genann Neural Network Library
// to learn hand written digits.
//
// Get it from the machine learning database:
// Get the training data from the machine learning database:
// wget http://archive.ics.uci.edu/ml/machine-learning-databases/semeion/semeion.data
#include "Genann.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define toss(t, n) ((t*) malloc((n) * sizeof(t)))
#define retoss(ptr, t, n) (ptr = (t*) realloc((ptr), (n) * sizeof(t)))
typedef double (*genann_actfun)(double);
typedef struct
{
double** id;
@ -25,6 +28,165 @@ typedef struct
}
Data;
typedef struct
{
int inputs;
int hidden_layers;
int hidden;
int outputs;
genann_actfun activation_hidden;
genann_actfun activation_output;
int total_weights;
int total_neurons;
double* weight;
double* output;
double* delta;
}
Genann;
static double genann_act_sigmoid(const double a)
{
return a < -45.0 ? 0 : a > 45.0 ? 1.0 : 1.0 / (1 + exp(-a));
}
static void genann_randomize(Genann* const ann)
{
for(int i = 0; i < ann->total_weights; i++)
{
double r = (double) rand() / RAND_MAX;
ann->weight[i] = r - 0.5;
}
}
// Clean this up. The mallocs do not look right.
static Genann *genann_init(const int inputs, const int hidden_layers, const int hidden, const int outputs)
{
const int hidden_weights = hidden_layers ? (inputs + 1) * hidden + (hidden_layers - 1) * (hidden + 1) * hidden : 0;
const int output_weights = (hidden_layers ? (hidden + 1) : (inputs + 1)) * outputs;
const int total_weights = hidden_weights + output_weights;
const int total_neurons = inputs + hidden * hidden_layers + outputs;
// Allocate extra size for weights, outputs, and deltas.
const int size = sizeof(Genann) + sizeof(double) * (total_weights + total_neurons + (total_neurons - inputs));
Genann* ret = (Genann*) malloc(size);
ret->inputs = inputs;
ret->hidden_layers = hidden_layers;
ret->hidden = hidden;
ret->outputs = outputs;
ret->total_weights = total_weights;
ret->total_neurons = total_neurons;
// Set pointers.
ret->weight = (double*) ((char*) ret + sizeof(Genann));
ret->output = ret->weight + ret->total_weights;
ret->delta = ret->output + ret->total_neurons;
ret->activation_hidden = genann_act_sigmoid;
ret->activation_output = genann_act_sigmoid;
genann_randomize(ret);
return ret;
}
static double const *genann_run(Genann const *ann, double const *inputs)
{
const double* w = ann->weight;
double* o = ann->output + ann->inputs;
const double* i = ann->output;
// Copy the inputs to the scratch area, where we also store each neuron's
// output, for consistency. This way the first layer isn't a special case.
memcpy(ann->output, inputs, sizeof(double) * ann->inputs);
const genann_actfun act = ann->activation_hidden;
const genann_actfun acto = ann->activation_output;
// Figure hidden layers, if any.
for(int h = 0; h < ann->hidden_layers; h++)
{
for(int j = 0; j < ann->hidden; j++)
{
double sum = *w++ * -1.0;
for(int k = 0; k < (h == 0 ? ann->inputs : ann->hidden); k++)
sum += *w++ * i[k];
*o++ = act(sum);
}
i += (h == 0 ? ann->inputs : ann->hidden);
}
const double* ret = o;
// Figure output layer.
for(int j = 0; j < ann->outputs; ++j)
{
double sum = *w++ * -1.0;
for(int k = 0; k < (ann->hidden_layers ? ann->hidden : ann->inputs); ++k)
sum += *w++ * i[k];
*o++ = acto(sum);
}
return ret;
}
static void genann_train(const Genann* ann, const double* inputs, const double* desired_outputs, const double rate)
{
// To begin with, we must run the network forward.
genann_run(ann, inputs);
// First set the output layer deltas.
{
// First output.
const double* o = ann->output + ann->inputs + ann->hidden * ann->hidden_layers;
// First delta.
double* d = ann->delta + ann->hidden * ann->hidden_layers;
// First desired output.
const double* t = desired_outputs;
// Set output layer deltas.
for(int j = 0; j < ann->outputs; j++, o++, t++)
*d++ = (*t - *o) * *o * (1.0 - *o);
}
// Set hidden layer deltas, start on last layer and work backwards.
// Note that loop is skipped in the case of hidden_layers == 0.
for(int h = ann->hidden_layers - 1; h >= 0; h--)
{
// Find first output and delta in this layer.
const double* o = ann->output + ann->inputs + (h * ann->hidden);
double* d = ann->delta + (h * ann->hidden);
// Find first delta in following layer (which may be hidden or output).
const double* const dd = ann->delta + ((h + 1) * ann->hidden);
// Find first weight in following layer (which may be hidden or output).
const double* const ww = ann->weight + ((ann->inputs + 1) * ann->hidden) + ((ann->hidden+1) * ann->hidden * (h));
for(int j = 0; j < ann->hidden; j++, d++, o++)
{
double delta = 0;
for(int k = 0; k < (h == ann->hidden_layers - 1 ? ann->outputs : ann->hidden); k++)
{
const double forward_delta = dd[k];
const int windex = k * (ann->hidden + 1) + (j + 1);
const double forward_weight = ww[windex];
delta += forward_delta * forward_weight;
}
*d = *o * (1.0 - *o) * delta;
}
}
// Train the outputs.
{
// Find first output delta. First output delta.
const double* d = ann->delta + ann->hidden * ann->hidden_layers;
// Find first weight to first output delta.
double* w = ann->weight + (ann->hidden_layers ? ((ann->inputs + 1) * ann->hidden + (ann->hidden + 1) * ann->hidden * (ann->hidden_layers - 1)) : 0);
// Find first output in previous layer.
const double* const i = ann->output + (ann->hidden_layers ? (ann->inputs + ann->hidden * (ann->hidden_layers - 1)) : 0);
// Set output layer weights.
for(int j = 0; j < ann->outputs; ++j, ++d)
for(int k = 0; k < (ann->hidden_layers ? ann->hidden : ann->inputs) + 1; k++)
*w++ += (k == 0) ? (*d * rate * -1.0) : (*d * rate * i[k - 1]);
}
// Train the hidden layers.
for(int h = ann->hidden_layers - 1; h >= 0; h--)
{
// Find first delta in this layer.
const double* d = ann->delta + (h * ann->hidden);
// Find first input to this layer.
double* const i = ann->output + (h ? (ann->inputs + ann->hidden * (h - 1)) : 0);
// Find first weight to this layer.
double* w = ann->weight + (h ? ((ann->inputs + 1) * ann->hidden + (ann->hidden + 1) * (ann->hidden) * (h - 1)) : 0);
for(int j = 0; j < ann->hidden; j++, d++)
for(int k = 0; k < (h == 0 ? ann->inputs : ann->hidden) + 1; k++)
*w++ += (k == 0) ? (*d * rate * -1.0) : (*d * rate * i[k - 1]);
}
}
static int lns(FILE* const file)
{
int ch = EOF;
@ -114,18 +276,18 @@ static void shuffle(const Data d, const int upper)
}
}
static void print(const double* const arr, const int size)
static void print(const double* const arr, const int size, const double thresh)
{
for(int i = 0; i < size; i++)
printf("%d ", arr[i] > 0.9);
printf("%d ", arr[i] > thresh);
}
static int cmp(const double* const a, const double* const b, const int size)
static int cmp(const double* const a, const double* const b, const int size, const double thresh)
{
for(int i = 0; i < size; i++)
{
const int aa = a[i] > 0.9;
const int bb = b[i] > 0.9;
const int aa = a[i] > thresh;
const int bb = b[i] > thresh;
if(aa != bb)
return 0;
}
@ -134,16 +296,17 @@ static int cmp(const double* const a, const double* const b, const int size)
static void predict(Genann* ann, const Data d)
{
const double thresh = 0.8;
int matches = 0;
for(int i = d.split; i < d.rows; i++)
{
// Prediciton.
const double* const pred = genann_run(ann, d.id[i]);
const double* const real = d.od[i];
print(pred, d.ocols);
print(pred, d.ocols, thresh);
printf(":: ");
print(real, d.ocols);
const int match = cmp(pred, real, d.ocols);
print(real, d.ocols, thresh);
const int match = cmp(pred, real, d.ocols, thresh);
printf("-> %d\n", match);
matches += match;
}
@ -189,7 +352,7 @@ int main(int argc, char* argv[])
shuffle(data, data.rows);
Genann* ann = train(data, 128, 1, data.icols / 2.0, 3.0); // Hyperparams.
predict(ann, data);
genann_free(ann);
free(ann);
dfree(data);
return 0;
}

73
test.c Normal file
View File

@ -0,0 +1,73 @@
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
// [i1] [h1] [o1]
//
// [i2] [h2] [o2]
//
// [b1] [b2]
static double act(const double in)
{
return 1.0 / (1.0 + exp(-in));
}
int main()
{
const double rate = 0.5;
// Input.
const double i1 = 0.05;
const double i2 = 0.10;
// Output.
const double t1 = 0.01;
const double t2 = 0.99;
// Weights and biases.
double w[] = { 0.15, 0.20, 0.25, 0.30, 0.40, 0.45, 0.50, 0.55 };
double b[] = { 0.35, 0.60 };
double et = 0;
for(int i = 0; i < 10000; i++)
{
// Compute.
const double h1 = act(w[0] * i1 + w[1] * i2 + b[0]);
const double h2 = act(w[2] * i1 + w[3] * i2 + b[0]);
const double o1 = act(w[4] * h1 + w[5] * h2 + b[1]);
const double o2 = act(w[6] * h1 + w[7] * h2 + b[1]);
// Error calculation.
const double to1 = t1 - o1;
const double to2 = t2 - o2;
const double e1 = 0.5 * to1 * to1;
const double e2 = 0.5 * to2 * to2;
et = e1 + e2;
const double a = -to1 * o1 * (1.0 - o1);
const double b = -to2 * o2 * (1.0 - o2);
const double c = (w[4] * a + w[6] * b) * (1.0 - h1);
const double d = (w[5] * a + w[7] * b) * (1.0 - h2);
// Back Propogation.
w[0] -= rate * h1 * c * i1;
w[1] -= rate * h1 * c * i2;
w[2] -= rate * h2 * d * i1;
w[3] -= rate * h2 * d * i2;
w[4] -= rate * h1 * a;
w[5] -= rate * h2 * a;
w[6] -= rate * h1 * b;
w[7] -= rate * h2 * b;
#if 0
printf("w1 %.9f\n", w[0]);
printf("w2 %.9f\n", w[1]);
printf("w3 %.9f\n", w[2]);
printf("w4 %.9f\n", w[3]);
printf("w5 %.9f\n", w[4]);
printf("w6 %.9f\n", w[5]);
printf("w7 %.9f\n", w[6]);
printf("w8 %.9f\n", w[7]);
#endif
}
printf("%0.12f\n", et);
}