Salta al contenuto principale

Modelli convoluzionali (CNN) in Pytorch per la classificazione di immagini

Profile picture for user luca77king

Una CNN, o Convolutional Neural Network, è un tipo speciale di rete neurale progettata per lavorare con immagini. Non è un semplice "perceptron con tanti neuroni": è una struttura pensata per rilevare forme, strutture e pattern visivi, come i bordi di un oggetto, il muso di un cane, le ali di un aereo.

La sua forza sta nella convoluzione, un’operazione matematica che applica dei filtri (kernel) su piccole porzioni dell’immagine, come se scorressi una lente d’ingrandimento su ogni parte. Ogni filtro è una matrice piccola (di solito 3x3 o 5x5) che analizza un dettaglio locale e produce una mappa chiamata feature map. È come dire: “Questa zona contiene un bordo verticale? Una curva? Una trama particolare?”

Ogni strato convoluzionale prende in input una rappresentazione dell’immagine (inizialmente i pixel stessi, poi le feature più complesse) e restituisce una nuova immagine trasformata, in cui le informazioni visive diventano sempre più astratte: prima linee, poi forme, poi oggetti.

Per evitare che l’immagine diventi troppo grande e ingestibile, si usano i pooling layer: riducono la dimensione dell’immagine mantenendo solo l’informazione più importante in ogni zona. Il più usato è il MaxPooling: prende il valore più alto in un piccolo blocco, conservando solo il segnale dominante.

Alla fine, dopo alcuni strati di convoluzione e pooling, la rete ha imparato una rappresentazione numerica molto ricca dell’immagine. A questo punto, questi numeri vengono dati a uno o più strati completamente connessi (dense layer), che ragionano in termini di classi: “con questi pattern, è più probabile che stia vedendo un cane o una macchina?”

Tutto il sistema viene addestrato con backpropagation, cioè confrontando le previsioni con le etichette vere e aggiornando i pesi dei filtri per minimizzare l’errore.

Quindi una CNN funziona bene sulle immagini perché:

  • guarda solo piccole porzioni alla volta (come farebbe il nostro occhio);

  • riutilizza gli stessi filtri su tutta l’immagine (efficienza e coerenza);

  • riesce a imparare gerarchie di pattern, da pixel a oggetti;

  • riduce progressivamente la complessità, mantenendo l’essenziale.

L'obiettivo di una rete neurale convoluzionale (CNN) è riconoscere cosa c'è dentro un'immagine. Vuoi sapere se stai guardando una barca o un palazzo? La CNN lo impara. Non guardando un’immagine come faresti tu, ma estraendo pattern numerici che corrispondono a forme, bordi, curve, texture.

Ogni immagine digitale è già un dato. È una matrice (o meglio, un tensore tridimensionale) di numeri che rappresentano l’intensità della luce in ogni pixel. Per un'immagine a colori, questi numeri sono divisi in tre canali: rosso, verde e blu. Quindi, un’immagine 32x32 RGB è un tensore di forma [3, 32, 32].

Cosa vogliamo classificare?

Prendiamo un dataset reale: CIFAR-10. È un insieme di immagini a colori in 10 categorie. Le categorie sono:

  • aereo

  • automobile

  • uccello

  • gatto

  • cervo

  • cane

  • rana

  • cavallo

  • nave

  • camion

Il nostro obiettivo è semplice: quando la rete riceve un’immagine, deve dire a quale classe appartiene. Non sa nulla a priori, non sa cosa sia una barca. Ma, vedendo migliaia di esempi etichettati, impara a riconoscerne le caratteristiche.

Come una CNN capisce le immagini

Nel cervello umano, riconoscere un oggetto significa attivare neuroni che rispondono a forme e configurazioni note. Le CNN fanno lo stesso, ma in modo artificiale: applicano filtri numerici all’immagine. Questi filtri scansionano l’immagine come delle finestrelle mobili, producendo nuove immagini dette mappe di attivazione. Ogni mappa rappresenta una caratteristica rilevata: un bordo verticale, una curva, una texture.

I filtri non li progetti tu: la rete li impara da sola durante l’addestramento. Partendo da un'immagine grezza, il primo strato della CNN estrarrà pattern basilari. Strato dopo strato, questi pattern diventano sempre più complessi, fino a rappresentare vere e proprie strutture (il muso di un cane, le eliche di una nave).

Una volta estratte queste caratteristiche, la rete le appiattisce in un vettore e le passa a uno o più strati completamente connessi, che funzionano da classificatori. Il risultato è un vettore di 10 valori, uno per classe, da cui viene scelta quella con la probabilità più alta.

Codice Completo: da Immagini a Predizione

Ecco l'intero flusso: carichiamo il dataset, definiamo la rete, la addestriamo, e la usiamo per classificare.

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# Preprocessing: convertiamo le immagini in tensori normalizzati
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

# Carichiamo CIFAR-10
trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset, batch_size=64, shuffle=False)

# Definizione della CNN
class CNN(nn.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.conv1 = nn.Conv2d(3, 32, 3, padding=1)
        self.conv2 = nn.Conv2d(32, 64, 3, padding=1)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(64 * 8 * 8, 256)
        self.fc2 = nn.Linear(256, 10)

    def forward(self, x):
        x = self.pool(F.relu(self.conv1(x)))  # output: [64, 32, 16, 16]
        x = self.pool(F.relu(self.conv2(x)))  # output: [64, 64, 8, 8]
        x = x.view(-1, 64 * 8 * 8)
        x = F.relu(self.fc1(x))
        x = self.fc2(x)
        return x

# Inizializziamo modello, loss e ottimizzatore
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = CNN().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# Addestramento
for epoch in range(10):
    model.train()
    total_loss = 0
    for inputs, labels in trainloader:
        inputs, labels = inputs.to(device), labels.to(device)
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Epoch {epoch+1}, Loss: {total_loss:.3f}")

# Valutazione
model.eval()
correct = 0
total = 0
with torch.no_grad():
    for inputs, labels in testloader:
        inputs, labels = inputs.to(device), labels.to(device)
        outputs = model(inputs)
        _, predicted = torch.max(outputs, 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

print(f"Accuratezza: {100 * correct / total:.2f}%")

Conclusione

Una CNN impara a riconoscere una barca o un palazzo non perché sa cos’è, ma perché ha visto abbastanza esempi da associare certi pattern (curve morbide, superfici lisce, colori del cielo) alla classe nave, e altri pattern (linee dritte, blocchi regolari, finestre) alla classe palazzo.

Insegni alla rete a vedere, ma la sua "vista" è numerica, statistica. Non guarda l'immagine, la analizza in profondità, scoprendo forme nascoste tra i pixel.

Assistente virtuale (BETA)