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