229 lines
7.6 KiB
C
229 lines
7.6 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
//#include "cifar-10/cifar10_manager.h"
|
|
#include "mnist/mnist_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 1 // Un solo neurone di output (binario)
|
|
#define N_EPOCHE 100 // Numero di epoche di addestramento
|
|
#define LEARNING_RATE 0.01 // Tasso di apprendimento
|
|
#define N_INPUTS 784 // Dimensioni di un'immagine CIFAR-10 (32x32x3)
|
|
|
|
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();
|
|
int 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 sigmoide_derivata(double x) {
|
|
return sigmoide(x) * (1 - sigmoide(x));
|
|
}
|
|
|
|
double relu(double x) {
|
|
return x > 0 ? x : 0;
|
|
}
|
|
|
|
double relu_derivata(double x) {
|
|
return x > 0 ? 1 : 0;
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
|
|
// Forward pass
|
|
double forward_pass(ReteNeurale *rete, byte *input) {
|
|
double *output_hidden = (double *)malloc(N_NEURONI_HIDDEN * sizeof(double));
|
|
double output_final;
|
|
|
|
// 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
|
|
double somma = 0.0;
|
|
for (int j = 0; j < N_NEURONI_HIDDEN; j++) {
|
|
somma += output_hidden[j] * rete->layers[2].percettroni[0].pesi[j];
|
|
}
|
|
somma += rete->layers[2].percettroni[0].bias;
|
|
output_final = sigmoide(somma); // Applica sigmoide
|
|
|
|
free(output_hidden);
|
|
return output_final;
|
|
}
|
|
|
|
// Backpropagation
|
|
void backpropagation(ReteNeurale *rete, byte *input, byte target, double output_final) {
|
|
// Gradiente della loss (binary cross-entropy)
|
|
double grad_output = output_final - target;
|
|
|
|
// Gradiente rispetto ai pesi e bias del layer di output
|
|
for (int j = 0; j < N_NEURONI_HIDDEN; j++) {
|
|
rete->layers[2].percettroni[0].pesi[j] -= LEARNING_RATE * grad_output * output_final * (1 - output_final) * input[j];
|
|
}
|
|
rete->layers[2].percettroni[0].bias -= LEARNING_RATE * grad_output * output_final * (1 - output_final);
|
|
|
|
// 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] = grad_output * output_final * (1 - output_final) * rete->layers[2].percettroni[0].pesi[j];
|
|
grad_hidden[j] *= relu_derivata(output_final); // 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];
|
|
}
|
|
}
|
|
|
|
// Addestramento
|
|
void allena(ReteNeurale *rete, Dataset *dataset) {
|
|
for (int epoca = 0; epoca < N_EPOCHE; epoca++) {
|
|
printf("Epoca %d\n", epoca+1);
|
|
for (int i = 0; i < dataset->size; i++) {
|
|
byte *input = dataset->istanze[i].dati;
|
|
byte target = (dataset->istanze[i].classificazione == 7) ? 1 : 0; // 1 per cavalli, 0 per il resto
|
|
|
|
// Forward pass
|
|
double output_final = forward_pass(rete, input);
|
|
|
|
// Backpropagation
|
|
backpropagation(rete, input, target, output_final);
|
|
}
|
|
printf("Epoca %d completata\n", epoca + 1);
|
|
}
|
|
}
|
|
|
|
// Funzione per fare previsioni
|
|
int prevedi(ReteNeurale *rete, byte *input) {
|
|
double output_final = forward_pass(rete, input);
|
|
return (output_final >= 0.5) ? 2 : 0; // 2 per cavalli, 0 per il resto
|
|
}
|
|
|
|
// 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++) {
|
|
int previsione = prevedi(rete, immagini[i]);
|
|
printf("Immagine %d: Etichetta vera = %d, Previsione = %d\n", i + 1, etichette[i], previsione);
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
srand(time(NULL));
|
|
|
|
// Creazione della rete
|
|
ReteNeurale rete = crea_rete();
|
|
|
|
// Caricamento del dataset (da implementare)
|
|
Dataset *dataset;
|
|
dataset = get_dataset("mnist/train-images.idx3-ubyte", "mnist/train-labels.idx1-ubyte");
|
|
// dataset = carica_dataset("cifar10.bin");
|
|
|
|
// 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;
|
|
} |