Functional-link Net Training Algorithm for Neural
Nets -- CPS615 -- Fall 1996 (Project)
Ulf Dittmer
Source Code
/*
program to implement a functional-link net
training algorithm for neural networks
written by Ulf Dittmer (ucdittme@top.cis.syr.edu)
*/
#define DUMP 0 /* print debugging info on screen */
#include
#include
#include
#include
#include "mpi.h"
#define NMXFUNCS 20 /* max no. of functions per network */
#define NMXINP 4096 /* max no. of input samples in each class */
#define NMXIATTR 4 /* max no. of input features */
#define RATIO 10 /* ration of update_weight to check_error calls */
/* timed for values of 1 and 10 */
#define ETA 0.2 /* learning rate */
#define ALPHA 0.9 /* momentum */
/* These values are approporiate for a wide range range of problems. */
float delta_w[NMXFUNCS]; /* previous weight changes */
float w_out[NMXFUNCS], samples[NMXINP][NMXIATTR], target[NMXINP];
float maxe; /* max allowed system error */
float maxep; /* max allowed pattern error */
char task_name[20]; /* name of input file */
int dummy, nr_input, ninattr, nr_iter = 0, nr_funcs;
long randseed = 568731L;
time_t start, stop;
float fv[NMXFUNCS][NMXINP]; /* precalculated function values */
double errors[2];
int id, procs; /* rank of this CPU / no. of CPUs */
int x1, x2; /* lower and upper boundary for this CPU */
MPI_Status status;
/*******************************************************************************/
int random()
{
randseed = 15625L * randseed + 22221L;
return((randseed >> 16) & 0x7FFF);
}
/******************************************************************************/
float f (int n, int sample)
/* calculate the values of the node functions:
n indicates which function of our pool
sample indicates for which input sample */
{
int i;
float result = 1.0;
if (n < nr_funcs - ninattr)
for (i=0; i < ninattr; i++, n>>=1)
if (n & 1)
result *= samples[sample][i];
else ;
else
result *= samples[sample][n-nr_funcs+ninattr] * samples[sample][n-nr_funcs+ninattr];
return result;
}
/*******************************************************************************/
void init_session()
/* prompts user for sample file, reads it in, initializes weights of
connections and precalculates and stores node functions */
{
int i, j;
char fnam[20];
FILE *fp;
if (id == 0) {
printf("\nData file name (.dat will be appended): ");
scanf("%s", task_name);
}
MPI_Bcast (&task_name[0], 20, MPI_UNSIGNED_CHAR, 0, MPI_COMM_WORLD, status);
strcpy(fnam, task_name);
strcat(fnam,".dat");
if((fp = fopen(fnam,"r"))==NULL) {
printf("\nFile %s does not exist",fnam);
exit(0);
}
fscanf(fp ,"%d", &ninattr);
fscanf(fp ,"%d", &nr_input);
for (i=0; i < nr_input; i++) {
for (j=0; j < ninattr; j++)
fscanf(fp ,"%f", &samples[i][j]);
fscanf(fp, "%f", &target[i]);
}
if((i = fclose(fp)) != 0) {
printf("\nFile cannot be closed %d",i);
exit(0);
}
if (id == 0) {
printf("\nMax total error (enter a fraction between 0 and 1)?: ");
scanf("%f", &maxe);
printf("\nMax individual error (enter a fraction between 0 and 1)?: ");
scanf("%f" ,&maxep);
}
MPI_Bcast (&maxe, 1, MPI_FLOAT, 0, MPI_COMM_WORLD, status);
MPI_Bcast (&maxep, 1, MPI_FLOAT, 0, MPI_COMM_WORLD, status);
nr_funcs = pow(2.0, (float) ninattr) + ninattr;
/* Determine the range of input examples that this processor must compute. */
x1 = id * nr_input / procs;
x2 = (id + 1) * nr_input / procs - 1;
if (DUMP) {
printf("\nProcessor %d out of %d from %d to %d\n", id, procs, x1, x2);
printf("%d %d %d\n", nr_input, ninattr, nr_funcs);
}
randseed = time(0);
MPI_Bcast (&randseed, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD, status);
/* Broadcasting the seed is sufficient to ensure that all CPUs generate the same
random numbers. So we don't have to broadcast the initial w_out[] array. */
for (j=0; j < nr_funcs; j++) {
delta_w[j] = 0.0;
w_out[j] = random()/pow(2.0,15.0)-0.5;
}
for (j=x1; j <= x2; j++)
for (i=0; i < nr_funcs; i++)
fv[i][j] = f(i,j);
}
/******************************************************************************/
void dwrite()
/* write information about training run and the resulting
weights of the connections to disk file */
{
int i, c;
char var_file_name[20];
FILE *fp;
strcpy(var_file_name, task_name);
switch (procs) {
case 1:
strcat(var_file_name, "_1_v.dat");
break;
case 2:
strcat(var_file_name, "_2_v.dat");
break;
case 4:
strcat(var_file_name, "_4_v.dat");
break;
case 8:
strcat(var_file_name, "_8_v.dat");
break;
default:
strcat(var_file_name, "_?_v.dat");
break;
}
if ((fp = fopen(var_file_name,"w+")) == NULL) {
perror("Cannot open data file ");
exit(0);
}
fprintf(fp, "number of inputs: %d\n", ninattr);
fprintf(fp, "number of samples: %d\n", nr_input);
fprintf(fp, "alpha: %2.2f\n", ALPHA);
fprintf(fp, "eta: %2.2f\n\n", ETA);
fprintf(fp, "iterations: %d\n", nr_iter);
fprintf(fp, "seconds: %f\n", difftime(stop, start));
fprintf(fp, "max system error: %f\n", maxe);
fprintf(fp, "max pattern error: %f\n", maxep);
fprintf(fp, "system error: %f\n\n", errors[0]);
for (i=0; i < nr_funcs; i++) fprintf(fp, "%d %f\n", i, w_out[i]);
if ((c = fclose(fp)) !=0) printf("\nFile cannot be closed %d ",c);
}
/******************************************************************************/
void update_weights()
{
int i, j;
double cum_dw[NMXFUNCS], cum_dw_comm[NMXFUNCS], net;
for (i=0; i < nr_funcs; i++) cum_dw[i] = 0.0;
for (j=x1; j <= x2; j++) {
net = 0.0;
for (i=0; i < nr_funcs; i++)
net += w_out[i] * fv[i][j];
for (i=0; i < nr_funcs; i++)
cum_dw[i] += (target[j] - net) * fv[i][j];
}
MPI_Allreduce(&cum_dw[0], &cum_dw_comm[0], NMXFUNCS, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
for(i=0; i < nr_funcs; i++) {
delta_w[i] = ETA * 2 * cum_dw_comm[i] / nr_input + ALPHA * delta_w[i];
w_out[i] += delta_w[i];
}
/* w_out[] are the weights, delta_w[] the weights changes.
We store delta_w because next cycle's momentum
calculation needs it. */
}
/******************************************************************************/
int check_error()
{
double net, errors_comm[2];
int i, j;
errors[0] = 0.0;
errors[1] = 0.0;
for (j=x1; j <= x2; j++) {
net = - target[j];
for (i=0; i < nr_funcs; i++) net += w_out[i] * fv[i][j];
net = fabs(net);
errors[0] += net * net;
if (net > maxep) errors[1] += 1.0;
}
/* errors[0] is the overall system error, errors[1] is the number
of misclassified samples. They are together in an array to
save one call to MPI_Allreduce. */
MPI_Allreduce(&errors[0], &errors_comm[0], 2, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD);
errors[0] = errors_comm[0];
if ((DUMP) & ((nr_iter % 100) == 0) & (id == 0))
printf("iter:%d miss:%5.0f total_error:%f\n", nr_iter, errors_comm[1], errors[0]);
return ((errors[0] < maxe) & (errors_comm[1] == 0)) ? 1 : 0;
}
/******************************************************************************/
void main(int argc, char *argv[])
{
int i, result = 0;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &id);
MPI_Comm_size(MPI_COMM_WORLD, &procs);
init_session();
start = time(NULL);
while (result == 0) {
for (i=1; i <= RATIO ; i++, nr_iter++)
update_weights();
/* RATIO = 1 means one check_error for each update_weights */
result = check_error();
}
stop = time(NULL);
if (id == 0) {
printf("%d iterations in %4.2f seconds\n", nr_iter, difftime(stop, start));
for (i=0; i < nr_funcs ; i++)
printf("func %d : %f\n", i, w_out[i]);
}
dwrite ();
MPI_Finalize();
}
Input Data
4
16
0.0 0.0 0.0 0.0 0.0
0.0 0.0 0.0 1.0 1.0
0.0 0.0 1.0 0.0 1.0
0.0 0.0 1.0 1.0 0.0
0.0 1.0 0.0 0.0 1.0
0.0 1.0 0.0 1.0 0.0
0.0 1.0 1.0 0.0 0.0
0.0 1.0 1.0 1.0 1.0
1.0 0.0 0.0 0.0 1.0
1.0 0.0 0.0 1.0 0.0
1.0 0.0 1.0 0.0 0.0
1.0 0.0 1.0 1.0 1.0
1.0 1.0 0.0 0.0 0.0
1.0 1.0 0.0 1.0 1.0
1.0 1.0 1.0 0.0 1.0
1.0 1.0 1.0 1.0 0.0
number of inputs: 4
number of samples: 16
alpha: 0.90
eta: 0.20
iterations: 220
seconds: 0.000000
max system error: 0.300000
max pattern error: 0.500000
system error: 0.267760
0 0.258881
1 0.455836
2 0.117780
3 -1.298514
4 0.412332
5 -1.298514
6 -1.298516
7 2.850136
8 0.151117
9 -1.298508
10 -1.298509
11 2.850129
12 -1.298511
13 2.850134
14 2.850140
15 -6.120528
16 0.117519
17 0.455579
18 0.161020
19 0.422235