track
De câte ori ai văzut o valoare a pierderii NaN în timp ce antrenezi o rețea neuronală adâncă?
După ore întregi de antrenament, curba pierderii arată sănătos, apoi brusc țâșnește spre infinit. De obicei, motivul este explozia gradientului – valori ale gradientului care cresc atât de mult în timpul backpropagation încât actualizările parametrilor devin instabile și modelul cedează. Problema afectează cel mai tare rețelele recurente, dar apare și la transformere și rețele feedforward adânci.
Decuparea gradientului rezolvă asta limitând mărimea gradientelor înainte să ajungă la optimizator. E o singură linie în bucla ta de antrenare care menține actualizările limitate fără să schimbi modelul.
În acest articol, voi acoperi intuiția din spatele decupării gradientului, cele două metode principale, cum alegi un prag și cum o implementezi în PyTorch și TensorFlow.
Dar ce este, de fapt, pierderea în data science? Citește articolul nostru despre funcția de pierdere în machine learning ca să afli.
Ce este decuparea gradientului?
Decuparea gradientului este o tehnică ce limitează amplitudinea gradientelor în timpul antrenării pentru a preveni actualizări instabile ale parametrilor.
Când un gradient devine prea mare, optimizatorul face un pas uriaș în spațiul parametrilor și împinge ponderile într-o regiune în care pierderea explodează. Decuparea te ajută plafonând dimensiunea pasului înainte să provoace daune.
E important de notat că decuparea gradientului nu afectează arhitectura modelului. Nu adaugi straturi și nu schimbi funcțiile de activare. Doar modifică procesul de antrenare interceptând gradienții între backpropagation și pasul optimizatorului.
Asta o face ieftină de încercat și ușor de scos. După cum vei vedea mai jos, îți ia o singură linie de cod.
Cum funcționează decuparea gradientului
Mecanismul e simplu. Operația de decupare este plasată între trecerea înapoi și pasul optimizatorului și urmează aceiași patru pași la fiecare iterație.
- Calculează gradienții: Rulează trecerea înainte, calculează pierderea și rulează backpropagation. Aici nu se schimbă nimic, gradienții circulă prin rețea ca de obicei.
- Verifică magnitudinea gradientului: Măsoară cât de mari sunt gradienții. În funcție de metodă, asta înseamnă să te uiți la valori individuale sau să calculezi norma totală peste toți parametrii.
- Redu gradienții dacă depășesc un prag: Dacă magnitudinea trece peste limita setată, scalează gradienții în jos. Dacă nu, lasă-i neschimbați.
- Actualizează parametrii modelului: Trimite gradienții decupați către optimizator și aplică actualizarea ponderilor.
De cele mai multe ori, gradienții tăi rămân sub limită și antrenarea decurge ca și cum n-ar exista decupare. Când apare un vârf, decuparea îl prinde înainte ca optimizatorul să reacționeze.
Atât.
Metode comune de decupare a gradientului
Există două moduri de a decupa gradienții, iar diferența ține de ce măsori și ce scalezi.
Decupare după valoare
Decuparea după valoare limitează individual fiecare element al gradientului.
Alegi un interval, să zicem [-1.0, 1.0], iar orice valoare a gradientului din afara intervalului este rotunjită la limita cea mai apropiată. Un gradient de 2.5 devine 1.0. Un gradient de -2.5 devine -1.0. Valorile deja în interval rămân neschimbate.

Exemplu de decupare după valoare
Atractivitatea constă în simplitate. Nu e nicio matematică dincolo de o operație min/max și rulează rapid.
Dar abordarea are un dezavantaj. Decuparea valorilor individuale schimbă direcția vectorului gradient. Dacă o componentă e decupată și altele nu, vectorul actualizat nu mai indică acolo unde backpropagation a „spus” că ar trebui. Optimizatorul ajunge să facă un pas într-o direcție ușor greșită.
De aceea decuparea după valoare e mai rar folosită în practică.
Decupare după normă
Decuparea după normă scalează întregul vector gradient atunci când magnitudinea totală depășește un prag.
În loc să te uiți la valori individuale, calculează norma tuturor gradientelor la un loc (de obicei norma L2) și o compară cu o valoare maximă. Dacă norma e sub prag, nu se întâmplă nimic. Dacă e deasupra, fiecare gradient este înmulțit cu același factor de scalare pentru a readuce norma la limită.

Exemplu de decupare după normă
Avantajul este păstrarea direcției. Cum fiecare componentă se micșorează cu același factor, vectorul gradient continuă să indice în direcția originală. Doar scurtezi pasul, nu îl redirecționezi.
De aceea decuparea după normă a devenit standard. clip_grad_norm_ din PyTorch și clipnorm din TensorFlow implementează această metodă, iar majoritatea pipeline-urilor moderne o folosesc implicit.
Explozii de gradient vs dispariții de gradient
Exploziile și disparițiile de gradient sunt probleme comune în deep learning, dar doar una este rezolvată de decupare.
Explozii de gradient
Exploziile de gradient apar când valorile gradientului cresc prea mult în timpul backpropagation.
Asta apare de obicei în rețele adânci sau arhitecturi recurente, unde gradienții sunt înmulțiți peste multe straturi sau pași de timp. Dacă aceste înmulțiri se compun în direcția greșită, magnitudinea gradientului explodează. Optimizatorul face apoi o actualizare uriașă, ponderile sar la valori extreme, iar pierderea devine adesea NaN sau Inf.
O vei vedea ca vârfuri bruște ale pierderii sau un model care deviază din senin.
Dispariții de gradient
Disparițiile de gradient sunt problema opusă. Valorile gradientului se micșorează spre zero pe măsură ce se propagă înapoi prin rețea.
Când gradienții devin prea mici, actualizările de greutate devin infime. Straturile timpurii nu mai învață, straturile adânci învață încet, iar antrenarea aproape se oprește. Curba pierderii se aplatizează și nu mai îmbunătățește, chiar și după multe epoci.
Asta a fost principalul motiv pentru care RNN-urile aveau dificultăți cu secvențe lungi înainte de LSTM și GRU.
Unde se potrivește decuparea
Decuparea gradientului abordează exploziile de gradient, nu disparițiile.
Decuparea micșorează gradienții prea mari, dar nu face nimic când sunt prea mici. Pentru dispariții de gradient, ai nevoie de inițializare mai bună a ponderilor, conexiuni reziduale, batch normalization sau arhitecturi concepute să păstreze fluxul gradientului.
Decuparea gradientului după normă explicată
Decuparea după normă este metoda pe care majoritatea cititorilor o caută de fapt când vorbesc despre decuparea gradientului.
Procesul are trei pași. Mai întâi, calculezi norma tuturor gradientelor combinate. Apoi, compari norma cu pragul ales. În al treilea rând, rescalezi gradienții dacă norma e prea mare.
Norma este de obicei L2, ceea ce înseamnă că ridici la pătrat fiecare valoare a gradientului, le aduni și iei rădăcina pătrată. Dacă ai gradienți g_1, g_2, ..., g_n peste toți parametrii modelului, norma L2 este:

Formula pentru decuparea după normă
Odată ce ai norma, o compari cu pragul c. Dacă ||g|| <= c, gradienții trec neschimbați. Dacă ||g|| > c, fiecare gradient e înmulțit cu factorul de scalare c / ||g||. Asta aduce noua normă exact la c.
Contează pentru că fiecare componentă se micșorează cu același factor. Proporțiile relative dintre valori rămân neschimbate, ceea ce înseamnă că vectorul indică în aceeași direcție. Scurtezi pasul pe care îl face optimizatorul, nu îi schimbi direcția.
Această proprietate de păstrare a direcției face ca decuparea după normă să fie alegerea implicită. Decuparea după valoare poate răsuci vectorul gradient într-o direcție nouă. Decuparea după normă îi schimbă doar lungimea.
clip_grad_norm_ din PyTorch și clipnorm din TensorFlow fac exact asta. Când cineva spune „folosesc decuparea gradientului”, aproape întotdeauna se referă la decuparea după normă.
Alegerea unui prag pentru decuparea gradientului
Pragul este un hiperparametru, ceea ce înseamnă că nu există o valoare universală care funcționează pentru orice model.
Dacă îl setezi prea sus, decuparea aproape nu se activează. Gradienții rămân aproape mereu sub limită, așa că plasa de siguranță nu prinde nimic. Antrenarea decurge ca și cum decuparea n-ar exista și vei vedea în continuare vârfuri ale pierderii când gradienții explodează.
Dacă îl setezi prea jos, decupezi prea agresiv. Fiecare batch își micșorează gradienții, ceea ce face ca actualizările ponderilor să fie mai mici decât ar trebui. Învățarea încetinește și modelul tău converge mai greu, uneori mult mai greu.
Un punct de pornire frecvent este 1.0, care funcționează bine pentru multe arhitecturi. Valori între 0.5 și 5.0 acoperă majoritatea cazurilor practice.
Abordarea mai bună este să monitorizezi normele gradientului în timpul antrenării. Loghează norma ne-decupată la fiecare pas și uită-te la distribuție. Dacă majoritatea normelor stau în jur de 0.3 cu vârfuri ocazionale până la 50, setează pragul undeva peste intervalul tipic, dar mult sub vârfuri – 2.0 sau 3.0 ar fi rezonabile aici.
Trateaz-o ca pe orice alt hiperparametru. Pornește de la 1.0, urmărește ce se întâmplă și ajustează în funcție de comportamentul antrenării.
Decuparea gradientului în rețele neuronale recurente
RNN-urile sunt locul unde decuparea gradientului a devenit prima dată o tehnică standard.
Motivul ține de cum propagă RNN-urile gradienții prin timp. Backpropagation through time înmulțește aceleași matrici de greutate peste mulți pași de timp, iar aceste înmulțiri repetate pot compune valori masive. Secvențele lungi agravează problema.
LSTM-urile și GRU-urile au redus problema prin mecanismele de gating, dar nu au eliminat-o. Ambele arhitecturi beneficiază în continuare de decupare, mai ales când antrenezi pe secvențe lungi sau cu rate de învățare mari.
Pentru antrenarea RNN, decuparea după normă cu un prag între 1.0 și 5.0 este implicitul tipic. Dacă folosești nn.LSTM sau nn.GRU din PyTorch și pierderea explodează în timpul antrenării, adăugarea clip_grad_norm_ este de obicei primul lucru de încercat.
Decuparea gradientului în deep learning modern
Decuparea gradientului nu a dispărut când transformerele au înlocuit RNN-urile.
Modele lingvistice mari precum GPT și BERT folosesc decuparea atât în pre-antrenare, cât și în fine-tuning. La fel și transformerele pentru viziune, modelele de difuzie și majoritatea arhitecturilor adânci cu sute de straturi. Optimizatorii Adam și AdamW, care domină antrenarea modernă, sunt adesea cuplați cu decuparea după normă la praguri în jur de 1.0.
Motivul este același ca la RNN-uri. Rețelele adânci înmulțesc gradienții peste multe straturi, iar batch-urile mari combinate cu rate de învățare ridicate pot produce vârfuri ocazionale ale gradientului. Decuparea gestionează acele vârfuri fără să afecteze pașii normali de antrenare.
Majoritatea implementărilor de referință includ decuparea implicit. Trainer de la Hugging Face, PyTorch Lightning și DeepSpeed expun decuparea ca opțiune standard de configurare. Dacă antrenezi ceva mai mare decât un model de jucărie, decuparea face aproape sigur parte din pipeline.
E o linie de cod care costă aproape nimic și previne prăbușirea rulărilor după ore de calcul. De aceea a rămas.
Decuparea gradientului în PyTorch
PyTorch gestionează decuparea gradientului cu o singură funcție utilitară: torch.nn.utils.clip_grad_norm_.
Apelul de decupare vine între loss.backward() și optimizer.step(). Backpropagation trebuie mai întâi să umple gradienții, apoi decuparea îi micșorează dacă e nevoie, apoi optimizatorul aplică actualizarea. Dacă o pui în altă parte, nu va funcționa.
Iată un script complet, executabil, care antrenează un MLP mic pe date sintetice de regresie cu decuparea gradientului activă:
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset
torch.manual_seed(42)
# Synthetic regression data
n_samples = 1000
n_features = 20
inputs = torch.randn(n_samples, n_features)
targets = (inputs.sum(dim=1, keepdim=True) * 2.0 + torch.randn(n_samples, 1) * 0.1)
dataset = TensorDataset(inputs, targets)
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
# Small feedforward network
model = nn.Sequential(
nn.Linear(n_features, 64),
nn.ReLU(),
nn.Linear(64, 64),
nn.ReLU(),
nn.Linear(64, 1),
)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
loss_fn = nn.MSELoss()
# Training loop with gradient clipping
n_epochs = 5
max_grad_norm = 1.0
for epoch in range(n_epochs):
epoch_loss = 0.0
for batch_inputs, batch_targets in dataloader:
optimizer.zero_grad()
predictions = model(batch_inputs)
loss = loss_fn(predictions, batch_targets)
loss.backward()
# Gradient clipping
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=max_grad_norm)
optimizer.step()
epoch_loss += loss.item()
print(f"Epoch {epoch + 1}: loss = {epoch_loss / len(dataloader):.4f}")

Output PyTorch
Funcția clip_grad_norm_ primește două argumente principale:
-
parameters: parametrii modelului ale căror gradienți vrei să îi decupezi. Paseazămodel.parameters()pentru a acoperi întregul model. -
max_norm: pragul pentru norma gradientului. O valoare de1.0este un punct de pornire comun.
Există și argumentul opțional norm_type care e implicit 2.0 pentru norma L2. Rar vei avea nevoie să-l schimbi.
Liniuța de subliniere de la finalul lui clip_grad_norm_ indică o operație in-place. Funcția modifică direct gradienții în atributul .grad al fiecărui parametru, deci nu trebuie să ții cont de valoarea returnată. Returnează totuși norma totală a gradientelor înainte de decupare, utilă dacă vrei s-o loghezi.
Pentru decupare după valoare în loc de după normă, PyTorch are torch.nn.utils.clip_grad_value_:
torch.nn.utils.clip_grad_value_(model.parameters(), clip_value=0.5)
Dar, după cum am discutat, rar (dacă vreodată) vei folosi această implementare.
Asta e tot setup-ul. Două linii adăugate în bucla ta de antrenare.
Decuparea gradientului în TensorFlow
TensorFlow gestionează decuparea la nivelul optimizatorului, nu ca apel separat de funcție.
Când creezi un optimizator, pasezi clipnorm sau clipvalue ca argument. Optimizatorul aplică intern decuparea la fiecare pas, așa că nu trebuie să-ți modifici deloc bucla de antrenare.
Iată un exemplu complet funcțional folosind API-ul Keras pe date sintetice de regresie:
import numpy as np
import tensorflow as tf
tf.random.set_seed(42)
np.random.seed(42)
# Synthetic regression data
n_samples = 1000
n_features = 20
x_train = np.random.randn(n_samples, n_features).astype(np.float32)
y_train = (x_train.sum(axis=1, keepdims=True) * 2.0
+ np.random.randn(n_samples, 1).astype(np.float32) * 0.1)
# Small feedforward network
model = tf.keras.Sequential([
tf.keras.layers.Dense(64, activation="relu", input_shape=(n_features,)),
tf.keras.layers.Dense(64, activation="relu"),
tf.keras.layers.Dense(1),
])
# Optimizer with gradient clipping by norm
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3, clipnorm=1.0)
model.compile(optimizer=optimizer, loss="mse")
model.fit(x_train, y_train, epochs=5, batch_size=32)

Output TensorFlow
Cele două argumente fac lucruri diferite:
-
clipnormdecupează după norma L2 a fiecărui tensor de gradient. Dacă norma depășește pragul, tensorul este scalat proporțional în jos. -
clipvaluedecupează individual fiecare element al gradientului. Orice valoare peste prag este limitată la prag, iar orice valoare sub pragul negativ este limitată la pragul negativ.
Pentru a trece de la decuparea după normă la cea după valoare, schimbă argumentul:
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-3, clipvalue=0.5)
Ambele argumente funcționează cu orice optimizator Keras: Adam, SGD, RMSprop, AdamW și restul. Există și argumentul global_clipnorm care decupează pe baza normei calculate peste toți gradienții combinați, nu per-tensor. Asta se potrivește mai bine cu comportamentul implicit al PyTorch.
Dacă scrii o buclă de antrenare personalizată cu tf.GradientTape, optimizatorul tot gestionează decuparea când apelezi apply_gradients:
for epoch in range(5):
for batch_x, batch_y in zip(np.array_split(x_train, 32), np.array_split(y_train, 32)):
with tf.GradientTape() as tape:
predictions = model(batch_x, training=True)
loss = tf.reduce_mean(tf.square(predictions - batch_y))
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
Aceasta e diferența dintre cele două framework-uri. PyTorch îți pune decuparea în mâini, în interiorul buclei. TensorFlow o împinge în interiorul optimizatorului. Logica de bază e identică.
Decuparea gradientului vs alte tehnici de stabilizare
Decuparea gradientului nu este singura metodă de a stabiliza antrenarea și nu e întotdeauna unealta potrivită.
Alte tehnici rezolvă probleme înrudite, dar diferite. Unele previn creșterea gradientelor din start, altele le împiedică să dispară, iar unele fac suprafața pierderii mai ușor de optimizat. Îți arăt câteva tehnici diferite.
Batch normalization
Batch normalization normalizează activările în fiecare mini-batch în timpul antrenării.
Menține ieșirile straturilor într-un interval stabil, ceea ce face magnitudinea gradientelor mai previzibilă. Rețelele antrenate cu batch norm tolerează rate de învățare mai mari și converg mai repede, fiind mai puțin sensibile la alegerea inițializării ponderilor.
Dar batch norm nu oprește direct exploziile de gradient. Reduce frecvența lor, nu și impactul când apar. Multe modele tot cuplează batch norm cu decuparea gradientului din acest motiv.
Conexiuni reziduale
Conexiunile reziduale adaugă scurtături care sar peste unul sau mai multe straturi, lăsând gradienții să curgă direct de la straturi târzii la straturi timpurii.
Asta rezolvă problema dispariției gradientului în rețele adânci. Fără conexiuni reziduale, antrenarea rețelelor cu peste 20–30 de straturi devine dificilă pentru că gradienții se micșorează spre zero pe măsură ce se propagă înapoi. Cu ele, rețele cu sute de straturi se antrenează fără probleme.
Conexiunile reziduale vizează partea opusă a problemei față de decupare. Decuparea gestionează gradienții prea mari. Reziduurile gestionează gradienții care devin prea mici.
Inițializarea atentă a ponderilor
Valorile inițiale ale ponderilor stabilesc magnitudinea de pornire a activărilor și a gradientelor. O inițializare proastă poate cauza explozii sau dispariții de gradient încă din primul pas.
Metodele precum inițializarea Xavier și He scalează ponderile inițiale în funcție de dimensiunea stratului. Asta menține varianțele activărilor stabile între straturi la începutul antrenării, prevenind multe probleme cu gradientul înainte să apară.
O inițializare bună reduce șansa să ai nevoie de decupare, dar nu o elimină. Vârfuri de gradient pot apărea și mai târziu în antrenare, mai ales cu rate de învățare mari sau batch-uri atipice.
Cum se potrivesc între ele
Tehnicile acestea nu sunt alternative. Sunt unelte complementare care rezolvă părți diferite ale aceleiași probleme generale.
Un setup modern tipic folosește inițializare atentă la start, conexiuni reziduale în arhitectură, batch normalization (sau layer normalization) în interiorul rețelei și decuparea gradientului ca plasă de siguranță în timpul optimizării. Fiecare tratează un mod specific de eșec, iar împreună fac rețelele adânci antrenabile.
Concluzie
Decuparea gradientului este una dintre cele mai simple remedii în deep learning și rezolvă o problemă care îți poate strica ore de antrenare într-un singur pas.
Vestea bună e că nu trebuie să-ți schimbi arhitectura modelului sau să-ți rescrii codul de antrenare. O linie în PyTorch sau un argument în TensorFlow sunt suficiente pentru a implementa decuparea gradientului.
Funcționează cel mai bine ca parte a unui setup mai amplu. Cupleaz-o cu inițializarea atentă a ponderilor, conexiuni reziduale și batch sau layer normalization și vei avea un pipeline de antrenare care gestionează instabilitățile din mai multe unghiuri.
Dacă pierderea îți explodează, începe cu decuparea. Dacă dispare, caută alte soluții. Și dacă antrenezi ceva mai mare decât un model mic, adaugă decuparea implicit în pipeline și uită de ea.
Decuparea gradientului este doar unul dintre mulții termeni pe care orice inginer de machine learning trebuie să-i cunoască. Dacă vrei să le înveți pe celelalte și să fii pregătit pentru job în 2026, înscrie-te azi în parcursul Machine Learning Engineer.
Întrebări frecvente despre decuparea gradientului
Ce este decuparea gradientului în deep learning?
Decuparea gradientului este o tehnică ce limitează mărimea gradientelor în timpul antrenării rețelelor neuronale pentru a preveni actualizări instabile ale parametrilor. Când gradienții cresc prea mult în timpul backpropagation, optimizatorul face pași uriași care împing ponderile în regiuni nefavorabile și determină explozia pierderii. Decuparea plafonează magnitudinea gradientului înainte de pasul optimizatorului, astfel încât actualizările rămân limitate chiar și când gradienții brute au vârfuri.
Când ar trebui să folosesc decuparea gradientului?
Folosește decuparea gradientului ori de câte ori antrenarea e instabilă, mai ales dacă vezi vârfuri bruște ale pierderii, valori NaN sau divergență după ore de antrenare. E practică standard pentru rețele recurente precum LSTM și GRU și pentru orice arhitectură adâncă antrenată cu rate de învățare mari. Dacă curba pierderii arată bine, poți s-o omiți, dar adăugarea ei ca plasă de siguranță nu strică.
Care e diferența dintre decuparea după valoare și după normă?
Decuparea după valoare limitează individual fiecare element al gradientului, ceea ce poate schimba direcția vectorului gradient. Decuparea după normă scalează întregul vector când magnitudinea totală depășește un prag, păstrând direcția originală și scurtând doar pasul. Decuparea după normă este alegerea standard în deep learning modern deoarece nu distorsionează direcția actualizării.
Cum aleg un prag bun pentru decuparea gradientului?
Începe cu 1.0 și ajustează în funcție de ce observi în antrenare. Loghează normele gradientului ne-decupat pe batch-uri și uită-te la distribuție. Setează pragul peste intervalul tipic al normei, dar bine sub vârfurile pe care vrei să le prinzi. Dacă e setat prea sus, decuparea nu se activează, iar dacă e prea jos, vei încetini în mod inutil învățarea.
Rezolvă decuparea gradientului și disparițiile de gradient?
Nu. Decuparea gradientului abordează doar exploziile de gradient, când valorile devin prea mari. Disparițiile de gradient sunt problema opusă, când valorile scad spre zero și învățarea practic se oprește. Pentru dispariții de gradient, folosește o inițializare mai bună a ponderilor, conexiuni reziduale, batch normalization sau arhitecturi precum LSTM și GRU.