Files
classificatore_immagini/percettroni_documentation.md

9.9 KiB
Raw Permalink Blame History

Documentazione della Libreria percettroni.h

Questa libreria implementa una rete neurale feedforward completamente connessa (fully-connected) in linguaggio C puro, progettata per la classificazione di immagini sui dataset MNIST e CIFAR-10. L'implementazione segue i principi fondamentali del deep learning con backpropagation e discesa del gradiente stocastica.

Struttura dei Dati

Percettrone

Il percettrone è l'unità fondamentale di calcolo nella rete neurale, ispirato al modello biologico del neurone.

typedef struct {
    float *pesi;    // Vettore dei pesi sinaptici
    float bias;     // Bias (soglia di attivazione)
    int size;       // Numero di input (dimensione del vettore pesi)
} Percettrone;

Proprietà matematiche:

  • Ogni percettrone calcola una combinazione lineare degli input: z = Σ(w_i * x_i) + b
  • La funzione di attivazione trasforma questo valore in un output non lineare
  • I pesi rappresentano la "forza" delle connessioni sinaptiche
  • Il bias permette di spostare la soglia di attivazione

Layer

Un layer (strato) è un insieme di percettroni che operano in parallelo sugli stessi input.

typedef struct {
    Percettrone *percettroni;   // Array di percettroni
    int size;                   // Numero di percettroni nello strato
} Layer;

ReteNeurale

La rete neurale è composta da una sequenza di layer interconnessi.

typedef struct {
    Layer *layers;  // Array di layer
    int size;       // Numero totale di layer
} ReteNeurale;

Costanti Fondamentali

  • LRE = 0.01: Learning Rate (tasso di apprendimento) - controlla la dimensione dei passi nella discesa del gradiente
  • soglia_sigmoide = 0.5: Soglia per la classificazione binaria con funzione sigmoide
  • TOLLERANZA = 99.5: Percentuale di accuratezza richiesta per l'arresto anticipato dell'addestramento
  • FUNZIONE_ATTIVAZIONE = 1: Tipo di funzione di attivazione (0=sigmoide, 1=ReLU, 2=gradino)

Funzioni di Inizializzazione

inizializza_percettrone(int n_pesi)

Descrizione: Inizializza un singolo percettrone con pesi casuali secondo la strategia di inizializzazione He.

Implementazione matematica:

  • Utilizza il Teorema Centrale del Limite per generare pesi distribuiti normalmente
  • Deviazione standard: σ = √(2/n) dove n è il numero di input
  • Formula: peso = (Σ₁₂(rand()) - 6) × √(2/n)
  • Il bias viene inizializzato a 0

Proprietà: Questa inizializzazione previene il problema del vanishing/exploding gradient nelle reti profonde con ReLU.

inizializza_layer(int n_percettroni, int n_pesi)

Descrizione: Crea un layer completo con un numero specificato di percettroni, ognuno con lo stesso numero di pesi.

Architettura dinamica: La libreria supporta architetture con riduzione progressiva del numero di neuroni:

  • Layer intermedi: neuroni = neuroni_iniziali × (layer_rimanenti / layer_totali)
  • Questo crea una struttura a imbuto che estrae caratteristiche sempre più astratte

inizializza_rete_neurale(int numero_input, int numero_layers, int numero_percettroni_iniziali, int numero_percettroni_finali)

Descrizione: Costruisce l'intera rete neurale con architettura personalizzabile.

Caratteristiche:

  • Supporta reti profonde con qualsiasi numero di layer
  • Configurazione flessibile del numero di neuroni per layer
  • Calcolo automatico del numero totale di parametri (pesi + bias)

Funzioni di Attivazione e Previsione

attivazione(Percettrone p, float *valori)

Descrizione: Calcola l'output di un percettrone applicando la funzione di attivazione scelta.

Formule matematiche implementate:

  1. Sigmoide (FUNZIONE_ATTIVAZIONE = 0):

    σ(z) = 1 / (1 + e^(-z))
    
    • Output nell'intervallo (0, 1)
    • Derivata: σ'(z) = σ(z) × (1 - σ(z))
  2. ReLU (FUNZIONE_ATTIVAZIONE = 1):

    ReLU(z) = max(0, z)
    
    • Output nell'intervallo [0, ∞)
    • Derivata: ReLU'(z) = 1 se z > 0, altrimenti 0
  3. Gradino (FUNZIONE_ATTIVAZIONE = 2):

    step(z) = 1 se z > 0, altrimenti 0
    
    • Output binario {0, 1}
    • Derivata: sempre 0 (non utilizzabile per backpropagation)

derivata_attivazione(float valore)

Descrizione: Calcola la derivata della funzione di attivazione rispetto all'input.

Importanza nel backpropagation: La derivata è essenziale per calcolare i gradienti durante l'addestramento, permettendo alla rete di apprendere attraverso la regola della catena.

softmax(float *input, int size)

Descrizione: Applica la funzione softmax al layer di output per la classificazione multi-classe.

Formula matematica:

softmax(z_i) = e^(z_i) / Σⱼ(e^(z_j))

Proprietà:

  • Trasforma gli output in probabilità che sommano a 1
  • È differenziabile, permettendo l'uso con backpropagation
  • Implementazione numerica stabile con sottrazione del massimo per evitare overflow

previsione_softmax(float *livello_percettroni, int size)

Descrizione: Esegue la classificazione finale applicando softmax e restituendo l'indice della classe con probabilità massima.

Forward Propagation

elabora_attivazioni(ReteNeurale *rete, Istanza istanza)

Descrizione: Esegue il forward propagation attraverso l'intera rete, calcolando le attivazioni di tutti i neuroni.

Processo:

  1. Normalizza gli input pixel (0-255 → 0.0-1.0)
  2. Propaga sequenzialmente attraverso ogni layer
  3. Restituisce una matrice 2D con tutte le attivazioni intermedie

Importanza: Le attivazioni intermedie sono necessarie per il calcolo dei gradienti durante il backpropagation.

Backpropagation e Discesa del Gradiente

elabora_gradienti(ReteNeurale *rete_neurale, byte output, float **attivazioni)

Descrizione: Calcola i gradienti dell'errore rispetto ai pesi utilizzando l'algoritmo di backpropagation.

Algoritmo matematico:

  • Layer di output: δ_output = y_true - y_pred (per perdita cross-entropy con softmax)
  • Layer nascosti: δ_hidden = (W^T × δ_next) ⊙ f'(z)
    • Dove è il prodotto elemento-per-elemento (Hadamard product)
    • f'(z) è la derivata della funzione di attivazione

discesa_gradiente(ReteNeurale *rete, float **attivazioni, float **gradienti)

Descrizione: Propaga i gradienti all'indietro attraverso i layer nascosti.

Regola della catena: Applica ricorsivamente la regola della catena per calcolare i gradienti nei layer precedenti.

calcola_gradiente_disceso(ReteNeurale *rete, int livello, int indice_peso, float **gradienti)

Descrizione: Calcola il contributo aggregato di un peso specifico al gradiente del layer successivo.

Formula: gradiente_disceso = Σ(δ_j × w_ji) dove j indica i neuroni del layer successivo.

Aggiornamento dei Pesi

aggiorna_pesi(ReteNeurale *rete_neurale, float **attivazioni, float **gradienti, Istanza istanza)

Descrizione: Aggiorna tutti i pesi e bias della rete utilizzando la discesa del gradiente stocastica.

Regola di aggiornamento:

w_new = w_old + η × δ × input
b_new = b_old + η × δ

Dove η è il learning rate (LRE) e δ è il gradiente dell'errore.

correggi_pesi_percettrone_float() e correggi_pesi_percettrone_byte()

Descrizione: Funzioni specializzate per l'aggiornamento dei pesi nel primo layer (che riceve input grezzi come byte) e nei layer successivi (che ricevono attivazioni normalizzate come float).

Addestramento

addestra(ReteNeurale *rete_neurale, Dataset set)

Descrizione: Esegue un'epoca completa di addestramento sul dataset fornito.

Processo completo:

  1. Per ogni istanza nel dataset:
    • Forward propagation per ottenere le predizioni
    • Calcolo dell'accuratezza corrente
    • Backpropagation per calcolare i gradienti
    • Aggiornamento dei pesi
  2. Calcolo della percentuale di risposte corrette
  3. Arresto anticipato se l'accuratezza supera la TOLLERANZA

Caratteristiche:

  • Implementa la discesa del gradiente stocastica (SGD)
  • Supporta l'arresto anticipato per prevenire overfitting
  • Fornisce feedback sull'accuratezza durante l'addestramento

Persistenza

salvaReteNeurale(const char *filename, ReteNeurale *rete)

Descrizione: Salva l'intera rete su file binario in formato proprietario.

Formato: Struttura annidata che preserva esattamente la topologia e i valori dei pesi.

caricaReteNeurale(const char *filename)

Descrizione: Carica una rete precedentemente salvata da file.

Utilizzo: Permette di riprendere l'addestramento o di utilizzare modelli pre-addestrati.

Debugging e Analisi

stampa_pesi_rete(ReteNeurale *rete)

Descrizione: Stampa tutti i pesi e bias della rete in formato leggibile.

Utilizzo: Essenziale per il debugging e l'analisi del comportamento della rete durante l'addestramento.

Proprietà Matematiche della Rete

Architettura Feedforward

  • Informazione che fluisce solo in avanti (input → output)
  • Nessuna connessione ricorrente o laterale
  • Computazione deterministica e parallela all'interno di ogni layer

Universal Approximation Theorem

Questa implementazione, essendo una rete feedforward con almeno uno strato nascosto e funzioni di attivazione non lineari, può approssimare qualsiasi funzione continua con precisione arbitraria, dato un numero sufficiente di neuroni.

Ottimizzazione

  • Funzione di perdita: Implicitamente cross-entropy (grazie all'uso di softmax e backpropagation)
  • Algoritmo di ottimizzazione: Discesa del gradiente stocastica
  • Regularizzazione: Nessuna esplicita, ma l'inizializzazione He e l'arresto anticipato aiutano a prevenire overfitting

Complessità Computazionale

  • Forward propagation: O(N×M) dove N è il numero totale di neuroni e M il numero di connessioni
  • Backpropagation: O(N×M) - stessa complessità del forward pass
  • Memoria: O(N×M) per memorizzare pesi, gradienti e attivazioni intermedie

Questa libreria rappresenta un'implementazione educativa completa di una rete neurale moderna, dimostrando i principi fondamentali del deep learning in modo trasparente e accessibile.