#include #include #include #include #include "cifar-10/cifar10_manager.h" // Costanti configurabili #define N_LAYERS 3 // Numero di layer (input, hidden, output) #define N_NEURONI_HIDDEN 128 // Numero di neuroni nei layer nascosti #define N_NEURONI_OUTPUT 10 // Numero di neuroni nel layer di output (10 classi) #define N_EPOCHE 10 // Numero di epoche di addestramento #define LEARNING_RATE 0.01 // Tasso di apprendimento #define N_INPUTS 3072 // Dimensioni di un'immagine CIFAR-10 (32x32x3) // Strutture dati /* typedef unsigned char byte; typedef struct { byte classificazione; byte dati[N_INPUTS]; } Istanza; typedef struct { int size; Istanza *istanze; } Dataset; */ typedef struct { double *pesi; double bias; int size; } Percettrone; typedef struct { Percettrone *percettroni; int size; } Layer; typedef struct { Layer *layers; int size; } ReteNeurale; double sigmoide(double); double relu(double); double relu_derivata(double); double softmax(double*, int, int); ReteNeurale crea_rete(); double *prevedi(ReteNeurale*, byte*); void allena(ReteNeurale*, Dataset*); void salva_rete(ReteNeurale*, const char*); ReteNeurale carica_rete(const char*); void carica_immagini_casuali(Dataset*, byte[][N_INPUTS], byte*); void fai_previsioni(ReteNeurale*, byte[][N_INPUTS], byte*); void backpropagation(ReteNeurale*, byte*, byte, double*); double *forward_pass(ReteNeurale*, byte*); // Funzioni di utilità double sigmoide(double x) { return 1.0 / (1.0 + exp(-x)); } double relu(double x) { return x > 0 ? x : 0; } double relu_derivata(double x) { return x > 0 ? 1 : 0; } double softmax(double *x, int index, int size) { double max = x[0]; for (int i = 1; i < size; i++) { if (x[i] > max) max = x[i]; } double sum = 0.0; for (int i = 0; i < size; i++) { sum += exp(x[i] - max); } return exp(x[index] - max) / sum; } // Inizializzazione della rete ReteNeurale crea_rete() { ReteNeurale rete; rete.size = N_LAYERS; rete.layers = (Layer *)malloc(N_LAYERS * sizeof(Layer)); // Layer di input (non ha pesi, solo pass-through) rete.layers[0].size = N_INPUTS; rete.layers[0].percettroni = NULL; // Layer nascosto rete.layers[1].size = N_NEURONI_HIDDEN; rete.layers[1].percettroni = (Percettrone *)malloc(N_NEURONI_HIDDEN * sizeof(Percettrone)); for (int i = 0; i < N_NEURONI_HIDDEN; i++) { rete.layers[1].percettroni[i].size = N_INPUTS; rete.layers[1].percettroni[i].pesi = (double *)malloc(N_INPUTS * sizeof(double)); for (int j = 0; j < N_INPUTS; j++) { rete.layers[1].percettroni[i].pesi[j] = ((double)rand() / RAND_MAX) * 2 - 1; // Pesi casuali tra -1 e 1 } rete.layers[1].percettroni[i].bias = ((double)rand() / RAND_MAX) * 2 - 1; // Bias casuale tra -1 e 1 } // Layer di output rete.layers[2].size = N_NEURONI_OUTPUT; rete.layers[2].percettroni = (Percettrone *)malloc(N_NEURONI_OUTPUT * sizeof(Percettrone)); for (int i = 0; i < N_NEURONI_OUTPUT; i++) { rete.layers[2].percettroni[i].size = N_NEURONI_HIDDEN; rete.layers[2].percettroni[i].pesi = (double *)malloc(N_NEURONI_HIDDEN * sizeof(double)); for (int j = 0; j < N_NEURONI_HIDDEN; j++) { rete.layers[2].percettroni[i].pesi[j] = ((double)rand() / RAND_MAX) * 2 - 1; // Pesi casuali tra -1 e 1 } rete.layers[2].percettroni[i].bias = ((double)rand() / RAND_MAX) * 2 - 1; // Bias casuale tra -1 e 1 } return rete; } // Funzione per la previsione double *prevedi(ReteNeurale *rete, byte *input) { double *output = (double *)malloc(N_NEURONI_OUTPUT * sizeof(double)); // Implementazione del forward pass // (Da completare) return output; } // Funzione per l'addestramento void allena(ReteNeurale *rete, Dataset *dataset) { printf("Sono dentro l'allenamento\n"); for (int epoca = 0; epoca < N_EPOCHE; epoca++) { printf("Epoca %d\n", epoca); for (int i = 0; i < dataset->size; i++) { byte *input = dataset->istanze[i].dati; byte target = dataset->istanze[i].classificazione; // Forward pass double *output_final = forward_pass(rete, input); // Backpropagation backpropagation(rete, input, target, output_final); free(output_final); } } } // Forward pass double *forward_pass(ReteNeurale *rete, byte *input) { double *output_hidden = (double *)malloc(N_NEURONI_HIDDEN * sizeof(double)); double *output_final = (double *)malloc(N_NEURONI_OUTPUT * sizeof(double)); // Layer nascosto for (int i = 0; i < N_NEURONI_HIDDEN; i++) { double somma = 0.0; for (int j = 0; j < N_INPUTS; j++) { somma += input[j] * rete->layers[1].percettroni[i].pesi[j]; } somma += rete->layers[1].percettroni[i].bias; output_hidden[i] = relu(somma); // Applica ReLU } // Layer di output for (int i = 0; i < N_NEURONI_OUTPUT; i++) { double somma = 0.0; for (int j = 0; j < N_NEURONI_HIDDEN; j++) { somma += output_hidden[j] * rete->layers[2].percettroni[i].pesi[j]; } somma += rete->layers[2].percettroni[i].bias; output_final[i] = somma; // Non applica softmax qui, verrà fatto durante la loss } free(output_hidden); return output_final; } // Backpropagation void backpropagation(ReteNeurale *rete, byte *input, byte target, double *output_final) { // Calcola la softmax e la loss (cross-entropy) double softmax_output[N_NEURONI_OUTPUT]; double sum_exp = 0.0; for (int i = 0; i < N_NEURONI_OUTPUT; i++) { sum_exp += exp(output_final[i]); } for (int i = 0; i < N_NEURONI_OUTPUT; i++) { softmax_output[i] = exp(output_final[i]) / sum_exp; } // Gradiente della loss rispetto all'output double grad_output[N_NEURONI_OUTPUT]; for (int i = 0; i < N_NEURONI_OUTPUT; i++) { grad_output[i] = softmax_output[i] - (i == target ? 1 : 0); } // Gradiente rispetto ai pesi e bias del layer di output for (int i = 0; i < N_NEURONI_OUTPUT; i++) { for (int j = 0; j < N_NEURONI_HIDDEN; j++) { rete->layers[2].percettroni[i].pesi[j] -= LEARNING_RATE * grad_output[i] * output_final[j]; } rete->layers[2].percettroni[i].bias -= LEARNING_RATE * grad_output[i]; } // Gradiente rispetto ai pesi e bias del layer nascosto double grad_hidden[N_NEURONI_HIDDEN]; for (int j = 0; j < N_NEURONI_HIDDEN; j++) { grad_hidden[j] = 0.0; for (int i = 0; i < N_NEURONI_OUTPUT; i++) { grad_hidden[j] += grad_output[i] * rete->layers[2].percettroni[i].pesi[j]; } grad_hidden[j] *= relu_derivata(output_final[j]); // Derivata di ReLU } for (int j = 0; j < N_NEURONI_HIDDEN; j++) { for (int k = 0; k < N_INPUTS; k++) { rete->layers[1].percettroni[j].pesi[k] -= LEARNING_RATE * grad_hidden[j] * input[k]; } rete->layers[1].percettroni[j].bias -= LEARNING_RATE * grad_hidden[j]; } } // Serializzazione della rete void salva_rete(ReteNeurale *rete, const char *filename) { FILE *file = fopen(filename, "wb"); if (file == NULL) { perror("Errore nell'apertura del file"); return; } fwrite(rete, sizeof(ReteNeurale), 1, file); fclose(file); } // Deserializzazione della rete ReteNeurale carica_rete(const char *filename) { FILE *file = fopen(filename, "rb"); if (file == NULL) { perror("Errore nell'apertura del file"); exit(1); } ReteNeurale rete; fread(&rete, sizeof(ReteNeurale), 1, file); fclose(file); return rete; } // Funzione per caricare 4 immagini casuali dal dataset void carica_immagini_casuali(Dataset *dataset, byte immagini[4][N_INPUTS], byte *etichette) { for (int i = 0; i < 4; i++) { int indice_casuale = rand() % dataset->size; // Sceglie un'immagine casuale for (int j = 0; j < N_INPUTS; j++) { immagini[i][j] = dataset->istanze[indice_casuale].dati[j]; // Copia i dati dell'immagine } etichette[i] = dataset->istanze[indice_casuale].classificazione; // Copia l'etichetta } } // Funzione per fare previsioni su 4 immagini void fai_previsioni(ReteNeurale *rete, byte immagini[4][N_INPUTS], byte *etichette) { for (int i = 0; i < 4; i++) { double *output = prevedi(rete, immagini[i]); // Passa l'immagine attraverso la rete int previsione = 0; double max_prob = output[0]; for (int j = 1; j < N_NEURONI_OUTPUT; j++) { if (output[j] > max_prob) { max_prob = output[j]; previsione = j; } } printf("Immagine %d: Etichetta vera = %d, Previsione = %d\n", i + 1, etichette[i], previsione); free(output); // Libera la memoria allocata per l'output } } int main() { srand(time(NULL)); printf("Mi appresto a caricare la rete\n"); // Creazione della rete ReteNeurale rete = crea_rete(); printf("Mi appresto a caricare il dataset\n"); // Caricamento del dataset (da implementare) Dataset *dataset; dataset = get_dataset("cifar-10/data_batch_1.bin"); printf("Mi appresto ad allenare\n"); // Addestramento della rete allena(&rete, dataset); // Salvataggio della rete salva_rete(&rete, "rete_neurale.bin"); // Caricamento di 4 immagini casuali byte immagini[4][N_INPUTS]; byte etichette[4]; carica_immagini_casuali(dataset, immagini, etichette); // Previsioni sulle immagini fai_previsioni(&rete, immagini, etichette); // Liberazione della memoria // (Da implementare) return 0; }