diff --git a/classificatore_singolo b/classificatore_singolo index 31ab327..5b9c29a 100755 Binary files a/classificatore_singolo and b/classificatore_singolo differ diff --git a/classificatore_singolo.c b/classificatore_singolo.c index 1518e8e..da47838 100644 --- a/classificatore_singolo.c +++ b/classificatore_singolo.c @@ -1,186 +1,197 @@ #include #include "percettroni.h" -//Scelgo quale categoria voglio identificare. nel caso dello xor -1 -#define CATEGORIA -1 -#define NUM_LAYERS 3 -#define PERCETTRONI_LAYER_0 4 -#define MAX_EPOCHE 1000000 +// Scelgo quale categoria voglio identificare. nel caso dello xor -1 +#define CATEGORIA 7 +#define NUM_LAYERS 4 +#define PERCETTRONI_LAYER_0 64 +#define MAX_EPOCHE 1000 byte get_out_corretto(byte); -void stampa_layer_indirizzo(Layer*); +void stampa_layer_indirizzo(Layer *); void stampa_tempo(time_t[], int); -void main() { +void main() +{ time_t tempo_epoche[MAX_EPOCHE]; srand(time(NULL)); - Dataset *set_appoggio = crea_dataset_xor();//get_dataset(file_immagini, file_label); + Dataset *set_appoggio = get_dataset(file_immagini, file_label); - if(set_appoggio == NULL) + if (set_appoggio == NULL) return; Dataset set = *set_appoggio; free(set_appoggio); ReteNeurale rete_neurale; ReteNeurale *puntatore_rete = caricaReteNeurale(file_pesi); - if(puntatore_rete == NULL) { + if (puntatore_rete == NULL) + { rete_neurale = inizializza_rete_neurale(NUM_LAYERS, PERCETTRONI_LAYER_0, N_INPUTS); - } else { + } + else + { rete_neurale = *puntatore_rete; free(puntatore_rete); printf("Caricate impostazioni rete neurale da file\n"); } + //rete_neurale = inizializza_rete_neurale(NUM_LAYERS, PERCETTRONI_LAYER_0, N_INPUTS); + int corrette = 0; + printf("Numero elementi nel dataset: %d\n", set.size); - //ADDESTRAMENTO - for(int i = 0; i < MAX_EPOCHE; i++) { - - //printf("Epoca %d\n", i); - //stampa_tempo(tempo_epoche, i); - int corrette = 0; - double errore_totale = 0.0; + // ADDESTRAMENTO + for (int i = 0; i < MAX_EPOCHE; i++) + { + /* if (corrette == 4) + { + printf("\nConvergo in epoche: %d\n", i); - for(int indice_set = 0; indice_set < set.size; indice_set++) { + // stampa_risultati_layer_multi(p_ext_1, p_ext_2, pout); + for (int j = 0; j < 4; j++) + { + double **risultato = elabora_sigmoidi(rete_neurale, set.istanze[j]); - //sigmoidi è un array bidimensionale, la prima dimensione identifica il layer, la seconda il percettrone nel layer - // sigmoidi[indice_layer][indice_percettrone] - double **sigmoidi = (double **)malloc(sizeof(double*) * NUM_LAYERS); - - //Il layer di ingresso ha bisogno di una funzione a parte perchè prende byte come input - sigmoidi[0] = (double*)malloc(sizeof(double) * PERCETTRONI_LAYER_0); - sigmoidi[0] = funzioni_attivazione_layer_byte(rete_neurale.layers[0], set.istanze[indice_set].dati); - - //Calcolo le sigmoidi in tutti gli altri layer - for(int j = 1; j < NUM_LAYERS; j++) { - sigmoidi[j] = (double*)malloc(sizeof(double) * rete_neurale.layers[j].size); - sigmoidi[j] = funzioni_attivazione_layer_double(rete_neurale.layers[j], sigmoidi[j-1]); + printf("Input: [%d,%d] -> probabilità: %f -> risultato atteso: %d\n", set.istanze[j].dati[0], set.istanze[j].dati[1], risultato[NUM_LAYERS - 1][0], set.istanze[j].classificazione); } + break; + } */ + + // printf("Epoca %d\n", i); + // stampa_tempo(tempo_epoche, i); + corrette = 0; + double errore_totale = 0.0; + + for (int indice_set = 0; indice_set < set.size -1; indice_set++) + { + //printf("Qui ci arrivo %d\n", indice_set); + double **sigmoidi = elabora_sigmoidi(rete_neurale, set.istanze[indice_set]); + + /* for(int k = 0; k < rete_neurale.size; k++) + for(int j = 0; j < rete_neurale.layers[k].size; j++) + printf("sigmoide[%d][%d] = %f\n", k, j, sigmoidi[k][j]); */ + byte output_corretto = get_out_corretto(set.istanze[indice_set].classificazione); - //Se prevede male - if(previsione(sigmoidi[NUM_LAYERS-1][0]) != output_corretto) { + double **gradienti = (double **)malloc(sizeof(double *) * NUM_LAYERS); - //gradienti è un array bidimensionale, la prima dimensione identifica il layer, la seconda il percettrone nel layer - //gradienti[indice_layer][indice_percettrone] - //Questo vettore identifica i gradienti dei percettroni - double **gradienti = (double**)malloc(sizeof(double*) * NUM_LAYERS); + // Alloco la dimensione per ogni layer + for (int indice_layer = 0; indice_layer < NUM_LAYERS; indice_layer++) + { + gradienti[indice_layer] = (double *)malloc(sizeof(double) * rete_neurale.layers[indice_layer].size); + } - //Alloco la dimensione per ogni layer - for(int indice_layer = 0; indice_layer < NUM_LAYERS; indice_layer++) { - gradienti[indice_layer] = (double*)malloc(sizeof(double) * rete_neurale.layers[indice_layer].size); - } + - /* Gestione dell'errore - Funzione di perdita -> errore quadratico medio: (risultato_esatto - previsione)^2 - Gradiente dell'errore rispetto all'attivazione = derivata(funzione_perdita) * derivata(sigmoide_out) - = -(risultato_esatto - previsione) * (sigmoide_out * (1 - sigmoide_out)) - - //Per ogni percettrone sugli altri layer: - - calcolare derivata della propria sigmoide - - gradiente dell'errore retropropagato = peso del ne - - */ - - //Derivata funzione di perdita - double gradiente_errore = -(output_corretto - sigmoidi[NUM_LAYERS-1][0]); - //Derivata funzione attivazione - double derivata_sigmoide_out = sigmoidi[NUM_LAYERS-1][0] * (1 - sigmoidi[NUM_LAYERS-1][0]); - if (derivata_sigmoide_out == 0.0) derivata_sigmoide_out = 1; - - //Gradiente del percettrone output - gradienti[NUM_LAYERS-1][0] = gradiente_errore * derivata_sigmoide_out; + // Derivata funzione di perdita + // printf("output_corretto = %d, previsione: %f\n", output_corretto, sigmoidi[NUM_LAYERS - 1][0]); + double gradiente_errore = (output_corretto - sigmoidi[NUM_LAYERS - 1][0]); - //Ricorda di partire dal penultimo layer in quanto l'ultimo è già fatto - discesa_gradiente(rete_neurale, sigmoidi, gradienti); + errore_totale += pow(gradiente_errore, 2); - /* A questo punto ho tutti i gradienti dei percettroni, non mi resta che trovare i gradienti dei pesi e correggerli - */ + // Derivata funzione attivazione + double derivata_sigmoide_out = sigmoidi[NUM_LAYERS - 1][0] * (1.0 - sigmoidi[NUM_LAYERS - 1][0]); + // if (derivata_sigmoide_out == 0.0) derivata_sigmoide_out = 1; - //Correggo il livello output - for(int indice_peso = 0; indice_peso < rete_neurale.layers[NUM_LAYERS-1].percettroni[0].size; indice_peso ++) { - //Determino gradiente del peso - double gradiente_peso = gradienti[NUM_LAYERS-1][0] * sigmoidi[NUM_LAYERS-2][indice_peso]; - rete_neurale.layers[NUM_LAYERS-1].percettroni[0].pesi[indice_peso] -= gradiente_peso * LRE; - } - rete_neurale.layers[NUM_LAYERS-1].percettroni[0].bias -= gradienti[NUM_LAYERS-1][0] * LRE; - - + // Gradiente del percettrone output + gradienti[NUM_LAYERS - 1][0] = gradiente_errore * derivata_sigmoide_out; - //Applico la correzione dal penultimo layer andando indietro fino al secondo (il primo si fa diverso) - for(int indice_layer = NUM_LAYERS - 2; indice_layer >= 0; indice_layer--) { - //Applico la correzione a tutti i percettroni del layer dal primo a seguire - for(int indice_percettrone = 0; indice_percettrone < rete_neurale.layers[indice_layer].size; indice_percettrone++) { - //Devo prendere il gradiente del percettrone e moltiplicarlo con gli input associati ai pesi - if(indice_layer != 0) { - correggi_pesi_percettrone_double(&rete_neurale.layers[indice_layer].percettroni[indice_percettrone], indice_layer, sigmoidi, gradienti[indice_layer][indice_percettrone]); - } else { - correggi_pesi_percettrone_byte(&rete_neurale.layers[0].percettroni[indice_percettrone], set.istanze[indice_set], gradienti[0][indice_percettrone], indice_percettrone); - } - + discesa_gradiente(rete_neurale, sigmoidi, gradienti); + + // A questo punto ho tutti i gradienti dei percettroni, non mi resta che trovare i gradienti dei pesi e correggerli + + // Correggo il livello output + for (int indice_peso = 0; indice_peso < rete_neurale.layers[NUM_LAYERS - 1].percettroni[0].size; indice_peso++) + { + // Determino gradiente del peso + double gradiente_peso = gradienti[NUM_LAYERS - 1][0] * sigmoidi[NUM_LAYERS - 2][indice_peso]; + rete_neurale.layers[NUM_LAYERS - 1].percettroni[0].pesi[indice_peso] += gradiente_peso * LRE; + } + rete_neurale.layers[NUM_LAYERS - 1].percettroni[0].bias += gradienti[NUM_LAYERS - 1][0] * LRE; + + // Applico la correzione dal penultimo layer andando indietro fino al secondo (il primo si fa diverso) + for (int indice_layer = NUM_LAYERS - 2; indice_layer >= 0; indice_layer--) + { + // Applico la correzione a tutti i percettroni del layer dal primo a seguire + for (int indice_percettrone = 0; indice_percettrone < rete_neurale.layers[indice_layer].size; indice_percettrone++) + { + // Devo prendere il gradiente del percettrone e moltiplicarlo con gli input associati ai pesi + if (indice_layer != 0) + { + correggi_pesi_percettrone_double(&rete_neurale.layers[indice_layer].percettroni[indice_percettrone], indice_layer, sigmoidi, gradienti[indice_layer][indice_percettrone]); + } + else + { + correggi_pesi_percettrone_byte(&rete_neurale.layers[0].percettroni[indice_percettrone], set.istanze[indice_set], gradienti[0][indice_percettrone], indice_percettrone); } } - - - - //gradienti[NUM_LAYERS-1][0] = (output_corretto - sigmoidi[NUM_LAYERS-1][0]); - errore_totale += gradienti[NUM_LAYERS-1][0]; - - //correggi_layer_interni(&rete_neurale, gradienti, sigmoidi); - //correggi_layer_input(&rete_neurale.layers[0], gradienti, sigmoidi, set.istanze[indice_set].dati, NUM_LAYERS); } - else + + if (previsione(sigmoidi[NUM_LAYERS - 1][0]) == output_corretto) { corrette++; } + } - if(corrette == 4) { - printf("\tConvergo all'epoca %d\n", i); - break; + printf("Errore: %f, risposte corrette: %d/%d\n", errore_totale / 10000, corrette, set.size); + // printf("\tRisposte corrette: %d\n", corrette); + + /* if (i == MAX_EPOCHE - 1) + { + printf("\nUltima epoca (%d), stato della rete:\n", i); + double **risultato; + for (int j = 0; j < 4; j++) + { + risultato = elabora_sigmoidi(rete_neurale, set.istanze[j]); + printf("Input: [%d,%d] -> probabilità: %f -> previsione: %d -> risultato attesto: %d\n", set.istanze[j].dati[0], set.istanze[j].dati[1], risultato[NUM_LAYERS - 1][0], previsione(risultato[NUM_LAYERS - 1][0]), set.istanze[j].classificazione); + for (int k = 0; k < rete_neurale.size; k++) + for (int j = 0; j < rete_neurale.layers[k].size; j++) + printf("sigmoide[%d][%d] = %f\n", k, j, risultato[k][j]); } - } - - printf("Errore: %f\n", errore_totale / 4); - printf("\tRisposte corrette: %d\n", corrette); - - if(corrette == set.size) { - break; - } + } */ } - //salvaReteNeurale(file_pesi, &rete_neurale); + salvaReteNeurale(file_pesi, &rete_neurale); } -//Questa funzione ritorna 1 se la categoria è quella che voglio individuare, altrimenti 0 -byte get_out_corretto(byte categoria) { - if(CATEGORIA != -1) { - if(categoria == CATEGORIA) +// Questa funzione ritorna 1 se la categoria è quella che voglio individuare, altrimenti 0 +byte get_out_corretto(byte categoria) +{ + if (CATEGORIA != -1) + { + if (categoria == CATEGORIA) return 1; else return 0; } - else return categoria; + else + return categoria; } -void stampa_layer_indirizzo(Layer *layer) { - for(int i = 0; i < layer->size; i++) { +void stampa_layer_indirizzo(Layer *layer) +{ + for (int i = 0; i < layer->size; i++) + { printf("Percettrone %d ->", i); - for(int j = 0; j < layer->percettroni->size; j++) { - printf("\t peso %d, valore: %f",j, layer->percettroni[i].pesi[j]); + for (int j = 0; j < layer->percettroni->size; j++) + { + printf("\t peso %d, valore: %f", j, layer->percettroni[i].pesi[j]); layer->percettroni[i].pesi[j] += 1; } printf("\n"); } } -void stampa_tempo(time_t tempo_epoche[], int i) { +void stampa_tempo(time_t tempo_epoche[], int i) +{ time(&tempo_epoche[i]); - if(i > 0) { - double tempo_trascorso_epoca = difftime(tempo_epoche[i], tempo_epoche[i-1]); + if (i > 0) + { + double tempo_trascorso_epoca = difftime(tempo_epoche[i], tempo_epoche[i - 1]); double tempo_trascorso_totale = difftime(tempo_epoche[i], tempo_epoche[0]); int minuti_epoca = (int)tempo_trascorso_epoca / 60; int secondi_epoca = (int)tempo_trascorso_epoca % 60; diff --git a/classificatore_singolo_xor b/classificatore_singolo_xor new file mode 100755 index 0000000..d0b75e9 Binary files /dev/null and b/classificatore_singolo_xor differ diff --git a/percettroni.h b/percettroni.h index 284394c..b3fac51 100644 --- a/percettroni.h +++ b/percettroni.h @@ -6,28 +6,31 @@ char *file_pesi = "rete_pesi.bin"; char *file_immagini = "mnist/t10k-images.idx3-ubyte"; char *file_label = "mnist/t10k-labels.idx1-ubyte"; -//#include "mnist/mnist_manager.h" -//#include "cifar_10/cifar10_manager.h" -#include "xor_manager.h" +#include "mnist/mnist_manager.h" +// #include "cifar_10/cifar10_manager.h" +//#include "xor_manager.h" // Siccome il char è un byte che rappresenta il valore tra 0 e 255. Per evitare confusioni definisco il tipo "byte" come in Java typedef unsigned char byte; -double LRE = 0.5; +double LRE = 2; double soglia_sigmoide = 0.5; -typedef struct { +typedef struct +{ double *pesi; double bias; int size; } Percettrone; -typedef struct { +typedef struct +{ Percettrone *percettroni; int size; } Layer; -typedef struct { +typedef struct +{ Layer *layers; int size; } ReteNeurale; @@ -38,37 +41,34 @@ Percettrone inzializza_percettrone(int); ReteNeurale inizializza_rete_neurale(int, int, int); Layer inizializza_layer(int, int); -double sigmoide_byte(Percettrone, byte*, int); -double sigmoide_double(Percettrone, double*, int); -double *funzioni_attivazione_layer_byte(Layer, byte*); -double *funzioni_attivazione_layer_double(Layer, double*); - -void correggi_layer_interni(ReteNeurale*, double**, double**); -void correggi_layer_input(Layer*, double**, double**, byte*, int); - -void discesa_gradiente(ReteNeurale, double**, double**); -double calcola_gradiente_disceso(ReteNeurale, int, int, double**); -void correggi_pesi_percettrone_double(Percettrone*, int, double**, double); -void correggi_pesi_percettrone_byte(Percettrone*, Istanza, double, int); +double sigmoide(Percettrone p, double*); +double **elabora_sigmoidi(ReteNeurale, Istanza); +void discesa_gradiente(ReteNeurale, double **, double **); +double calcola_gradiente_disceso(ReteNeurale, int, int, double **); +void correggi_pesi_percettrone_double(Percettrone *, int, double **, double); +void correggi_pesi_percettrone_byte(Percettrone *, Istanza, double, int); int previsione(double); -void salvaReteNeurale(const char*, ReteNeurale*); -ReteNeurale *caricaReteNeurale(const char*); +void salvaReteNeurale(const char *, ReteNeurale *); +ReteNeurale *caricaReteNeurale(const char *); -//Questa funzione genera un valore reale random compreso nell'intervallo [-1, 1] -double randomico() { +/* + ################# INIZIALIZZAZIONI ################################ +*/ +// Questa funzione genera un valore reale random compreso nell'intervallo [-1, 1] +double randomico() +{ // Genero numeri nell'intervallo [-1,1] - return ((double)(rand() % 101 * 0.01 * 2 ) -1); + return ((double)(rand() % 101 * 0.01 * 2.0) -1.0); } -//Questa funzione inizializza il percettrone allocando la memoria in base al numero dei pesi che voglio ed inizializza il loro valore usando randomico() -Percettrone inizializza_percettrone(int n_pesi) { +Percettrone inizializza_percettrone(int n_pesi) +{ Percettrone p; - p.pesi = (double*) malloc(sizeof(double) * n_pesi); - for(int i = 0; i < n_pesi; i++) { + p.pesi = (double *)malloc(sizeof(double) * n_pesi); + for (int i = 0; i < n_pesi; i++) { p.pesi[i] = randomico(); - //printf("peso[%d]: %f\n",i, p.pesi[i]); } p.bias = randomico(); @@ -78,41 +78,14 @@ Percettrone inizializza_percettrone(int n_pesi) { return p; } -ReteNeurale inizializza_rete_neurale(int numero_layers, int numero_percettroni_iniziali, int numero_input) { - ReteNeurale r; - r.layers = (Layer*)malloc(sizeof(Layer) * numero_layers); - r.size = numero_layers; - - //Funzione esponenziale inversa layer 5 - for (int livello = 0; livello < numero_layers; livello++) { - double esponente = (double)livello / (double)numero_layers; - double frazione = (double)1 / (double)numero_percettroni_iniziali; - - int numero_percettroni_livello = (int)((double)numero_percettroni_iniziali * pow(frazione, esponente)); - if(livello == numero_layers -1) - numero_percettroni_livello = 1; - - //printf("esponente %f -> frazione: %f\n", esponente, frazione); - printf("Layer %d -> percettroni: %d\n", livello, numero_percettroni_livello); - - if(livello == 0) - r.layers[livello] = inizializza_layer(numero_percettroni_livello, numero_input); - else - r.layers[livello] = inizializza_layer(numero_percettroni_livello, r.layers[livello-1].size); - } - - return r; -} - -//Questa funzione serve ad inizializzare il singolo layer con il numero di percettroni che vogliamo -//Ogni percettrone a sua volta viene automaticamente inizializzato con il numero di pesi che vogliamo e coi valori di partenza -Layer inizializza_layer(int n_percettroni, int n_pesi) { +Layer inizializza_layer(int n_percettroni, int n_pesi) +{ Layer layer; layer.percettroni = (Percettrone *)malloc(sizeof(Percettrone) * n_percettroni); - for(int i = 0; i < n_percettroni; i++) { + for (int i = 0; i < n_percettroni; i++) + { layer.percettroni[i] = inizializza_percettrone(n_pesi); - } layer.size = n_percettroni; @@ -120,179 +93,173 @@ Layer inizializza_layer(int n_percettroni, int n_pesi) { return layer; } -//Questa funzione viene usata per il primo livello perchè ha un vettore di byte (unsigned char) in input -double sigmoide_byte(Percettrone p, byte *valori, int n_input) { - - double sommatoria = 0.0; - //printf("valori: [%d][%d]", valori[0], valori[1]); - //printf("pesi: [%f][%f]", p.pesi[0], p.pesi[1]); +ReteNeurale inizializza_rete_neurale(int numero_layers, int numero_percettroni_iniziali, int numero_input) +{ + ReteNeurale r; + r.layers = (Layer *)malloc(sizeof(Layer) * numero_layers); + r.size = numero_layers; - for(int i = 0; i < n_input; i++) { - sommatoria += (valori[i] * p.pesi[i]); + // Funzione esponenziale inversa layer 5 + for (int livello = 0; livello < numero_layers; livello++) + { + double esponente = (double)livello / (double)numero_layers; + double frazione = (double)1 / (double)numero_percettroni_iniziali; + + int numero_percettroni_livello = (int)((double)numero_percettroni_iniziali * pow(frazione, esponente)); + if (livello == numero_layers - 1) + numero_percettroni_livello = 1; + + // printf("esponente %f -> frazione: %f\n", esponente, frazione); + printf("Layer %d -> percettroni: %d\n", livello, numero_percettroni_livello); + + if (livello == 0) + r.layers[livello] = inizializza_layer(numero_percettroni_livello, numero_input); + else + r.layers[livello] = inizializza_layer(numero_percettroni_livello, r.layers[livello - 1].size); } - //printf("sommatoria= %f\n", sommatoria); - double funzione = sommatoria + p.bias; - double potenza_e = exp(-funzione); - //printf("potenza_e= %f\n", potenza_e); - //formula sigmoide - double risultato = 1 / ( 1 + potenza_e); - //printf("risultato= %f\n", risultato); - return risultato; + + return r; } -//Questa funzione viene usata per gli altri livelli dove gli input sono double, ossia i valori della sigmoide dei livelli precedenti -double sigmoide_double(Percettrone p, double *valori, int n_input) { + +/* + ################# PREVISIONI ################################ +*/ + + +double sigmoide(Percettrone p, double *valori) +{ double sommatoria = 0.0; - for(int i = 0; i < n_input; i++) { + for (int i = 0; i < p.size; i++) { sommatoria += (valori[i] * p.pesi[i]); + //printf("valore [%f] peso[%f] ", valori[i], p.pesi[i]); } - double funzione = sommatoria + p.bias; - //printf("sommatoria= %f\n", sommatoria); - double potenza_e = exp(-funzione); - //printf("potenza_e= %f\n", potenza_e); - //formula sigmoide - double risultato = 1 / ( 1 + potenza_e); - //printf("risultato= %f\n", risultato); + sommatoria += p.bias; + double risultato = 1.0 / (1.0 + exp(-sommatoria)); + //printf(" sommatoria %f -> %f\n",sommatoria, risultato); return risultato; } -//Questa funzione calcola tutte le funzioni di attivazione dei percettroni del layer che prende dei byte come inputs -double *funzioni_attivazione_layer_byte(Layer layer, byte *inputs) { - - double *funzioni = (double*)malloc(sizeof(double) * layer.size); - - for(int i = 0; i < layer.size; i++) { - funzioni[i] = sigmoide_byte(layer.percettroni[i], inputs, layer.percettroni[i].size); - //printf("\tsigmoide layer input %f\n", funzioni[i]); - } - - return funzioni; -} - -//Questa funzione calcola tutte le funzioni di attivazione dei percettroni del layer che prende dei double come inputs (le sigmoidi del livello precedente) -double *funzioni_attivazione_layer_double(Layer layer, double *inputs) { - - double *funzioni = (double*)malloc(sizeof(double) * layer.size); - - for(int i = 0; i < layer.size; i++) { - funzioni[i] = sigmoide_double(layer.percettroni[i], inputs, layer.percettroni[i].size); - //printf("\tsigmoide layer %d: %f\n", i, funzioni[i]); - } - - return funzioni; -} - -//Questa funzione restituisce il valore 0,1 in base alla soglia di attivazione della funzione sigmoide -int previsione(double valore) { - if(valore >= soglia_sigmoide) +int previsione(double valore) +{ + if (valore >= soglia_sigmoide) return 1; else return 0; } + +void discesa_gradiente(ReteNeurale rete, double **sigmoidi, double **gradienti) +{ + //printf("Qui?\n"); + // For che scorre i layer dal penultimo al primo QUINI SIZE -2 + for (int indice_layer = rete.size - 2; indice_layer >= 0; indice_layer--) + { + //printf("layer: %d ", indice_layer); + // printf("Mi trovo nel layer %d, ho %d percettroni\n", indice_layer, rete.layers[indice_layer].size); + // For che scorre i percettroni del layer partendo dal primo + // Per ogni percettrone mi devo prendere il gradiente disceso dal livello sopra e moltiplicarlo per la derivata di attivazione + for (int indice_percettrone = 0; indice_percettrone < rete.layers[indice_layer].size; indice_percettrone++) + { + //printf("percettrone: %d ", indice_percettrone); + double derivata_attivazione = sigmoidi[indice_layer][indice_percettrone] * (1.0 - sigmoidi[indice_layer][indice_percettrone]); - - - -//Questa funzione prende la matrice dei gradienti e la matrice delle sigmoidi per correggere tutti i layer tranne quello di ingresso -void correggi_layer_interni(ReteNeurale *rete, double **gradienti, double **sigmoidi) { - - for(int indice_layer = rete->size-1; indice_layer > 0; indice_layer--) { - for(int indice_percettrone = 0; indice_percettrone < rete->layers[indice_layer].size; indice_percettrone++) {//Numero percettroni - - for(int indice_peso = 0; indice_peso < rete->layers[indice_layer].percettroni[indice_percettrone].size; indice_peso++) {//Numero pesi - gradienti[indice_layer][indice_percettrone] = gradienti[rete->size-1][0] * (sigmoidi[indice_layer][indice_percettrone] * (1 - sigmoidi[indice_layer][indice_percettrone])); - rete->layers[indice_layer].percettroni[indice_percettrone].pesi[indice_peso] += (gradienti[indice_layer][indice_percettrone] * LRE * sigmoidi[indice_layer-1][indice_percettrone]); - //rete->layers[indice_layer].percettroni[indice_percettrone].pesi[indice_peso] += (gradienti[rete->size-1][0] * LRE * sigmoidi[indice_layer-1][indice_percettrone]); - } - rete->layers[indice_layer].percettroni[indice_percettrone].bias += (gradienti[indice_layer][indice_percettrone] * LRE); - //printf("bias: %f\n", rete->layers[indice_layer].percettroni[indice_percettrone].bias); - } - } -} -//Questa funzione prende tutti i parametri della precedente + gli input passati dal dataset per correggere il layer di ingresso -void correggi_layer_input(Layer *layer, double **gradienti, double **sigmoidi, byte *inputs, int n_layers) { - //L'indice del layer d'ingresso che prende byte per input - int indice_layer = 0; - for(int indice_percettrone = 0; indice_percettrone < layer->size; indice_percettrone++) {//Numero percettroni - for(int indice_peso = 0; indice_peso < layer->percettroni->size; indice_peso++) { //Numero pesi - - gradienti[indice_layer][indice_percettrone] = gradienti[n_layers-1][0] * (sigmoidi[indice_layer][indice_percettrone] * (1 - sigmoidi[indice_layer][indice_percettrone])); - layer->percettroni[indice_percettrone].pesi[indice_peso] += (gradienti[indice_layer][indice_percettrone] * LRE * inputs[indice_peso]); - //layer->percettroni[indice_percettrone].pesi[indice_peso] += (gradienti[n_layers-1][0] * LRE * inputs[indice_peso]); - } - layer->percettroni[indice_percettrone].bias += (gradienti[n_layers-1][0] * LRE); - } -} - - - - - - -void discesa_gradiente(ReteNeurale rete, double **sigmoidi, double **gradienti) { - //For che scorre i layer dal penultimo al primo QUINI SIZE -2 - for(int indice_layer = rete.size -2; indice_layer >= 0; indice_layer--) { - //printf("Mi trovo nel layer %d, ho %d percettroni\n", indice_layer, rete.layers[indice_layer].size); - - //For che scorre i percettroni del layer partendo dal primo - //Per ogni percettrone mi devo prendere il gradiente disceso dal livello sopra e moltiplicarlo per la derivata di attivazione - for(int indice_percettrone = 0; indice_percettrone < rete.layers[indice_layer].size; indice_percettrone++) { - - double derivata_attivazione = sigmoidi[indice_layer][indice_percettrone] * (1 - sigmoidi[indice_layer][indice_percettrone]); - - //Passo anche l'indice del percettrone perchè corrisponde all'indice del peso del livello sopra + // Passo anche l'indice del percettrone perchè corrisponde all'indice del peso del livello sopra double gradiente_disceso = calcola_gradiente_disceso(rete, indice_layer + 1, indice_percettrone, gradienti); - + gradienti[indice_layer][indice_percettrone] = gradiente_disceso * derivata_attivazione; } + //printf("\n"); } + //printf("Qui ?\n"); } -double calcola_gradiente_disceso(ReteNeurale rete, int livello, int indice_peso, double **gradienti) { - //printf("Qui ci arrivo\n"); +double calcola_gradiente_disceso(ReteNeurale rete, int livello, int indice_peso, double **gradienti) +{ + // printf("Qui ci arrivo\n"); double sommatoria = 0.0; - //printf("Layer %d: N_percettroni: %d\n", livello, rete.layers[livello].size); - //Calcolo la sommatoria dei gradienti dei percettroni per i pesi - for(int indice_percettrone = 0; indice_percettrone < rete.layers[livello].size; indice_percettrone++) { + // printf("Layer %d: N_percettroni: %d\n", livello, rete.layers[livello].size); + // Calcolo la sommatoria dei gradienti dei percettroni per i pesi + for (int indice_percettrone = 0; indice_percettrone < rete.layers[livello].size; indice_percettrone++) + { sommatoria += (gradienti[livello][indice_peso] * rete.layers[livello].percettroni[indice_percettrone].pesi[indice_peso]); } return sommatoria; } -void correggi_pesi_percettrone_double(Percettrone *p, int layer, double **input, double gradiente_percettrone) { +double **elabora_sigmoidi(ReteNeurale rete, Istanza istanza) +{ + // sigmoidi è un array bidimensionale, la prima dimensione identifica il layer, la seconda il percettrone nel layer + // sigmoidi[indice_layer][indice_percettrone] + double **sigmoidi = (double **)malloc(sizeof(double *) * rete.size); + double *inputs = (double *)malloc(sizeof(double *) * N_INPUTS); + for(int i = 0; i < N_INPUTS; i++) { + inputs[i] = (double)istanza.dati[i]; + } - for (int indice_peso = 0; indice_peso < p->size; indice_peso++) { - //Determino il gradiente del peso + sigmoidi[0] = (double *)malloc(sizeof(double) * rete.layers[0].size); + for(int indice_percettrone = 0; indice_percettrone < rete.layers[0].size; indice_percettrone ++) { + sigmoidi[0][indice_percettrone] = sigmoide(rete.layers[0].percettroni[indice_percettrone], inputs); + } + + for(int indice_layer = 1; indice_layer < rete.size; indice_layer ++) { + sigmoidi[indice_layer] = (double *)malloc(sizeof(double) * rete.layers[indice_layer].size); + for(int indice_percettrone = 0; indice_percettrone < rete.layers[indice_layer].size; indice_percettrone ++) { + sigmoidi[indice_layer][indice_percettrone] = sigmoide(rete.layers[indice_layer].percettroni[indice_percettrone], sigmoidi[indice_layer-1]); + } + } + + return sigmoidi; +} + + +/* + ################# CORREZIONI ################################ +*/ + + +void correggi_pesi_percettrone_double(Percettrone *p, int layer, double **input, double gradiente_percettrone) +{ + + for (int indice_peso = 0; indice_peso < p->size; indice_peso++) + { + // Determino il gradiente del peso double gradiente_peso = gradiente_percettrone * input[layer - 1][indice_peso]; - //Modifico il peso - p->pesi[indice_peso] -= (gradiente_peso * LRE); + // Modifico il peso + p->pesi[indice_peso] += (gradiente_peso * LRE); } - p->bias -= (gradiente_percettrone * LRE); + p->bias += (gradiente_percettrone * LRE); } -void correggi_pesi_percettrone_byte(Percettrone *p, Istanza input, double gradiente_percettrone, int indice_percettrone) { - for (int indice_peso = 0; indice_peso < p->size; indice_peso++) { - //Determino il gradiente del peso +void correggi_pesi_percettrone_byte(Percettrone *p, Istanza input, double gradiente_percettrone, int indice_percettrone) +{ + for (int indice_peso = 0; indice_peso < p->size; indice_peso++) + { + // Determino il gradiente del peso double gradiente_peso = gradiente_percettrone * (double)input.dati[indice_peso]; - - //Modifico il peso Qui si impalla perchè per qualche ragione arriva size elevatissimo - p->pesi[indice_peso] -= (gradiente_peso * LRE); + + // Modifico il peso Qui si impalla perchè per qualche ragione arriva size elevatissimo + p->pesi[indice_peso] += (gradiente_peso * LRE); } - - p->bias -= (gradiente_percettrone * LRE); + + p->bias += (gradiente_percettrone * LRE); } -//Una volta finito il ciclo delle epoche viene salvato lo stato della rete neurale -void salvaReteNeurale(const char *filename, ReteNeurale *rete) { + +/* + ################# IMPORT EXPORT ################################ +*/ +void salvaReteNeurale(const char *filename, ReteNeurale *rete) +{ FILE *file = fopen(filename, "wb"); - if (!file) { + if (!file) + { perror("Errore nell'apertura del file"); exit(EXIT_FAILURE); } @@ -301,12 +268,14 @@ void salvaReteNeurale(const char *filename, ReteNeurale *rete) { fwrite(&rete->size, sizeof(int), 1, file); // Scrivi ogni layer - for (int i = 0; i < rete->size; i++) { + for (int i = 0; i < rete->size; i++) + { Layer *layer = &rete->layers[i]; fwrite(&layer->size, sizeof(int), 1, file); // Scrivi ogni percettrone nel layer - for (int j = 0; j < layer->size; j++) { + for (int j = 0; j < layer->size; j++) + { Percettrone *perc = &layer->percettroni[j]; fwrite(&perc->size, sizeof(int), 1, file); fwrite(perc->pesi, sizeof(double), perc->size, file); @@ -317,17 +286,19 @@ void salvaReteNeurale(const char *filename, ReteNeurale *rete) { fclose(file); } -//Quando parte il programma carica lo stato della rete neurale dal file oppure inizializza una rete neurale con pesi random se il file non esiste -ReteNeurale *caricaReteNeurale(const char *filename) { +ReteNeurale *caricaReteNeurale(const char *filename) +{ FILE *file = fopen(filename, "rb"); - if (!file) { + if (!file) + { perror("Errore nell'apertura del file"); return NULL; } ReteNeurale *rete = malloc(sizeof(ReteNeurale)); - if (!rete) { + if (!rete) + { perror("Errore nell'allocazione della memoria"); return NULL; } @@ -335,27 +306,32 @@ ReteNeurale *caricaReteNeurale(const char *filename) { // Leggi il numero di layer fread(&rete->size, sizeof(int), 1, file); rete->layers = malloc(rete->size * sizeof(Layer)); - if (!rete->layers) { + if (!rete->layers) + { perror("Errore nell'allocazione della memoria"); return NULL; } // Leggi ogni layer - for (int i = 0; i < rete->size; i++) { + for (int i = 0; i < rete->size; i++) + { Layer *layer = &rete->layers[i]; fread(&layer->size, sizeof(int), 1, file); layer->percettroni = malloc(layer->size * sizeof(Percettrone)); - if (!layer->percettroni) { + if (!layer->percettroni) + { perror("Errore nell'allocazione della memoria"); return NULL; } // Leggi ogni percettrone nel layer - for (int j = 0; j < layer->size; j++) { + for (int j = 0; j < layer->size; j++) + { Percettrone *perc = &layer->percettroni[j]; fread(&perc->size, sizeof(int), 1, file); perc->pesi = malloc(perc->size * sizeof(double)); - if (!perc->pesi) { + if (!perc->pesi) + { perror("Errore nell'allocazione della memoria"); return NULL; } @@ -366,4 +342,130 @@ ReteNeurale *caricaReteNeurale(const char *filename) { fclose(file); return rete; -} \ No newline at end of file +} + + + + + + + + + +/* double sigmoide_byte(Percettrone, byte *, int); +double sigmoide_double(Percettrone, double *, int); +double *funzioni_attivazione_layer_byte(Layer, byte *); +double *funzioni_attivazione_layer_double(Layer, double *); */ +/* void correggi_layer_interni(ReteNeurale *, double **, double **); +void correggi_layer_input(Layer *, double **, double **, byte *, int); */ +/* // Questa funzione calcola tutte le funzioni di attivazione dei percettroni del layer che prende dei byte come inputs +double *funzioni_attivazione_layer_byte(Layer layer, byte *inputs) +{ + + double *funzioni = (double *)malloc(sizeof(double) * layer.size); + + for (int i = 0; i < layer.size; i++) + { + funzioni[i] = sigmoide_byte(layer.percettroni[i], inputs, layer.percettroni[i].size); + // printf("\tsigmoide layer input %f\n", funzioni[i]); + } + + return funzioni; +} + +// Questa funzione calcola tutte le funzioni di attivazione dei percettroni del layer che prende dei double come inputs (le sigmoidi del livello precedente) +double *funzioni_attivazione_layer_double(Layer layer, double *inputs) +{ + + double *funzioni = (double *)malloc(sizeof(double) * layer.size); + + for (int i = 0; i < layer.size; i++) + { + funzioni[i] = sigmoide_double(layer.percettroni[i], inputs, layer.percettroni[i].size); + // printf("\tsigmoide layer %d: %f\n", i, funzioni[i]); + } + + return funzioni; +} + */ +/* // Questa funzione prende la matrice dei gradienti e la matrice delle sigmoidi per correggere tutti i layer tranne quello di ingresso +void correggi_layer_interni(ReteNeurale *rete, double **gradienti, double **sigmoidi) +{ + + for (int indice_layer = rete->size - 1; indice_layer > 0; indice_layer--) + { + for (int indice_percettrone = 0; indice_percettrone < rete->layers[indice_layer].size; indice_percettrone++) + { // Numero percettroni + + for (int indice_peso = 0; indice_peso < rete->layers[indice_layer].percettroni[indice_percettrone].size; indice_peso++) + { // Numero pesi + gradienti[indice_layer][indice_percettrone] = gradienti[rete->size - 1][0] * (sigmoidi[indice_layer][indice_percettrone] * (1 - sigmoidi[indice_layer][indice_percettrone])); + rete->layers[indice_layer].percettroni[indice_percettrone].pesi[indice_peso] += (gradienti[indice_layer][indice_percettrone] * LRE * sigmoidi[indice_layer - 1][indice_percettrone]); + // rete->layers[indice_layer].percettroni[indice_percettrone].pesi[indice_peso] += (gradienti[rete->size-1][0] * LRE * sigmoidi[indice_layer-1][indice_percettrone]); + } + rete->layers[indice_layer].percettroni[indice_percettrone].bias += (gradienti[indice_layer][indice_percettrone] * LRE); + // printf("bias: %f\n", rete->layers[indice_layer].percettroni[indice_percettrone].bias); + } + } +} +// Questa funzione prende tutti i parametri della precedente + gli input passati dal dataset per correggere il layer di ingresso +void correggi_layer_input(Layer *layer, double **gradienti, double **sigmoidi, byte *inputs, int n_layers) +{ + // L'indice del layer d'ingresso che prende byte per input + int indice_layer = 0; + for (int indice_percettrone = 0; indice_percettrone < layer->size; indice_percettrone++) + { // Numero percettroni + for (int indice_peso = 0; indice_peso < layer->percettroni->size; indice_peso++) + { // Numero pesi + + gradienti[indice_layer][indice_percettrone] = gradienti[n_layers - 1][0] * (sigmoidi[indice_layer][indice_percettrone] * (1 - sigmoidi[indice_layer][indice_percettrone])); + layer->percettroni[indice_percettrone].pesi[indice_peso] += (gradienti[indice_layer][indice_percettrone] * LRE * inputs[indice_peso]); + // layer->percettroni[indice_percettrone].pesi[indice_peso] += (gradienti[n_layers-1][0] * LRE * inputs[indice_peso]); + } + layer->percettroni[indice_percettrone].bias += (gradienti[n_layers - 1][0] * LRE); + } +} + */ +/* +// Questa funzione viene usata per il primo livello perchè ha un vettore di byte (unsigned char) in input +double sigmoide_byte(Percettrone p, byte *valori, int n_input) +{ + + double sommatoria = 0.0; + // printf("valori: [%d][%d]", valori[0], valori[1]); + // printf("pesi: [%f][%f]", p.pesi[0], p.pesi[1]); + + for (int i = 0; i < n_input; i++) + { + sommatoria += ((double)valori[i] * p.pesi[i]); + } + // printf("sommatoria= %f\n", sommatoria); + double funzione = sommatoria + p.bias; + double potenza_e = exp(-funzione); + // printf("potenza_e= %f\n", potenza_e); + // formula sigmoide + double risultato = 1.0 / (1.0 + potenza_e); + // printf("risultato= %f\n", risultato); + return risultato; +} + +// Questa funzione viene usata per gli altri livelli dove gli input sono double, ossia i valori della sigmoide dei livelli precedenti +double sigmoide_double(Percettrone p, double *valori, int n_input) +{ + double sommatoria = 0.0; + for (int i = 0; i < n_input; i++) + { + sommatoria += (valori[i] * p.pesi[i]); + } + + double funzione = sommatoria + p.bias; + // printf("sommatoria= %f\n", sommatoria); + double potenza_e = exp(-funzione); + // printf("potenza_e= %f\n", potenza_e); + // formula sigmoide + double risultato = 1.0 / (1.0 + potenza_e); + // printf("risultato= %f\n", risultato); + + return risultato; +} + */ \ No newline at end of file diff --git a/xor_manager.h b/xor_manager.h index f525694..b5dcc68 100644 --- a/xor_manager.h +++ b/xor_manager.h @@ -29,19 +29,19 @@ Dataset *crea_dataset_xor() { set->istanze[0].dati[0] = 0; set->istanze[0].dati[1] = 0; - set->istanze[0].classificazione = 1; + set->istanze[0].classificazione = 0; set->istanze[1].dati[0] = 0; set->istanze[1].dati[1] = 1; - set->istanze[1].classificazione = 0; + set->istanze[1].classificazione = 1; set->istanze[2].dati[0] = 1; set->istanze[2].dati[1] = 0; - set->istanze[2].classificazione = 0; + set->istanze[2].classificazione = 1; set->istanze[3].dati[0] = 1; set->istanze[3].dati[1] = 1; - set->istanze[3].classificazione = 1; + set->istanze[3].classificazione = 0; return set; } \ No newline at end of file