X-Git-Url: https://ruin.nu/git/?p=germs.git;a=blobdiff_plain;f=fann%2Fsrc%2Ffann_cascade.c;fp=fann%2Fsrc%2Ffann_cascade.c;h=47d1743b152b4bb2a748b98793c63f4c5b5a1b2d;hp=0000000000000000000000000000000000000000;hb=40d817fd1c0ec184927450858ca95b722ae8acba;hpb=0ced9c229cf05fa677686df27eca9f167f11a87f diff --git a/fann/src/fann_cascade.c b/fann/src/fann_cascade.c new file mode 100644 index 0000000..47d1743 --- /dev/null +++ b/fann/src/fann_cascade.c @@ -0,0 +1,996 @@ +/* + Fast Artificial Neural Network Library (fann) + Copyright (C) 2003 Steffen Nissen (lukesky@diku.dk) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "config.h" +#include "fann.h" +#include "string.h" + +#ifndef FIXEDFANN + +/* #define CASCADE_DEBUG */ +/* #define CASCADE_DEBUG_FULL */ + +void fann_print_connections_raw(struct fann *ann) +{ + unsigned int i; + + for(i = 0; i < ann->total_connections_allocated; i++) + { + if(i == ann->total_connections) + { + printf("* "); + } + printf("%f ", ann->weights[i]); + } + printf("\n\n"); +} + +/* Cascade training directly on the training data. + The connected_neurons pointers are not valid during training, + but they will be again after training. + */ +FANN_EXTERNAL void FANN_API fann_cascadetrain_on_data(struct fann *ann, struct fann_train_data *data, + unsigned int max_neurons, + unsigned int neurons_between_reports, + float desired_error) +{ + float error; + unsigned int i; + unsigned int total_epochs = 0; + int desired_error_reached; + + if(neurons_between_reports && ann->callback == NULL) + { + printf("Max neurons %3d. Desired error: %.6f\n", max_neurons, desired_error); + } + + for(i = 1; i <= max_neurons; i++) + { + /* train output neurons */ + total_epochs += fann_train_outputs(ann, data, desired_error); + error = fann_get_MSE(ann); + desired_error_reached = fann_desired_error_reached(ann, desired_error); + + /* print current error */ + if(neurons_between_reports && + (i % neurons_between_reports == 0 + || i == max_neurons || i == 1 || desired_error_reached == 0)) + { + if(ann->callback == NULL) + { + printf + ("Neurons %3d. Current error: %.6f. Total error:%8.4f. Epochs %5d. Bit fail %3d", + i, error, ann->MSE_value, total_epochs, ann->num_bit_fail); + if((ann->last_layer-2) != ann->first_layer) + { + printf(". candidate steepness %.2f. function %s", + (ann->last_layer-2)->first_neuron->activation_steepness, + FANN_ACTIVATIONFUNC_NAMES[(ann->last_layer-2)->first_neuron->activation_function]); + } + printf("\n"); + } + else if((*ann->callback) (ann, data, max_neurons, + neurons_between_reports, desired_error, total_epochs) == -1) + { + /* you can break the training by returning -1 */ + break; + } + } + + if(desired_error_reached == 0) + break; + + if(fann_initialize_candidates(ann) == -1) + { + /* Unable to initialize room for candidates */ + break; + } + + /* train new candidates */ + total_epochs += fann_train_candidates(ann, data); + + /* this installs the best candidate */ + fann_install_candidate(ann); + } + + /* Train outputs one last time but without any desired error */ + total_epochs += fann_train_outputs(ann, data, 0.0); + + if(neurons_between_reports && ann->callback == NULL) + { + printf("Train outputs Current error: %.6f. Epochs %6d\n", fann_get_MSE(ann), + total_epochs); + } + + /* Set pointers in connected_neurons + * This is ONLY done in the end of cascade training, + * since there is no need for them during training. + */ + fann_set_shortcut_connections(ann); +} + +FANN_EXTERNAL void FANN_API fann_cascadetrain_on_file(struct fann *ann, const char *filename, + unsigned int max_neurons, + unsigned int neurons_between_reports, + float desired_error) +{ + struct fann_train_data *data = fann_read_train_from_file(filename); + + if(data == NULL) + { + return; + } + fann_cascadetrain_on_data(ann, data, max_neurons, neurons_between_reports, desired_error); + fann_destroy_train(data); +} + +int fann_train_outputs(struct fann *ann, struct fann_train_data *data, float desired_error) +{ + float error, initial_error, error_improvement; + float target_improvement = 0.0; + float backslide_improvement = -1.0e20f; + unsigned int i; + unsigned int max_epochs = ann->cascade_max_out_epochs; + unsigned int stagnation = max_epochs; + + /* TODO should perhaps not clear all arrays */ + fann_clear_train_arrays(ann); + + /* run an initial epoch to set the initital error */ + initial_error = fann_train_outputs_epoch(ann, data); + + if(fann_desired_error_reached(ann, desired_error) == 0) + return 1; + + for(i = 1; i < max_epochs; i++) + { + error = fann_train_outputs_epoch(ann, data); + + /*printf("Epoch %6d. Current error: %.6f. Bit fail %d.\n", i, error, ann->num_bit_fail); */ + + if(fann_desired_error_reached(ann, desired_error) == 0) + { +#ifdef CASCADE_DEBUG + printf("Error %f < %f\n", error, desired_error); +#endif + return i + 1; + } + + /* Improvement since start of train */ + error_improvement = initial_error - error; + + /* After any significant change, set a new goal and + * allow a new quota of epochs to reach it */ + if((error_improvement > target_improvement) || (error_improvement < backslide_improvement)) + { + /*printf("error_improvement=%f, target_improvement=%f, backslide_improvement=%f, stagnation=%d\n", error_improvement, target_improvement, backslide_improvement, stagnation); */ + + target_improvement = error_improvement * (1.0f + ann->cascade_output_change_fraction); + backslide_improvement = error_improvement * (1.0f - ann->cascade_output_change_fraction); + stagnation = i + ann->cascade_output_stagnation_epochs; + } + + /* No improvement in allotted period, so quit */ + if(i >= stagnation) + { + return i + 1; + } + } + + return max_epochs; +} + +float fann_train_outputs_epoch(struct fann *ann, struct fann_train_data *data) +{ + unsigned int i; + + fann_reset_MSE(ann); + + for(i = 0; i < data->num_data; i++) + { + fann_run(ann, data->input[i]); + fann_compute_MSE(ann, data->output[i]); + fann_update_slopes_batch(ann, ann->last_layer - 1, ann->last_layer - 1); + } + + switch (ann->training_algorithm) + { + case FANN_TRAIN_RPROP: + fann_update_weights_irpropm(ann, (ann->last_layer - 1)->first_neuron->first_con, + ann->total_connections); + break; + case FANN_TRAIN_QUICKPROP: + fann_update_weights_quickprop(ann, data->num_data, + (ann->last_layer - 1)->first_neuron->first_con, + ann->total_connections); + break; + case FANN_TRAIN_BATCH: + case FANN_TRAIN_INCREMENTAL: + fann_error((struct fann_error *) ann, FANN_E_CANT_USE_TRAIN_ALG); + } + + return fann_get_MSE(ann); +} + +int fann_reallocate_connections(struct fann *ann, unsigned int total_connections) +{ + /* The connections are allocated, but the pointers inside are + * first moved in the end of the cascade training session. + */ + +#ifdef CASCADE_DEBUG + printf("realloc from %d to %d\n", ann->total_connections_allocated, total_connections); +#endif + ann->connections = + (struct fann_neuron **) realloc(ann->connections, + total_connections * sizeof(struct fann_neuron *)); + if(ann->connections == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return -1; + } + + ann->weights = (fann_type *) realloc(ann->weights, total_connections * sizeof(fann_type)); + if(ann->weights == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return -1; + } + + ann->train_slopes = + (fann_type *) realloc(ann->train_slopes, total_connections * sizeof(fann_type)); + if(ann->train_slopes == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return -1; + } + + ann->prev_steps = (fann_type *) realloc(ann->prev_steps, total_connections * sizeof(fann_type)); + if(ann->prev_steps == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return -1; + } + + ann->prev_train_slopes = + (fann_type *) realloc(ann->prev_train_slopes, total_connections * sizeof(fann_type)); + if(ann->prev_train_slopes == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return -1; + } + + ann->total_connections_allocated = total_connections; + + return 0; +} + +int fann_reallocate_neurons(struct fann *ann, unsigned int total_neurons) +{ + struct fann_layer *layer_it; + struct fann_neuron *neurons; + unsigned int num_neurons = 0; + unsigned int num_neurons_so_far = 0; + + neurons = + (struct fann_neuron *) realloc(ann->first_layer->first_neuron, + total_neurons * sizeof(struct fann_neuron)); + ann->total_neurons_allocated = total_neurons; + + if(neurons == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return -1; + } + + /* Also allocate room for more train_errors */ + ann->train_errors = (fann_type *) realloc(ann->train_errors, total_neurons * sizeof(fann_type)); + if(ann->train_errors == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return -1; + } + + if(neurons != ann->first_layer->first_neuron) + { + /* Then the memory has moved, also move the pointers */ + +#ifdef CASCADE_DEBUG_FULL + printf("Moving neuron pointers\n"); +#endif + + /* Move pointers from layers to neurons */ + for(layer_it = ann->first_layer; layer_it != ann->last_layer; layer_it++) + { + num_neurons = layer_it->last_neuron - layer_it->first_neuron; + layer_it->first_neuron = neurons + num_neurons_so_far; + layer_it->last_neuron = layer_it->first_neuron + num_neurons; + num_neurons_so_far += num_neurons; + } + } + + return 0; +} + +int fann_initialize_candidates(struct fann *ann) +{ + /* The candidates are allocated after the normal neurons and connections, + * but there is an empty place between the real neurons and the candidate neurons, + * so that it will be possible to make room when the chosen candidate are copied in + * on the desired place. + */ + unsigned int neurons_to_allocate, connections_to_allocate; + unsigned int num_candidates = fann_get_cascade_num_candidates(ann); + unsigned int num_neurons = ann->total_neurons + num_candidates + 1; + unsigned int candidate_connections_in = ann->total_neurons - ann->num_output; + unsigned int candidate_connections_out = ann->num_output; + + /* the number of connections going into a and out of a candidate is + * ann->total_neurons */ + unsigned int num_connections = + ann->total_connections + (ann->total_neurons * (num_candidates + 1)); + unsigned int first_candidate_connection = ann->total_connections + ann->total_neurons; + unsigned int first_candidate_neuron = ann->total_neurons + 1; + unsigned int connection_it, i, j, k, candidate_index; + struct fann_neuron *neurons; + fann_type initial_slope; + + /* First make sure that there is enough room, and if not then allocate a + * bit more so that we do not need to allocate more room each time. + */ + if(num_neurons > ann->total_neurons_allocated) + { + /* Then we need to allocate more neurons + * Allocate half as many neurons as already exist (at least ten) + */ + neurons_to_allocate = num_neurons + num_neurons / 2; + if(neurons_to_allocate < num_neurons + 10) + { + neurons_to_allocate = num_neurons + 10; + } + + if(fann_reallocate_neurons(ann, neurons_to_allocate) == -1) + { + return -1; + } + } + + if(num_connections > ann->total_connections_allocated) + { + /* Then we need to allocate more connections + * Allocate half as many connections as already exist + * (at least enough for ten neurons) + */ + connections_to_allocate = num_connections + num_connections / 2; + if(connections_to_allocate < num_connections + ann->total_neurons * 10) + { + connections_to_allocate = num_connections + ann->total_neurons * 10; + } + + if(fann_reallocate_connections(ann, connections_to_allocate) == -1) + { + return -1; + } + } + + /* Set the neurons. + */ + connection_it = first_candidate_connection; + neurons = ann->first_layer->first_neuron; + candidate_index = first_candidate_neuron; + + for(i = 0; i < ann->cascade_activation_functions_count; i++) + { + for(j = 0; j < ann->cascade_activation_steepnesses_count; j++) + { + for(k = 0; k < ann->cascade_num_candidate_groups; k++) + { + /* TODO candidates should actually be created both in + * the last layer before the output layer, and in a new layer. + */ + neurons[candidate_index].value = 0; + neurons[candidate_index].sum = 0; + + neurons[candidate_index].activation_function = + ann->cascade_activation_functions[i]; + neurons[candidate_index].activation_steepness = + ann->cascade_activation_steepnesses[j]; + + neurons[candidate_index].first_con = connection_it; + connection_it += candidate_connections_in; + neurons[candidate_index].last_con = connection_it; + /* We have no specific pointers to the output weights, but they are + * available after last_con */ + connection_it += candidate_connections_out; + ann->train_errors[candidate_index] = 0; + candidate_index++; + } + } + } + + /* Now randomize the weights and zero out the arrays that needs zeroing out. + */ +#ifdef CASCADE_DEBUG_FULL + printf("random cand weight [%d ... %d]\n", first_candidate_connection, num_connections - 1); +#endif + if(ann->training_algorithm == FANN_TRAIN_RPROP) + { + initial_slope = ann->rprop_delta_zero; + } + else + { + initial_slope = 0.0; + } + for(i = first_candidate_connection; i < num_connections; i++) + { + ann->weights[i] = fann_random_weight(); + /*ann->weights[i] = fann_rand(-0.25,0.25);*/ + ann->train_slopes[i] = 0; + ann->prev_steps[i] = 0; + ann->prev_train_slopes[i] = initial_slope; + } + + return 0; +} + +int fann_train_candidates(struct fann *ann, struct fann_train_data *data) +{ + fann_type best_cand_score = 0.0; + fann_type target_cand_score = 0.0; + fann_type backslide_cand_score = -1.0e20f; + unsigned int i; + unsigned int max_epochs = ann->cascade_max_cand_epochs; + unsigned int stagnation = max_epochs; + + if(ann->cascade_candidate_scores == NULL) + { + ann->cascade_candidate_scores = + (fann_type *) malloc(fann_get_cascade_num_candidates(ann) * sizeof(fann_type)); + if(ann->cascade_candidate_scores == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return 0; + } + } + + for(i = 0; i < max_epochs; i++) + { + best_cand_score = fann_train_candidates_epoch(ann, data); + + if(best_cand_score / ann->MSE_value > ann->cascade_candidate_limit) + { +#ifdef CASCADE_DEBUG + printf("above candidate limit %f/%f > %f", best_cand_score, ann->MSE_value, + ann->cascade_candidate_limit); +#endif + return i + 1; + } + + if((best_cand_score > target_cand_score) || (best_cand_score < backslide_cand_score)) + { +#ifdef CASCADE_DEBUG_FULL + printf("Best candidate score %f, real score: %f\n", ann->MSE_value - best_cand_score, + best_cand_score); + /* printf("best_cand_score=%f, target_cand_score=%f, backslide_cand_score=%f, stagnation=%d\n", best_cand_score, target_cand_score, backslide_cand_score, stagnation); */ +#endif + + target_cand_score = best_cand_score * (1.0f + ann->cascade_candidate_change_fraction); + backslide_cand_score = best_cand_score * (1.0f - ann->cascade_candidate_change_fraction); + stagnation = i + ann->cascade_candidate_stagnation_epochs; + } + + /* No improvement in allotted period, so quit */ + if(i >= stagnation) + { +#ifdef CASCADE_DEBUG + printf("Stagnation with %d epochs, best candidate score %f, real score: %f\n", i + 1, + ann->MSE_value - best_cand_score, best_cand_score); +#endif + return i + 1; + } + } + +#ifdef CASCADE_DEBUG + printf("Max epochs %d reached, best candidate score %f, real score: %f\n", max_epochs, + ann->MSE_value - best_cand_score, best_cand_score); +#endif + return max_epochs; +} + +void fann_update_candidate_slopes(struct fann *ann) +{ + struct fann_neuron *neurons = ann->first_layer->first_neuron; + struct fann_neuron *first_cand = neurons + ann->total_neurons + 1; + struct fann_neuron *last_cand = first_cand + fann_get_cascade_num_candidates(ann); + struct fann_neuron *cand_it; + unsigned int i, j, num_connections; + unsigned int num_output = ann->num_output; + fann_type max_sum, cand_sum, activation, derived, error_value, diff, cand_score; + fann_type *weights, *cand_out_weights, *cand_slopes, *cand_out_slopes; + fann_type *output_train_errors = ann->train_errors + (ann->total_neurons - ann->num_output); + + for(cand_it = first_cand; cand_it < last_cand; cand_it++) + { + cand_score = ann->cascade_candidate_scores[cand_it - first_cand]; + error_value = 0.0; + + /* code more or less stolen from fann_run to fast forward pass + */ + cand_sum = 0.0; + num_connections = cand_it->last_con - cand_it->first_con; + weights = ann->weights + cand_it->first_con; + + /* unrolled loop start */ + i = num_connections & 3; /* same as modulo 4 */ + switch (i) + { + case 3: + cand_sum += weights[2] * neurons[2].value; + case 2: + cand_sum += weights[1] * neurons[1].value; + case 1: + cand_sum += weights[0] * neurons[0].value; + case 0: + break; + } + + for(; i != num_connections; i += 4) + { + cand_sum += + weights[i] * neurons[i].value + + weights[i + 1] * neurons[i + 1].value + + weights[i + 2] * neurons[i + 2].value + weights[i + 3] * neurons[i + 3].value; + } + /* + * for(i = 0; i < num_connections; i++){ + * cand_sum += weights[i] * neurons[i].value; + * } + */ + /* unrolled loop end */ + + max_sum = 150/cand_it->activation_steepness; + if(cand_sum > max_sum) + cand_sum = max_sum; + else if(cand_sum < -max_sum) + cand_sum = -max_sum; + + activation = + fann_activation(ann, cand_it->activation_function, cand_it->activation_steepness, + cand_sum); + /* printf("%f = sigmoid(%f);\n", activation, cand_sum); */ + + cand_it->sum = cand_sum; + cand_it->value = activation; + + derived = fann_activation_derived(cand_it->activation_function, + cand_it->activation_steepness, activation, cand_sum); + + /* The output weights is located right after the input weights in + * the weight array. + */ + cand_out_weights = weights + num_connections; + + cand_out_slopes = ann->train_slopes + cand_it->first_con + num_connections; + for(j = 0; j < num_output; j++) + { + diff = (activation * cand_out_weights[j]) - output_train_errors[j]; +#ifdef CASCADE_DEBUG_FULL + /* printf("diff = %f = (%f * %f) - %f;\n", diff, activation, cand_out_weights[j], output_train_errors[j]); */ +#endif + cand_out_slopes[j] -= 2.0f * diff * activation; +#ifdef CASCADE_DEBUG_FULL + /* printf("cand_out_slopes[%d] <= %f += %f * %f;\n", j, cand_out_slopes[j], diff, activation); */ +#endif + error_value += diff * cand_out_weights[j]; + cand_score -= (diff * diff); +#ifdef CASCADE_DEBUG_FULL + /* printf("cand_score[%d][%d] = %f -= (%f * %f)\n", cand_it - first_cand, j, cand_score, diff, diff); */ + + printf("cand[%d]: error=%f, activation=%f, diff=%f, slope=%f\n", cand_it - first_cand, + output_train_errors[j], (activation * cand_out_weights[j]), diff, + -2.0 * diff * activation); +#endif + } + + ann->cascade_candidate_scores[cand_it - first_cand] = cand_score; + error_value *= derived; + + cand_slopes = ann->train_slopes + cand_it->first_con; + for(i = 0; i < num_connections; i++) + { + cand_slopes[i] -= error_value * neurons[i].value; + } + } +} + +void fann_update_candidate_weights(struct fann *ann, unsigned int num_data) +{ + struct fann_neuron *first_cand = (ann->last_layer - 1)->last_neuron + 1; /* there is an empty neuron between the actual neurons and the candidate neuron */ + struct fann_neuron *last_cand = first_cand + fann_get_cascade_num_candidates(ann) - 1; + + switch (ann->training_algorithm) + { + case FANN_TRAIN_RPROP: + fann_update_weights_irpropm(ann, first_cand->first_con, + last_cand->last_con + ann->num_output); + break; + case FANN_TRAIN_QUICKPROP: + fann_update_weights_quickprop(ann, num_data, first_cand->first_con, + last_cand->last_con + ann->num_output); + break; + case FANN_TRAIN_BATCH: + case FANN_TRAIN_INCREMENTAL: + fann_error((struct fann_error *) ann, FANN_E_CANT_USE_TRAIN_ALG); + break; + } +} + +fann_type fann_train_candidates_epoch(struct fann *ann, struct fann_train_data *data) +{ + unsigned int i, j; + unsigned int best_candidate; + fann_type best_score; + unsigned int num_cand = fann_get_cascade_num_candidates(ann); + fann_type *output_train_errors = ann->train_errors + (ann->total_neurons - ann->num_output); + struct fann_neuron *output_neurons = (ann->last_layer - 1)->first_neuron; + + for(i = 0; i < num_cand; i++) + { + /* The ann->MSE_value is actually the sum squared error */ + ann->cascade_candidate_scores[i] = ann->MSE_value; + } + /*printf("start score: %f\n", ann->MSE_value); */ + + for(i = 0; i < data->num_data; i++) + { + fann_run(ann, data->input[i]); + + for(j = 0; j < ann->num_output; j++) + { + /* TODO only debug, but the error is in opposite direction, this might be usefull info */ + /* if(output_train_errors[j] != (ann->output[j] - data->output[i][j])){ + * printf("difference in calculated error at %f != %f; %f = %f - %f;\n", output_train_errors[j], (ann->output[j] - data->output[i][j]), output_train_errors[j], ann->output[j], data->output[i][j]); + * } */ + + /* + * output_train_errors[j] = (data->output[i][j] - ann->output[j])/2; + * output_train_errors[j] = ann->output[j] - data->output[i][j]; + */ + + output_train_errors[j] = (data->output[i][j] - ann->output[j]); + + switch (output_neurons[j].activation_function) + { + case FANN_LINEAR_PIECE_SYMMETRIC: + case FANN_SIGMOID_SYMMETRIC: + case FANN_SIGMOID_SYMMETRIC_STEPWISE: + case FANN_THRESHOLD_SYMMETRIC: + case FANN_ELLIOT_SYMMETRIC: + case FANN_GAUSSIAN_SYMMETRIC: + output_train_errors[j] /= 2.0; + break; + case FANN_LINEAR: + case FANN_THRESHOLD: + case FANN_SIGMOID: + case FANN_SIGMOID_STEPWISE: + case FANN_GAUSSIAN: + case FANN_GAUSSIAN_STEPWISE: + case FANN_ELLIOT: + case FANN_LINEAR_PIECE: + break; + } + } + + fann_update_candidate_slopes(ann); + } + + fann_update_candidate_weights(ann, data->num_data); + + /* find the best candidate score */ + best_candidate = 0; + best_score = ann->cascade_candidate_scores[best_candidate]; + for(i = 1; i < num_cand; i++) + { + /*struct fann_neuron *cand = ann->first_layer->first_neuron + ann->total_neurons + 1 + i; + * printf("candidate[%d] = activation: %s, steepness: %f, score: %f\n", + * i, FANN_ACTIVATIONFUNC_NAMES[cand->activation_function], + * cand->activation_steepness, ann->cascade_candidate_scores[i]); */ + + if(ann->cascade_candidate_scores[i] > best_score) + { + best_candidate = i; + best_score = ann->cascade_candidate_scores[best_candidate]; + } + } + + ann->cascade_best_candidate = ann->total_neurons + best_candidate + 1; +#ifdef CASCADE_DEBUG_FULL + printf("Best candidate[%d]: with score %f, real score: %f\n", best_candidate, + ann->MSE_value - best_score, best_score); +#endif + + return best_score; +} + +/* add a layer ad the position pointed to by *layer */ +struct fann_layer *fann_add_layer(struct fann *ann, struct fann_layer *layer) +{ + int layer_pos = layer - ann->first_layer; + int num_layers = ann->last_layer - ann->first_layer + 1; + int i; + + /* allocate the layer */ + struct fann_layer *layers = + (struct fann_layer *) realloc(ann->first_layer, num_layers * sizeof(struct fann_layer)); + if(layers == NULL) + { + fann_error((struct fann_error *) ann, FANN_E_CANT_ALLOCATE_MEM); + return NULL; + } + + /* copy layers so that the free space is at the right location */ + for(i = num_layers - 1; i >= layer_pos; i--) + { + layers[i] = layers[i - 1]; + } + + /* the newly allocated layer is empty */ + layers[layer_pos].first_neuron = layers[layer_pos + 1].first_neuron; + layers[layer_pos].last_neuron = layers[layer_pos + 1].first_neuron; + + /* Set the ann pointers correctly */ + ann->first_layer = layers; + ann->last_layer = layers + num_layers; + +#ifdef CASCADE_DEBUG_FULL + printf("add layer at pos %d\n", layer_pos); +#endif + + return layers + layer_pos; +} + +void fann_set_shortcut_connections(struct fann *ann) +{ + struct fann_layer *layer_it; + struct fann_neuron *neuron_it, **neuron_pointers, *neurons; + unsigned int num_connections = 0, i; + + neuron_pointers = ann->connections; + neurons = ann->first_layer->first_neuron; + + for(layer_it = ann->first_layer + 1; layer_it != ann->last_layer; layer_it++) + { + for(neuron_it = layer_it->first_neuron; neuron_it != layer_it->last_neuron; neuron_it++) + { + + neuron_pointers += num_connections; + num_connections = neuron_it->last_con - neuron_it->first_con; + + for(i = 0; i != num_connections; i++) + { + neuron_pointers[i] = neurons + i; + } + } + } +} + +void fann_add_candidate_neuron(struct fann *ann, struct fann_layer *layer) +{ + unsigned int num_connections_in = layer->first_neuron - ann->first_layer->first_neuron; + unsigned int num_connections_out = + (ann->last_layer - 1)->last_neuron - (layer + 1)->first_neuron; + unsigned int num_connections_move = num_connections_out + num_connections_in; + + unsigned int candidate_con, candidate_output_weight; + int i; + + struct fann_layer *layer_it; + struct fann_neuron *neuron_it, *neuron_place, *candidate; + + /* We know that there is enough room for the new neuron + * (the candidates are in the same arrays), so move + * the last neurons to make room for this neuron. + */ + + /* first move the pointers to neurons in the layer structs */ + for(layer_it = ann->last_layer - 1; layer_it != layer; layer_it--) + { +#ifdef CASCADE_DEBUG_FULL + printf("move neuron pointers in layer %d, first(%d -> %d), last(%d -> %d)\n", + layer_it - ann->first_layer, + layer_it->first_neuron - ann->first_layer->first_neuron, + layer_it->first_neuron - ann->first_layer->first_neuron + 1, + layer_it->last_neuron - ann->first_layer->first_neuron, + layer_it->last_neuron - ann->first_layer->first_neuron + 1); +#endif + layer_it->first_neuron++; + layer_it->last_neuron++; + } + + /* also move the last neuron in the layer that needs the neuron added */ + layer->last_neuron++; + + /* this is the place that should hold the new neuron */ + neuron_place = layer->last_neuron - 1; + +#ifdef CASCADE_DEBUG_FULL + printf("num_connections_in=%d, num_connections_out=%d\n", num_connections_in, + num_connections_out); +#endif + + candidate = ann->first_layer->first_neuron + ann->cascade_best_candidate; + + /* the output weights for the candidates are located after the input weights */ + candidate_output_weight = candidate->last_con; + + /* move the actual output neurons and the indexes to the connection arrays */ + for(neuron_it = (ann->last_layer - 1)->last_neuron - 1; neuron_it != neuron_place; neuron_it--) + { +#ifdef CASCADE_DEBUG_FULL + printf("move neuron %d -> %d\n", neuron_it - ann->first_layer->first_neuron - 1, + neuron_it - ann->first_layer->first_neuron); +#endif + *neuron_it = *(neuron_it - 1); + + /* move the weights */ +#ifdef CASCADE_DEBUG_FULL + printf("move weight[%d ... %d] -> weight[%d ... %d]\n", neuron_it->first_con, + neuron_it->last_con - 1, neuron_it->first_con + num_connections_move - 1, + neuron_it->last_con + num_connections_move - 2); +#endif + for(i = neuron_it->last_con - 1; i >= (int)neuron_it->first_con; i--) + { +#ifdef CASCADE_DEBUG_FULL + printf("move weight[%d] = weight[%d]\n", i + num_connections_move - 1, i); +#endif + ann->weights[i + num_connections_move - 1] = ann->weights[i]; + } + + /* move the indexes to weights */ + neuron_it->last_con += num_connections_move; + num_connections_move--; + neuron_it->first_con += num_connections_move; + + /* set the new weight to the newly allocated neuron */ + ann->weights[neuron_it->last_con - 1] = + (ann->weights[candidate_output_weight]) * ann->cascade_weight_multiplier; + candidate_output_weight++; + } + + /* Now inititalize the actual neuron */ + neuron_place->value = 0; + neuron_place->sum = 0; + neuron_place->activation_function = candidate->activation_function; + neuron_place->activation_steepness = candidate->activation_steepness; + neuron_place->last_con = (neuron_place + 1)->first_con; + neuron_place->first_con = neuron_place->last_con - num_connections_in; +#ifdef CASCADE_DEBUG_FULL + printf("neuron[%d] = weights[%d ... %d] activation: %s, steepness: %f\n", + neuron_place - ann->first_layer->first_neuron, neuron_place->first_con, + neuron_place->last_con - 1, FANN_ACTIVATIONFUNC_NAMES[neuron_place->activation_function], + neuron_place->activation_steepness);/* TODO remove */ +#endif + + candidate_con = candidate->first_con; + /* initialize the input weights at random */ +#ifdef CASCADE_DEBUG_FULL + printf("move cand weights[%d ... %d] -> [%d ... %d]\n", candidate_con, + candidate_con + num_connections_in - 1, neuron_place->first_con, + neuron_place->last_con - 1); +#endif + + for(i = 0; i < (int)num_connections_in; i++) + { + ann->weights[i + neuron_place->first_con] = ann->weights[i + candidate_con]; +#ifdef CASCADE_DEBUG_FULL + printf("move weights[%d] -> weights[%d] (%f)\n", i + candidate_con, + i + neuron_place->first_con, ann->weights[i + neuron_place->first_con]); +#endif + } + + /* Change some of main variables */ + ann->total_neurons++; + ann->total_connections += num_connections_in + num_connections_out; + + return; +} + +void fann_install_candidate(struct fann *ann) +{ + struct fann_layer *layer; + + layer = fann_add_layer(ann, ann->last_layer - 1); + fann_add_candidate_neuron(ann, layer); + return; +} + +#endif /* FIXEDFANN */ + +FANN_EXTERNAL unsigned int FANN_API fann_get_cascade_num_candidates(struct fann *ann) +{ + return ann->cascade_activation_functions_count * + ann->cascade_activation_steepnesses_count * + ann->cascade_num_candidate_groups; +} + +FANN_GET_SET(float, cascade_output_change_fraction) +FANN_GET_SET(unsigned int, cascade_output_stagnation_epochs) +FANN_GET_SET(float, cascade_candidate_change_fraction) +FANN_GET_SET(unsigned int, cascade_candidate_stagnation_epochs) +FANN_GET_SET(unsigned int, cascade_num_candidate_groups) +FANN_GET_SET(fann_type, cascade_weight_multiplier) +FANN_GET_SET(fann_type, cascade_candidate_limit) +FANN_GET_SET(unsigned int, cascade_max_out_epochs) +FANN_GET_SET(unsigned int, cascade_max_cand_epochs) + +FANN_GET(unsigned int, cascade_activation_functions_count) +FANN_GET(enum fann_activationfunc_enum *, cascade_activation_functions) + +FANN_EXTERNAL void fann_set_cascade_activation_functions(struct fann *ann, + enum fann_activationfunc_enum * + cascade_activation_functions, + unsigned int + cascade_activation_functions_count) +{ + if(ann->cascade_activation_functions_count != cascade_activation_functions_count) + { + ann->cascade_activation_functions_count = cascade_activation_functions_count; + + /* reallocate mem */ + ann->cascade_activation_functions = + (enum fann_activationfunc_enum *)realloc(ann->cascade_activation_functions, + ann->cascade_activation_functions_count * sizeof(enum fann_activationfunc_enum)); + if(ann->cascade_activation_functions == NULL) + { + fann_error((struct fann_error*)ann, FANN_E_CANT_ALLOCATE_MEM); + return; + } + } + + memmove(ann->cascade_activation_functions, cascade_activation_functions, + ann->cascade_activation_functions_count * sizeof(enum fann_activationfunc_enum)); +} + +FANN_GET(unsigned int, cascade_activation_steepnesses_count) +FANN_GET(fann_type *, cascade_activation_steepnesses) + +FANN_EXTERNAL void fann_set_cascade_activation_steepnesses(struct fann *ann, + fann_type * + cascade_activation_steepnesses, + unsigned int + cascade_activation_steepnesses_count) +{ + if(ann->cascade_activation_steepnesses_count != cascade_activation_steepnesses_count) + { + ann->cascade_activation_steepnesses_count = cascade_activation_steepnesses_count; + + /* reallocate mem */ + ann->cascade_activation_steepnesses = + (fann_type *)realloc(ann->cascade_activation_steepnesses, + ann->cascade_activation_steepnesses_count * sizeof(fann_type)); + if(ann->cascade_activation_steepnesses == NULL) + { + fann_error((struct fann_error*)ann, FANN_E_CANT_ALLOCATE_MEM); + return; + } + } + + memmove(ann->cascade_activation_steepnesses, cascade_activation_steepnesses, + ann->cascade_activation_steepnesses_count * sizeof(fann_type)); +}