/* Modified by Mohan from ckm1.c on 9/13/96, default options */ /****** * The program is used for training a neural network according to the lvq algorithm * usage: * a) counterprop * or * b) counterprop -l input_file data_file weight_file * or * c) counterprop -o weight_file data_file output_file * * a) -- interactive user querying * b) -- learning. input_file contains data on structure and training parameters. * data_file is training data. weight_file is output weights. * c) -- output generation. weight_file is generated by learning. data_file is * test data. output_file will contain test data with associated class. ******/ #include #include #define READFILE 0 #define WRITEFILE 1 #define TASK_NONE 0 #define TASK_LEARNING 1 #define TASK_OUTPUT 2 #define MAXRAND (1<<16) struct input_s { double *input; /* input vector */ double *output; /* either desired or actual output */ }; struct network { /* The structure */ int io_nodes[2]; /* #input & #output dimensions */ int num_hidden; /* # nodes in the hidden layer */ double **weights[2]; /* weights[layer][n_hidden][n_io] */ /* The training parameters */ int learning; /* if 1 - network training. Else: output gen. */ double eta1_start, eta1_end; double eta2_start, eta2_end; double converge1, converge2; int num_iter1, num_iter2; /* The inputs */ int num_inputs; /* #inputs */ struct input_s *inputs; /* Storage for input vectors */ }; double pow(); void initrandom(); double get_rand(); void counterprop(), openfile(), deallocate_network(); struct network *readinput(), *readfromkeyboard(), *readfromfile(), *getnetwork(); void read_data_inputs(); void initnetwork(), trainnetwork(), writeoutputweights(), writeoutputdata(), printvector(); void phase1(), phase2(); int closest(); void update(), getoutputs(); double get_euclidean(); int expon_eta =1; /* For learning: argv[1] - input structure file argv[2] - training data file argv[3] - output weights file For output generation: argv[1] - weights file argv[2] - test data file argv[3] - output data file */ void main(argc, argv) int argc; char *argv[]; { if( argc == 1 ) counterprop(TASK_NONE, NULL, NULL, NULL); else if( argv[1][0] == '-' && argv[1][1] == 'l') counterprop(TASK_LEARNING, argv[2], argv[3], argv[4]); else if( argv[1][0] == '-' && argv[1][1] == 'o') counterprop(TASK_OUTPUT, argv[2], argv[3], argv[4]); else { printf("Usage\n"); printf("For interactive mode: \n"); printf("\tcounterprop\n"); printf("For learning\n"); printf("\tcounterprop -l input_file data_file weights_file\n"); printf("For output generation\n"); printf("\tcounterprop -o weights_file data_file output_file\n"); } } /* Opens the file filename for reading or writing (depending on type). Terminates the program on error. */ void openfile( filename, fp, type) char *filename; FILE **fp; int type; { if( filename ) { if( type == READFILE ) *fp = fopen(filename, "rt"); else *fp = fopen(filename, "wt"); if( *fp == NULL ) { printf("Unable to open file %s for %s\n",filename, (type == READFILE) ? "reading" : "writing"); _exit(0); } } } /* Initialize random generator with number of microseconds since January 1, 1970, which is almost a random value in itself. */ void initrandom() { struct timeval tp; struct timezone tzp; if( gettimeofday(&tp,&tzp) == -1 ) { printf("Could not initialize random number generator\n"); _exit(0); } srandom((int)tp.tv_usec); } /* Returns a double precision random number between low and high */ double get_rand(low, high) double low, high; { double x = random() % MAXRAND / (double)MAXRAND; return( x*(high-low) + low ); } /* Initialization of the network. Assigns random weights to each node in the network. Weights in second layer are completely random. Weights in first layer are assigned so that all weight vectors belong to the hypercube described by the extreme input sample values. In other words, all weights are larger than the minimum and smaller than the maximum input, for each input dimension. */ void initnetwork(p) struct network *p; { int j, k; double min, max; /* For each input dimension, find the min. and max. value, and assign all weights from that input node between those two values. */ for( k=0; kio_nodes[0]; k++) { min = max = p->inputs[0].input[k]; for( j=0; jnum_inputs; j++) if( p->inputs[j].input[k] > max ) max = p->inputs[j].input[k]; else if( p->inputs[j].input[k] < min ) min = p->inputs[j].input[k]; /* Otherwise, the get_rand would divide by zero */ if( min == max ) max = min * 1.1; /* Assign random weights to connections from input to hidden layer */ for( j=0; jnum_hidden; j++) p->weights[0][j][k] = get_rand(min, max); } /* Weights from hidden to output layer are completely random */ for( j=0; jnum_hidden; j++) for( k=0; kio_nodes[1]; k++) p->weights[1][j][k] = get_rand(0.0, 1.0); } /* Finds the euclidean distance between 2 vectors of doubles (x & y) both of size n Because the exact distance is not so important, do not calculate the square root. When comparing two distances, it does not matter whether a square root of both is taken or not. */ double get_euclidean( x, y, n) double *x, *y; int n; { double dist = 0.0; int i; for( i=0; i