PyTorch nu are o metodă fit() în nucleu: bucla de antrenare o scrii în Python. Tutorialul oficial o împarte în două funcții, una de antrenare și una de test, apelate la fiecare epocă. O epocă este o trecere completă prin setul de date; în interiorul ei iterezi loturile din DataLoader.
Pasul de antrenare are mereu aceeași ordine: datele pe dispozitivul modelului, forward (model(x)), pierderea, zero_grad(), backward(), step(). Dacă folosești un LR scheduler, îl apelezi după optimizer.step(); documentația optim avertizează că ordinea inversă sare peste prima valoare din program.
La validare comuți modelul cu model.eval() (Dropout oprit, BatchNorm cu statisticile acumulate) și oprești gradientul cu torch.no_grad() sau torch.inference_mode(). După validare revii cu model.train(). Oprirea timpurie (early stopping) și salvarea celui mai bun model nu sunt funcții PyTorch: le scrii în buclă, pe baza pierderii de validare.
Concepte cheie și caracteristici
| Concept / Parametru | Descriere practică |
|---|---|
model.train() |
Mod de antrenare: Dropout activ, BatchNorm actualizează statisticile. |
model.eval() |
Mod de evaluare: Dropout oprit, BatchNorm folosește statisticile acumulate. |
optimizator.zero_grad() / backward() / step() |
Pașii fiecărui lot, în această ordine (zero_grad poate sta și la începutul pasului). |
scheduler.step() |
După optimizer.step(): pe epocă sau pe lot, după tipul planificatorului. |
loss.item() |
Valoarea pierderii ca float; nu păstra tensorul cu graful în liste. |
Exemple practice și scenarii reale
Exemplul 1: Funcții separate de antrenare și validare
def antreneaza(loader, model, criteriu, optimizator, device):
model.train()
total, n = 0.0, 0
for x, y in loader:
x, y = x.to(device), y.to(device)
pierdere = criteriu(model(x), y)
optimizator.zero_grad()
pierdere.backward()
optimizator.step()
total += pierdere.item() * x.size(0)
n += x.size(0)
return total / n
def valideaza(loader, model, criteriu, device):
model.eval()
total, corecte, n = 0.0, 0, 0
with torch.no_grad():
for x, y in loader:
x, y = x.to(device), y.to(device)
logits = model(x)
total += criteriu(logits, y).item() * x.size(0)
corecte += (logits.argmax(dim=1) == y).sum().item()
n += x.size(0)
return total / n, corecte / n
Exemplul 2: Epoci cu salvarea celui mai bun model
cel_mai_bun = float("inf")
for epoca in range(20):
pierdere_train = antreneaza(train_loader, model, criteriu, optimizator, device)
pierdere_val, acuratete = valideaza(val_loader, model, criteriu, device)
print(f"epoca {epoca}: train {pierdere_train:.4f}, val {pierdere_val:.4f}, acc {acuratete:.3f}")
if pierdere_val < cel_mai_bun:
cel_mai_bun = pierdere_val
torch.save(model.state_dict(), "cel_mai_bun.pt")
Greșeli frecvente și capcane
- Să uiți
model.train()după validare. Modelul rămâne în modul de evaluare și Dropout nu mai lucrează la epocile următoare. - Să aduni pierderile ca tensori (
total += pierdere). Fiecare tensor ține referință la graf și memoria crește; foloseștepierdere.item(). - Să lași datele pe CPU când modelul e pe GPU. Fiecare lot se mută cu
.to(device)în buclă.