Come scrivere una piccola rete neurale da zero

Come scrivere una piccola rete neurale da zero

Come scrivere una piccola rete neurale da zero

Hai sentito parlare di reti neurali mille volte, magari le hai usate indirettamente attraverso qualche API, ma non hai mai capito davvero cosa succede sotto il cofano. Questo articolo è per te. Partiremo da zero, niente framework magici, niente scatole nere, e costruiremo insieme una piccola rete neurale funzionante in Python puro con NumPy. Alla fine saprai cosa sono pesi, bias, funzione di attivazione e backpropagation, e avrai il codice per dimostrarlo.

Indice dei contenuti – Come scrivere una piccola rete neurale da zero

  1. Il neurone artificiale: un interruttore intelligente
  2. Perché Python (e non altro)
  3. L’architettura: strati, pesi e bias
  4. Esempio pratico: costruire il forward pass
  5. Come la rete impara: loss function e backpropagation
  6. Esempio pratico: addestrare la rete sul problema XOR
  7. Il passo successivo: PyTorch e TensorFlow
  8. Conclusioni e formazione professionale

1. Il neurone artificiale: un interruttore intelligente

Immagina un vigile urbano che dirige il traffico. Ogni mattina osserva tre cose: quante auto ci sono sulla strada principale, quante sul viale laterale, e se è ora di punta. Sulla base di questi segnali, e della sua esperienza, decide se alzare o abbassare la paletta.

Un neurone artificiale funziona esattamente così.

Riceve in ingresso una serie di valori numerici (i “segnali”), ognuno moltiplicato per un peso che ne determina l’importanza. Somma tutto, aggiunge un piccolo valore chiamato bias (una soglia di attivazione), e poi decide se “accendersi” o no attraverso una funzione di attivazione.

In formula — e non spaventarti, è più semplice di quanto sembri:

output = activation( (x1 * w1) + (x2 * w2) + ... + bias )

Il valore x è l’input, w è il peso, bias è lo spostamento, e activation è la funzione che decide quanto “forte” rispondere. La più usata per iniziare è la sigmoid, che schiaccia qualsiasi numero in un valore tra 0 e 1 — come una percentuale di certezza.

Una rete neurale non è altro che tanti neuroni di questo tipo, organizzati in strati e collegati tra loro.

2. Perché Python (e non altro)

Prima di sporcarsi le mani con il codice, vale la pena rispondere a una domanda che sembra banale ma non lo è: perché Python?

La risposta breve è: perché quasi tutto il mondo dell’intelligenza artificiale parla Python. Librerie come NumPy, Pandas e Matplotlib forniscono strumenti efficienti per la manipolazione dei dati, mentre framework come TensorFlow e PyTorch offrono API intuitive costruite sopra Python stesso. La sintassi chiara di Python accelera la prototipazione e il debug, qualità fondamentale quando stai ancora capendo come funziona un algoritmo.

Ma c’è di più: Python è il linguaggio che ti dà accesso diretto all’ecosistema. Vuoi caricare un modello pre-addestrato da HuggingFace? Python. Vuoi fare fine-tuning su dati aziendali? Python. Vuoi integrare un modello in una API REST? Python.

Per chi comincia, c’è un vantaggio pratico immediato: il codice assomiglia molto alla matematica descritta a parole. Questo lo rende ideale per capire cosa sta succedendo davvero, prima di delegare tutto a un framework.

# Installa l'unica dipendenza necessaria per questo articolo
# pip install numpy
import numpy as np

# La funzione sigmoid: schiaccia qualsiasi valore tra 0 e 1
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# Proviamola su un paio di valori
print(sigmoid(0))    # 0.5 — massima incertezza
print(sigmoid(5))    # ~0.99 — quasi sicuro "sì"
print(sigmoid(-5))   # ~0.007 — quasi sicuro "no"

NumPy qui non è un capriccio: la sua implementazione della funzione esponenziale è scritta in C ed è enormemente più veloce di un ciclo Python puro — un dettaglio che conta quando stai lavorando su migliaia di neuroni.

3. L’architettura: strati, pesi e bias

Una rete neurale completa è fatta di tre tipi di strati:

  • Input layer: riceve i dati grezzi (i tuoi input).
  • Hidden layer(s): uno o più strati intermedi dove avviene la “magia” — la trasformazione dei dati.
  • Output layer: produce il risultato finale (una previsione, una classificazione, un numero).

I neuroni di strati adiacenti sono collegati da pesi: valori numerici che la rete impara ad aggiustare durante l’addestramento. All’inizio vengono inizializzati con valori casuali piccoli, e poi corretti progressivamente.

Ogni connessione ha il suo peso. In una rete con 2 input e 3 neuroni nascosti, ci sono già 2×3 = 6 pesi solo nel primo strato. In reti reali si parla di milioni. Per questo si usano le matrici: NumPy ci permette di fare queste moltiplicazioni in una sola riga di codice, invece di mille cicli annidati.

4. Esempio pratico: costruire il forward pass

Passiamo alla pratica su Come scrivere una piccola rete neurale da zero. Il forward pass è il viaggio dei dati attraverso la rete: dall’input all’output. È il momento in cui la rete fa una previsione, ancora sbagliata, all’inizio.

Costruiamo una piccola rete con:

  • 2 neuroni in input
  • 3 neuroni nascosti
  • 1 neurone in output
import numpy as np

# Riproducibilità: stesso seme = stessi numeri casuali ogni volta
np.random.seed(42)

# Inizializzazione dei pesi con valori piccoli casuali
# Strato 1: da 2 input a 3 neuroni nascosti
W1 = np.random.randn(2, 3) * 0.01
b1 = np.zeros((1, 3))  # bias inizializzati a zero

# Strato 2: da 3 neuroni nascosti a 1 output
W2 = np.random.randn(3, 1) * 0.01
b2 = np.zeros((1, 1))

# Funzione di attivazione sigmoid
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# Forward pass: un esempio di input
X = np.array([[0, 1]])  # un campione con 2 feature

# Strato nascosto
Z1 = X @ W1 + b1        # moltiplicazione matriciale + bias
A1 = sigmoid(Z1)        # attivazione

# Strato di output
Z2 = A1 @ W2 + b2
A2 = sigmoid(Z2)        # previsione finale

print(f"Previsione: {A2[0][0]:.4f}")
# Con pesi casuali piccoli, il risultato sarà vicino a 0.5

Cosa sta succedendo? X @ W1 è la moltiplicazione tra la matrice degli input e la matrice dei pesi: in un’unica operazione calcoliamo l’attivazione di tutti e 3 i neuroni nascosti contemporaneamente. È esattamente quello che renderebbe lento Python puro e veloce NumPy.

5. Come la rete impara: loss function e backpropagation

Finora la rete fa una previsione, ma non sa se è giusta. Ci vuole un metro di misura: la loss function (funzione di perdita). Misura quanto la previsione è lontana dal valore corretto.

Per problemi di classificazione binaria (sì/no, 0/1) si usa la binary cross-entropy:

def binary_cross_entropy(y_pred, y_true):
    # Evita log(0) con un piccolo epsilon
    eps = 1e-8
    return -np.mean(
        y_true * np.log(y_pred + eps) + (1 - y_true) * np.log(1 - y_pred + eps)
    )

Più alta è la loss, più la rete sbaglia. L’obiettivo dell’addestramento è minimizzare questa funzione.

Per farlo si usa la backpropagation: la rete calcola quanto ogni peso ha “contribuito” all’errore, e li aggiusta di conseguenza usando la gradient descent (discesa del gradiente). Il parametro che controlla quanto grande è ogni correzione si chiama learning rate.

L’analogia classica funziona ancora: immagina di essere in montagna nella nebbia e di dover scendere a valle. Non vedi il panorama, ma puoi sentire la pendenza del terreno sotto i piedi. Ogni passo lo fai nella direzione che scende. Il learning rate è la lunghezza del tuo passo: troppo lungo e salti oltre la valle, troppo corto e impieghi un’eternità.

6. Esempio pratico: addestrare la rete sul problema XOR

Il problema XOR è un classico: date due variabili binarie, l’output è 1 solo se i valori sono diversi (0,1 → 1; 1,0 → 1; 0,0 → 0; 1,1 → 0). Un singolo neurone non è in grado di risolverlo — ci vuole almeno uno strato nascosto. Perfetto per testare la nostra rete.

import numpy as np

np.random.seed(42)

# Dataset XOR
X = np.array([[0,0], [0,1], [1,0], [1,1]])
y = np.array([[0], [1], [1], [0]])

# Inizializzazione pesi
W1 = np.random.randn(2, 4) * 0.5
b1 = np.zeros((1, 4))
W2 = np.random.randn(4, 1) * 0.5
b2 = np.zeros((1, 1))

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def sigmoid_deriv(x):
    s = sigmoid(x)
    return s * (1 - s)  # derivata della sigmoid

lr = 0.5      # learning rate
epochs = 5000 # numero di cicli di addestramento

for epoch in range(epochs):
    # --- Forward pass ---
    Z1 = X @ W1 + b1
    A1 = sigmoid(Z1)
    Z2 = A1 @ W2 + b2
    A2 = sigmoid(Z2)

    # --- Calcolo errore ---
    loss = np.mean((A2 - y) ** 2)  # MSE semplice

    # --- Backpropagation ---
    dA2 = 2 * (A2 - y) / y.shape[0]
    dZ2 = dA2 * sigmoid_deriv(Z2)
    dW2 = A1.T @ dZ2
    db2 = np.sum(dZ2, axis=0, keepdims=True)

    dA1 = dZ2 @ W2.T
    dZ1 = dA1 * sigmoid_deriv(Z1)
    dW1 = X.T @ dZ1
    db1 = np.sum(dZ1, axis=0, keepdims=True)

    # --- Aggiornamento pesi ---
    W2 -= lr * dW2
    b2 -= lr * db2
    W1 -= lr * dW1
    b1 -= lr * db1

    if epoch % 1000 == 0:
        print(f"Epoch {epoch:5d} | Loss: {loss:.4f}")

# Risultato finale
print("\nPrevisioni finali:")
print(np.round(sigmoid(sigmoid(X @ W1 + b1) @ W2 + b2)))
# Output atteso: [[0], [1], [1], [0]]

Eseguendo questo codice vedrai la loss scendere progressivamente fino a quasi zero, e le previsioni finali corrispondere esattamente ai valori attesi. Hai appena addestrato una rete neurale da zero.

7. Il passo successivo: PyTorch e TensorFlow

Costruire una rete da zero con NumPy è il modo migliore per capire cosa succede davvero. Ma nella pratica professionale nessuno lo fa: si usano framework dedicati che gestiscono automaticamente la backpropagation, l’ottimizzazione e il deployment su GPU.

I due dominatori del settore nel 2025 sono PyTorch e TensorFlow.

PyTorch — sviluppato da Meta — è diventato lo standard nella ricerca e nella prototipazione rapida. Usa grafi computazionali dinamici: il codice si esegue riga per riga, esattamente come Python normale, rendendo il debug intuitivo. È molto “Pythonico” e ricorda NumPy nella sintassi. Non a caso domina nella comunità accademica.

TensorFlow — sviluppato da Google — è più solido per il deployment in produzione su larga scala. Offre strumenti nativi per il serving dei modelli, il supporto mobile e l’integrazione con l’ecosistema Google Cloud. Con TensorFlow 2.x e la modalità eager, le differenze con PyTorch si sono molto ridotte.

La stessa rete XOR, in PyTorch, diventa:

import torch
import torch.nn as nn

# Dataset XOR come tensori PyTorch
X = torch.tensor([[0,0],[0,1],[1,0],[1,1]], dtype=torch.float32)
y = torch.tensor([[0],[1],[1],[0]], dtype=torch.float32)

# Definizione del modello
model = nn.Sequential(
    nn.Linear(2, 4),   # strato nascosto: 2 input → 4 neuroni
    nn.Sigmoid(),
    nn.Linear(4, 1),   # strato output: 4 neuroni → 1 output
    nn.Sigmoid()
)

optimizer = torch.optim.SGD(model.parameters(), lr=0.5)
loss_fn = nn.MSELoss()

for epoch in range(5000):
    pred = model(X)
    loss = loss_fn(pred, y)
    optimizer.zero_grad()
    loss.backward()    # backpropagation automatica!
    optimizer.step()

print(torch.round(model(X)))

Nota come loss.backward() sostituisce tutte le righe di backpropagation scritte a mano prima. Il framework calcola automaticamente i gradienti per ogni parametro del modello. Meno codice, meno errori, stessa logica.

Se parti da zero oggi, PyTorch è il punto di partenza consigliato per la sua semplicità e per l’enorme quantità di tutorial e risorse disponibili.

8. Conclusioni e formazione professionale

Sei arrivato alla fine con una rete neurale funzionante e una comprensione reale di cosa significano termini come pesi, bias, attivazione, loss e gradient descent. Non sono più parole oscure, sono strumenti che hai usato con le tue mani.

Il percorso naturale da qui è: approfondire PyTorch o TensorFlow, esplorare architetture più complesse (CNN per le immagini, Transformer per il testo), e iniziare a lavorare su dataset reali.

Vuoi andare oltre in modo strutturato?

Nei percorsi formativi di Innovaformazione trovi corsi rivolti ad aziende, progettati per sviluppatori e ingegneri informatici che vogliono passare dalla teoria alla pratica su AI e Machine Learning, con esempi concreti, progetti reali e il supporto di un formatore esperto.

Scopri i corsi machine learning su Innovaformazione.net

Per informazioni e preventivo

  • info@innovaformazione.net
  • TEL. 3471012275 – Dario Carrassi

Per altri articoli tecnici di settore consigliamo di navigare sul nostro blog QUI.

(fonte) (fonte) (fonte)

Ti potrebbe interessare

Articoli correlati