/* * Rick van der Zwet * 0433373 * OS Assigment 3 * Licence: BSD * $Id: nn.c 557 2008-04-08 22:57:09Z rick $ */ #include #include #include #include #include /* NOTE: All first knobs are bias knobs or hidden stale knobs * - Validation is done using rounding, please make outputs discrete or * alter validation function */ /* Allow uniform and easy calls at functions */ #define TRUE 1 #define FALSE 0 /* Network variables */ /*NOTE: first node is 'hidden' bias knob */ #ifndef INPUT_SIZE #define INPUT_SIZE 11 #endif /*NOTE: first node is 'hidden' bias knob */ #ifndef HIDDEN_SIZE #define HIDDEN_SIZE 11 #endif /*NOTE: first node is 'hidden' 'lame' knob */ #ifndef OUTPUT_SIZE #define OUTPUT_SIZE 11 #endif /* Learn speed alpha of network */ #ifndef LEARN_SPEED #define LEARN_SPEED 0.5 #endif /* After QUALITY_ROUND trainingset check quality of network */ #define QUALITY_ROUND 100 /* Training set, to be used to train network */ char * file_training = "data/training.txt"; /* Validation set, to be used to test end result of network */ char * file_validate = "data/validate.txt"; /* Quality set, to be used to do quick testing whether network is * improving */ char * file_quality = "data/quality.txt"; /* Globally defined arrays, which represent the network */ double hidden[HIDDEN_SIZE]; double input[INPUT_SIZE]; double output[OUTPUT_SIZE]; double target[OUTPUT_SIZE]; double weight_HtoO[HIDDEN_SIZE][OUTPUT_SIZE]; double weight_ItoH[INPUT_SIZE][HIDDEN_SIZE]; #define WEIGHT_NOT_USED -99999 void stdInit() { int i; /* Should never change, been using */ for (i = 0; i < INPUT_SIZE; i++) weight_ItoH[i][0] = WEIGHT_NOT_USED; for (i = 0; i < HIDDEN_SIZE; i++) weight_HtoO[i][0] = WEIGHT_NOT_USED; } /* Random init of weights */ void randInit() { int i,j; /* Different numbers every call */ srandom(time(NULL)); for (i = 0; i < INPUT_SIZE; i++) for ( j = 1; j < HIDDEN_SIZE; j++) { weight_ItoH[i][j] = (double)(random() % 100) / 100; } for (i = 0; i < HIDDEN_SIZE; i++) for (j = 1; j < OUTPUT_SIZE; j++) weight_HtoO[i][j] = (double)(random() % 100) / 100; stdInit(); } /* Fixed init of weights */ void fixedInit() { int i,j; for (i = 0; i < INPUT_SIZE; i++) for ( j = 1; j < HIDDEN_SIZE; j++) { weight_ItoH[i][j] = 0.5; } for (i = 0; i < HIDDEN_SIZE; i++) for (j = 1; j < OUTPUT_SIZE; j++) weight_HtoO[i][j] = 0.5; stdInit(); } /* Define exact wights, used for debugging calculations * other flags INPUT = 2, HIDDEN = 2, OUTPUT = 1 */ void debugInit() { stdInit(); weight_ItoH[0][1] = 1; weight_ItoH[0][2] = 1; weight_ItoH[1][1] = 0.62; weight_ItoH[1][2] = 0.42; weight_ItoH[2][1] = 0.55; weight_ItoH[2][2] = -0.17; weight_HtoO[0][1] = 1; weight_HtoO[1][1] = 0.35; weight_HtoO[2][1] = 0.81; } /* calculate Aj's and Ai's (outputs) */ void nnCalc() { int i,j; double total; for (i = 1; i < HIDDEN_SIZE; i++) { total = 0; for (j = 0; j < INPUT_SIZE; j++) total += weight_ItoH[j][i] * input[j]; hidden[i] = 1 / ( 1 + exp(total * (-1))); } for (i = 1; i < OUTPUT_SIZE; i++) { total = 0; for (j = 0; j < HIDDEN_SIZE; j++) total += weight_HtoO[j][i] * hidden[j]; output[i] = 1 / ( 1 + exp(total * (-1))); } } /* train network, NOTE: nnCalc needs to be called first */ void nnTrain() { int i,j; double hidden_delta[HIDDEN_SIZE]; double output_delta[OUTPUT_SIZE]; double output_error[OUTPUT_SIZE]; double hidden_sum_delta[HIDDEN_SIZE]; for (i = 1; i < OUTPUT_SIZE; i++) { output_error[i] = target[i] - output[i]; output_delta[i] = output_error[i] * output[i] * (1 - output[i]); } for (i = 0; i < HIDDEN_SIZE; i++) { hidden_sum_delta[i] = 0; for (j = 1; j < OUTPUT_SIZE; j++) hidden_sum_delta[i] += weight_HtoO[i][j] * output_delta[j]; hidden_delta[i] = hidden[i] * (1 - hidden[i]) * hidden_sum_delta[i]; } for (i = 0; i < HIDDEN_SIZE; i++) for (j = 1; j < OUTPUT_SIZE; j++) { weight_HtoO[i][j] = weight_HtoO[i][j] + LEARN_SPEED * hidden[i] * output_delta[j]; } for (i = 0; i < INPUT_SIZE; i++) for (j = 1; j < HIDDEN_SIZE; j++) { weight_ItoH[i][j] = weight_ItoH[i][j] + LEARN_SPEED * input[i] * hidden_delta[j]; } } /* Verify wether target, matches output */ int nnValidate() { int i; //printf ("Rounding: %lf - %lf\n",output[1], target[1]); for (i = 1; i < OUTPUT_SIZE; i++) if (round(output[i]) != round(target[i])) return FALSE; return TRUE; } /* Pretty print of output */ void nnOutput() { int i; for(i = 0; i < INPUT_SIZE; i++) printf("%lf, ", input[i]); printf("= %lf - %lf - ", output[1], target[1]); if (nnValidate() == TRUE) printf("OK"); else printf("ERROR"); printf("\n"); } /* Pretty print of hidden knobs */ void nnHiddenOutput() { int i; for(i = 0; i < HIDDEN_SIZE; i++) printf("%lf, ", hidden[i]); printf(" - HIDDEN\n"); } /* Pretty print of all weights */ void nnNeuronOutput() { int i,j; for (i = 0; i < INPUT_SIZE; i++) for(j = 0; j < HIDDEN_SIZE; j++) if (weight_ItoH[i][j] != WEIGHT_NOT_USED) printf("weight_ItoH[%i][%i] = %lf\n", i, j, weight_ItoH[i][j]); printf("---\n"); for (i = 0; i < HIDDEN_SIZE; i++) for(j = 0; j < OUTPUT_SIZE; j++) if (weight_ItoH[i][j] != WEIGHT_NOT_USED) printf("weight_HtoO[%i][%i] = %lf\n", i, j, weight_HtoO[i][j]); } int nnReadInput(FILE * handle) { int i = 1; double finput; while (fscanf(handle, "%lf", &finput) != EOF) { if (i < INPUT_SIZE) input[i] = finput; else if (i < (INPUT_SIZE + OUTPUT_SIZE)) target[i - INPUT_SIZE] = finput; /* Calc next input */ i++; /* Skip hidden output knob */ if (i == INPUT_SIZE) i++; if (i == (INPUT_SIZE + OUTPUT_SIZE)) return TRUE; } /* Input not complete */ return FALSE; } /* Verify quality of current network */ double nnQualityCheck(char * file) { double validate_total = 0; double validate_ok = 0; double validate_percent = 0; FILE * handle; handle = fopen(file,"r"); while (nnReadInput(handle) == TRUE) { validate_total++; nnCalc(); if (nnValidate() == TRUE) validate_ok++; //else // nnOutput(); } fclose(handle); validate_percent = (validate_ok / validate_total) * 100; printf("Validating: %.0lf/%.0lf - %.2lf %%\n", validate_ok,validate_total,validate_percent); return(validate_percent); } /* Main program */ int main (int argc, char * argv[]) { int i,training_total, training_best; double quality_max, quality; FILE * handle; /* Set the bias knob */ input[0] = -1; hidden[0] = -1; /* Init set of all wights */ //debugInit(); fixedInit(); //randInit(); /* Set initial quality */ quality_max = nnQualityCheck(file_quality); training_best = 0; training_total = 0; printf("Running neural network with following parameters\n"); printf("Input nodes : %i\n", INPUT_SIZE); printf("Hidden nodes : %i\n", HIDDEN_SIZE); printf("Output nodes : %i\n", OUTPUT_SIZE); printf("Learning rate : %lf\n", LEARN_SPEED); printf("Quality check : %i\n", QUALITY_ROUND); printf("Initial quality : %lf %%\n", quality_max); /* Start training */ //nnNeuronOutput(); i = 1; handle = fopen(file_training,"r"); while ( nnReadInput(handle) == TRUE) { training_total++; nnCalc(); //nnOutput(); //nnHiddenOutput(); if (nnValidate() == FALSE) { nnTrain(); //nnNeuronOutput(); } /* Verifiy quality, stop training when quality is going down */ if ((training_total % QUALITY_ROUND) == 0) { printf("Learned: %i - ", training_total); quality = nnQualityCheck(file_quality); if (quality > quality_max) { quality_max = quality; training_best = training_total; } } } fclose(handle); printf("Max quality: %.2lf%% at training round: %i\n", quality_max, training_best); quality = nnQualityCheck(file_validate); return(EX_OK); }