mirror of
https://github.com/glouw/tinn
synced 2024-11-24 23:39:38 +03:00
testing
This commit is contained in:
parent
ab10629287
commit
758a0bcbbd
195
main.c
195
main.c
@ -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
73
test.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user