Ottocento foto. È tutto quello che ha un’azienda agricola che vuole riconoscere le foglie malate delle sue viti: ottocento immagini scattate col telefono, etichettate a mano da un agronomo. Per una rete neurale è pochissimo — abbastanza per imparare a memoria il dataset, non abbastanza per generalizzare. Raccoglierne altre diecimila costerebbe mesi. Eppure c’è un modo per moltiplicare quei dati senza scattare una sola foto in più: trasformarli.
Cambiare i pixel senza cambiare l’etichetta, e perché funziona, sta nel libro — con il codice in torchvision.
Leggi «Data augmentation: moltiplicare i dati senza raccoglierli» nel libro →
Una foglia fotografata da destra è la stessa foglia fotografata da sinistra. Un po’ più scura, perché è passata una nuvola, resta la stessa foglia. Ruotata di dieci gradi, ritagliata un po’ più stretta: sempre lei. La data augmentation prende ogni immagine del dataset e ne genera varianti plausibili, insegnando al modello una cosa fondamentale: quali differenze sono irrilevanti per la risposta. È come studiare per un esame rileggendo gli stessi concetti spiegati con parole diverse — il contenuto non cambia, ma si smette di memorizzare la forma e si inizia a capire la sostanza.
L’idea in una riga: invarianze insegnate coi dati
Formalmente, la data augmentation codifica le invarianze del problema. Se sappiamo che la classe di un’immagine non cambia sotto specchiatura orizzontale, possiamo dirlo al modello in due modi: costruire un’architettura matematicamente invariante (difficile), oppure mostrargli sia l’originale sia la versione specchiata con la stessa etichetta (facile). L’augmentation sceglie la via facile: è un modo di iniettare conoscenza di dominio nel training senza toccare l’architettura.
Il punto delicato è che le invarianze dipendono dal problema. Specchiare un gatto va benissimo; specchiare la foto di un cartello stradale con una scritta la trasforma in spazzatura. Capovolgere verticalmente una radiografia del torace produce un’immagine che nessun radiologo vedrà mai. Prima di applicare una trasformazione, la domanda da farsi è sempre: un esperto umano darebbe la stessa etichetta alla versione trasformata?
Il principio della data augmentation: da una sola immagine etichettata nascono molte varianti — specchiata, ruotata, ritagliata, scurita, disturbata — che condividono tutte la stessa etichetta.
Setup: le trasformazioni di base con torchvision
Passiamo al codice. Useremo PyTorch e torchvision, che include le trasformazioni più comuni già pronte. Installazione: pip install torch torchvision pillow. Il primo blocco definisce una pipeline di augmentation e la confronta con la pipeline «pulita» che useremo in validazione.
import torch
from torchvision import transforms
from PIL import Image
# Pipeline di TRAINING: trasformazioni casuali a ogni epoca
train_tf = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomRotation(degrees=15),
transforms.ColorJitter(brightness=0.3, contrast=0.3,
saturation=0.2),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
])
# Pipeline di VALIDAZIONE: deterministica, nessuna augmentation
val_tf = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
])
img = Image.open("foglia.jpg").convert("RGB")
print(train_tf(img).shape) # torch.Size([3, 224, 224])
Due cose da notare. Primo: l’augmentation si applica solo al training. In validazione e in produzione vogliamo una trasformazione deterministica, altrimenti misureremmo il modello su un bersaglio mobile. Secondo: le trasformazioni sono casuali e vengono ricalcolate a ogni accesso — la stessa immagine, vista in dieci epoche, produce dieci varianti diverse. Il dataset non viene duplicato su disco: si moltiplica al volo, a costo di memoria zero.
Visualizzare prima di addestrare
Errore classico: configurare l’augmentation alla cieca e scoprire dopo ore di training che le rotazioni tagliavano via metà del soggetto. Regola pratica: guardare sempre un campione di immagini aumentate prima di lanciare il training. Questo snippet genera una griglia di varianti della stessa immagine.
import matplotlib.pyplot as plt
# Solo le trasformazioni geometriche/colore, senza normalizzazione
preview_tf = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomRotation(degrees=15),
transforms.ColorJitter(brightness=0.3, contrast=0.3),
])
fig, axes = plt.subplots(2, 4, figsize=(12, 6))
for ax in axes.flat:
ax.imshow(preview_tf(img))
ax.axis("off")
fig.suptitle("8 varianti della stessa immagine")
plt.tight_layout()
plt.savefig("preview_augmentation.png", dpi=120)
Se nella griglia compaiono immagini che voi stessi faticate a classificare, l’augmentation è troppo aggressiva: state chiedendo al modello di essere invariante a trasformazioni che distruggono l’informazione. Meglio partire conservativi e alzare l’intensità solo se il modello continua a fare overfitting.
Misurare l’effetto: con e senza augmentation
L’augmentation non è un rito: è un intervento misurabile. Il modo onesto di valutarla è addestrare lo stesso modello due volte — con e senza — e confrontare le curve di validazione. Qui usiamo un piccolo dataset di esempio e un modello compatto per rendere l’esperimento eseguibile anche senza GPU.
from torchvision import datasets, models
from torch.utils.data import DataLoader
import torch.nn as nn
def make_loader(tf, train=True):
ds = datasets.CIFAR10(root="./data", train=train,
download=True, transform=tf)
return DataLoader(ds, batch_size=128, shuffle=train,
num_workers=2)
def train_one(tf_train, epochs=5):
device = "cuda" if torch.cuda.is_available() else "cpu"
model = models.resnet18(num_classes=10).to(device)
opt = torch.optim.AdamW(model.parameters(), lr=1e-3)
loss_fn = nn.CrossEntropyLoss()
tr, va = make_loader(tf_train), make_loader(val_tf, train=False)
for epoch in range(epochs):
model.train()
for x, y in tr:
x, y = x.to(device), y.to(device)
opt.zero_grad()
loss_fn(model(x), y).backward()
opt.step()
# accuracy di validazione
model.eval()
hits = total = 0
with torch.no_grad():
for x, y in va:
pred = model(x.to(device)).argmax(1).cpu()
hits += (pred == y).sum().item()
total += y.numel()
print(f"epoch {epoch+1}: val acc {hits/total:.3f}")
return hits / total
acc_plain = train_one(val_tf) # nessuna augmentation
acc_aug = train_one(train_tf) # con augmentation
print(f"senza: {acc_plain:.3f} — con: {acc_aug:.3f}")
Nota di trasparenza: su CIFAR-10 (immagini 32×32 che qui ridimensioniamo a 224) e con sole 5 epoche i numeri assoluti saranno modesti, e nelle prime epoche la versione senza augmentation può perfino sembrare migliore — impara più in fretta perché sta memorizzando. Il vantaggio dell’augmentation emerge sul lungo periodo, come divario tra accuracy di training e di validazione: è un investimento contro l’overfitting, non un acceleratore.
L’augmentation non aggiunge informazione al dataset: aggiunge scetticismo al modello, insegnandogli quali dettagli non meritano fiducia.
Oltre le basi: MixUp, CutMix, RandAugment
Le trasformazioni geometriche sono solo l’inizio. La ricerca ha prodotto tecniche più esotiche che funzionano sorprendentemente bene. MixUp fonde due immagini (e le loro etichette) in una media pesata: il modello impara che tra «70% gatto e 30% cane» e la certezza assoluta c’è un continuum. CutMix ritaglia un rettangolo da un’immagine e lo incolla su un’altra, mescolando le etichette in proporzione all’area. RandAugment elimina la fatica di scegliere: pesca trasformazioni a caso da un catalogo, con soli due iperparametri globali (quante e quanto intense).
from torchvision.transforms import RandAugment
train_tf_ra = transforms.Compose([
transforms.RandomResizedCrop(224, scale=(0.7, 1.0)),
transforms.RandomHorizontalFlip(),
RandAugment(num_ops=2, magnitude=9), # 2 op, intensità media
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]),
])
E fuori dalle immagini? Il principio si estende ovunque si sappiano definire trasformazioni che preservano l’etichetta: nel testo si sostituiscono sinonimi o si ritraduce da una lingua ponte; nell’audio si trasla il pitch o si mascherano bande dello spettrogramma; nelle serie temporali si applicano jitter e scaling. La grammatica cambia, la logica no.
Perché ti riguarda
La data augmentation è la tecnica con il miglior rapporto costo-beneficio quando i dati scarseggiano — cioè quasi sempre, fuori dalle big tech. Non richiede hardware in più, non richiede etichettatori, richiede solo di rispondere onestamente a una domanda: cosa può cambiare in un input senza che cambi la risposta giusta? Se sapete rispondere, avete appena moltiplicato il vostro dataset. E se non sapete rispondere, avete scoperto di non aver ancora capito davvero il vostro problema — il che, prima di addestrare qualsiasi modello, è la scoperta più utile di tutte.

I commenti sono riservati agli iscritti.
Accedi per commentare