Faza 03 · lecția 03

Retropropagarea de la zero

Scopul lecției: Retropropagarea este algoritmul care face posibilă învățarea. Fără ea, rețelele neurale sunt doar generatoare costisitoare de numere aleatoare.

Versiunea curentă AlexBred.com: primele 100 de lecții ale programului în limba română.

Curs
AI Engineering from Scratch
Fază
Fundamentele învățării profunde
Lectură
22 min.
Verificat
Cuprinsul lecției
  1. Obiective de învățare
  2. Problema
  3. Conceptul
  4. Regula lanțului aplicată rețelelor
  5. Grafuri computaționale
  6. Propagarea înainte și propagarea înapoi
  7. Fluxul gradientului printr-o rețea
  8. Gradienții care dispar
  9. Deducerea gradienților pentru o rețea cu două straturi
  10. Construiți
  11. Pasul 1: nodul Value
  12. Pasul 2: operații cu funcții de propagare înapoi
  13. Pasul 3: sigmoida și pierderea
  14. Pasul 4: propagarea înapoi
  15. Pasul 5: stratul și rețeaua
  16. Pasul 6: antrenarea pe XOR
  17. Pasul 7: clasificarea cercului
  18. Folosiți
  19. Livrați
  20. Exerciții
  21. Termeni-cheie
  22. Lecturi suplimentare

Retropropagarea este algoritmul care face posibilă învățarea. Fără ea, rețelele neurale sunt doar generatoare costisitoare de numere aleatoare.

Tip: Construire Limbaje: Python Cerințe preliminare: Lecția 03.02 — Rețele multistrat Durată: ~120 de minute

Obiective de învățare

  • Să implementați un motor autograd bazat pe obiecte Value, care construiește un graf computațional și calculează gradienții prin sortare topologică.
  • Să deduceți propagarea înapoi pentru adunare, înmulțire și sigmoidă folosind regula lanțului.
  • Să antrenați o rețea multistrat pentru clasificarea XOR și a cercului folosind exclusiv motorul de retropropagare construit de la zero.
  • Să identificați problema gradienților care dispar în rețele sigmoidale profunde și să explicați de ce aceștia se micșorează exponențial.

Problema

Rețeaua dumneavoastră are un singur strat ascuns, cu 768 de intrări și 3072 de ieșiri. Aceasta înseamnă 2.359.296 de ponderi. Rețeaua a făcut o predicție greșită. Care ponderi au provocat eroarea? Testarea individuală a fiecărei ponderi ar însemna 2,3 milioane de propagări înainte. Retropropagarea calculează toți cei 2,3 milioane de gradienți într-o singură propagare înapoi. Aceasta nu este doar o optimizare. Este diferența dintre ceva antrenabil și ceva imposibil de antrenat.

Abordarea naivă: luați o pondere, modificați-o cu o cantitate infimă, executați din nou propagarea înainte și măsurați dacă pierderea a crescut sau a scăzut. Astfel obțineți gradientul acelei ponderi. Acum faceți același lucru pentru fiecare pondere din rețea. Înmulțiți costul cu mii de pași de antrenare și milioane de puncte de date. Ați avea nevoie de intervale geologice pentru a antrena ceva util.

Retropropagarea rezolvă această problemă. O propagare înainte, o propagare înapoi, toți gradienții calculați. Trucul este regula lanțului din analiza matematică, aplicată sistematic unui graf computațional. Acesta este algoritmul care a făcut practică învățarea profundă. Fără el, am fi încă limitați la probleme demonstrative.

Notă tehnică a traducerii: Comparația cu câte o propagare înainte pentru fiecare parametru descrie diferențierea numerică prin perturbații, nu toate alternativele posibile. Pentru o pierdere scalară, retropropagarea este diferențiere automată în mod invers și calculează într-o singură traversare inversă gradienții tuturor parametrilor care contribuie la acea pierdere.

Conceptul

Regula lanțului aplicată rețelelor

Ați întâlnit regula lanțului în Faza 01, lecția 05. O recapitulare rapidă: dacă y = f(g(x)), atunci dy/dx = f'(g(x)) * g'(x). Înmulțiți derivatele de-a lungul lanțului.

Într-o rețea neurală, „lanțul” este succesiunea de operații de la intrare până la pierdere. Fiecare strat aplică ponderi, adaugă biasuri și trece rezultatul printr-o activare. Funcția de pierdere compară ieșirea finală cu ținta. Retropropagarea urmărește acest lanț în sens invers și calculează contribuția fiecărei operații la eroare.

Grafuri computaționale

Fiecare propagare înainte construiește un graf. Fiecare nod este o operație — înmulțire, adunare sau sigmoidă. Fiecare muchie transportă înainte o valoare și înapoi un gradient.

Диаграмма к уроку «Retropropagarea de la zero»

Propagarea înainte: valorile curg de la stânga la dreapta. x și w produc z1 = w*x. Adăugați b pentru a obține z2. Sigmoida furnizează activarea a. Comparați a cu ținta y prin funcția de pierdere.

Propagarea înapoi: gradienții curg de la dreapta la stânga. Începeți cu dL/da — felul în care se modifică pierderea odată cu activarea. Înmulțiți cu da/dz2, derivata sigmoidei. Obțineți astfel dL/dz2. Ramificați în dL/db, care este egal cu dL/dz2 deoarece z2 = z1 + b, și dL/dz1. Apoi dL/dw = dL/dz1 * x și dL/dx = dL/dz1 * w.

Fiecare nod din graf are o singură sarcină în timpul propagării înapoi: preia gradientul sosit din aval, îl înmulțește cu derivata sa locală și îl transmite mai departe spre intrări.

Propagarea înainte și propagarea înapoi

Диаграмма к уроку «Retropropagarea de la zero»

Propagarea înainte stochează valorile intermediare: z, a și intrările fiecărui strat. Propagarea înapoi are nevoie de asemenea valori salvate pentru a calcula gradienții. Acesta este compromisul memorie–calcul aflat în centrul retropropagării. Sacrificați memorie pentru stocarea activărilor în schimbul vitezei oferite de o singură traversare, în locul milioanelor de traversări.

Notă tehnică a traducerii: Implementările nu trebuie să păstreze literalmente fiecare valoare intermediară, ci numai valorile necesare regulilor de derivare. Checkpointing-ul activărilor reduce și mai mult memoria, recalculând în propagarea înapoi o parte dintre valorile produse înainte.

Fluxul gradientului printr-o rețea

Pentru o rețea cu trei straturi, gradienții se înlănțuie prin fiecare strat:

Диаграмма к уроку «Retropropagarea de la zero»

La fiecare strat, gradientul este înmulțit cu derivata sigmoidei. Derivata sigmoidei este a * (1 - a) și atinge cel mult 0,25, când a = 0,5. Dacă urmărim numai acești factori de activare, după trei straturi produsul este cel mult 0.25^3 = 0.0156. După zece straturi: 0.25^10 = 0.000001.

Notă tehnică a traducerii: Într-o rețea reală, gradientul este înmulțit și cu ponderile sau, în forma vectorială, cu Jacobienii straturilor. Prin urmare, 0.25^L descrie doar contribuția maximă a derivatelor sigmoidale, nu o limită universală pentru gradientul complet; ponderile îl pot atenua sau amplifica.

Gradienții care dispar

Aceasta este problema gradienților care dispar. Sigmoida comprimă ieșirea între 0 și 1. Derivata ei nu depășește niciodată 0,25. Suprapuneți suficiente straturi sigmoidale, iar contribuția acestor derivate face gradienții să se micșoreze drastic. Straturile timpurii abia învață, deoarece primesc gradienți aproape nuli.

sigmoid(z):     Intervalul ieșirii [0, 1]
sigmoid'(z):    Valoarea maximă 0,25 (la z = 0)

După 5 straturi:   gradient * 0.25^5 = 0.001x valoarea inițială
După 10 straturi:  gradient * 0.25^10 = 0.000001x valoarea inițială

Acesta este motivul pentru care rețelele sigmoidale profunde sunt foarte greu de antrenat. O soluție importantă — ReLU și variantele sale — este tema lecției 04. Pentru moment, rețineți că mecanismul retropropagării aplică exact regula lanțului; dificultatea provine din operațiile și parametrii prin care trebuie să se propage gradientul.

Notă tehnică a traducerii: Formularea originală conform căreia retropropagarea „funcționează perfect” este prea categorică. În practică, stabilitatea numerică, operațiile nediferențiabile, grafurile implementate greșit și gradienții care explodează pot produce dificultăți chiar dacă regula lanțului este corectă.

Deducerea gradienților pentru o rețea cu două straturi

Să scriem matematica în formă concretă pentru o rețea cu intrarea x, un strat ascuns cu sigmoidă, un strat de ieșire cu sigmoidă și pierdere MSE.

Propagarea înainte:

z1 = W1 * x + b1
a1 = sigmoid(z1)
z2 = W2 * a1 + b2
a2 = sigmoid(z2)
L = (a2 - y)^2

Propagarea înapoi, prin aplicarea pas cu pas a regulii lanțului:

dL/da2 = 2(a2 - y)
da2/dz2 = a2 * (1 - a2)
dL/dz2 = dL/da2 * da2/dz2 = 2(a2 - y) * a2 * (1 - a2)

dL/dW2 = dL/dz2 * a1
dL/db2 = dL/dz2

dL/da1 = dL/dz2 * W2
da1/dz1 = a1 * (1 - a1)
dL/dz1 = dL/da1 * da1/dz1

dL/dW1 = dL/dz1 * x
dL/db1 = dL/dz1

Fiecare gradient este produsul derivatelor locale urmărite înapoi de la pierdere. În esență, aceasta este retropropagarea.

Notă tehnică a traducerii: Relațiile de mai sus sunt scrise ca produse scalare simplificate. Pentru vectori și matrice, gradienții ponderilor includ produse exterioare și transpuneri compatibile cu formele tensorilor.

backprop-vanishing

Construiți

Pasul 1: nodul Value

Fiecare număr din calculul nostru devine un obiect Value. Acesta își stochează valoarea, gradientul și modul în care a fost creat, astfel încât să știe cum se calculează gradienții înapoi.

class Value:
    def __init__(self, data, children=(), op=''):
        self.data = data
        self.grad = 0.0
        self._backward = lambda: None
        self._children = set(children)
        self._op = op

    def __repr__(self):
        return f"Value(data={self.data:.4f}, grad={self.grad:.4f})"

Gradientul încă nu există, deci este 0.0. Funcția de propagare înapoi nu există încă, deci nu face nimic. Câmpul _children urmărește obiectele Value care l-au produs pe cel curent, pentru a putea sorta topologic graful mai târziu.

Pasul 2: operații cu funcții de propagare înapoi

Fiecare operație creează un obiect Value nou și definește modul în care gradienții curg înapoi prin ea.

def __add__(self, other):
    other = other if isinstance(other, Value) else Value(other)
    out = Value(self.data + other.data, (self, other), '+')

    def _backward():
        self.grad += out.grad
        other.grad += out.grad

    out._backward = _backward
    return out

def __mul__(self, other):
    other = other if isinstance(other, Value) else Value(other)
    out = Value(self.data * other.data, (self, other), '*')

    def _backward():
        self.grad += other.data * out.grad
        other.grad += self.data * out.grad

    out._backward = _backward
    return out

Pentru adunare: d(a+b)/da = 1, d(a+b)/db = 1. Prin urmare, ambele intrări primesc direct gradientul ieșirii.

Pentru înmulțire: d(a*b)/da = b, d(a*b)/db = a. Fiecare intrare primește valoarea celeilalte înmulțită cu gradientul ieșirii.

Operatorul += este esențial. Un obiect Value poate fi folosit în mai multe operații. Gradientul său este suma gradienților de pe toate căile.

Pasul 3: sigmoida și pierderea

import math

def sigmoid(self):
    x = self.data
    x = max(-500, min(500, x))
    s = 1.0 / (1.0 + math.exp(-x))
    out = Value(s, (self,), 'sigmoid')

    def _backward():
        self.grad += (s * (1 - s)) * out.grad

    out._backward = _backward
    return out

Derivata sigmoidei: sigmoid(x) * (1 - sigmoid(x)). Am calculat sigmoid(x) = s în timpul propagării înainte. Refolosiți valoarea. Nu este nevoie de muncă suplimentară.

Notă tehnică a traducerii: limitarea lui x la intervalul [-500, 500] păstrează calculul finit, dar modifică funcția. În afara intervalului, derivata clamp-ului este zero (cu o convenție la frontieră); backward-ul didactic nu o aplică și este astfel inconsistent pentru intrări limitate. O implementare de producție trebuie să reprezinte clamp-ul în graful de diferențiere sau să utilizeze o sigmoidă stabilă furnizată de bibliotecă.

def mse_loss(predicted, target):
    diff = predicted + Value(-target)
    return diff * diff

MSE pentru o singură ieșire este (predicted - target)^2. Exprimăm scăderea ca adunare cu un obiect Value negat.

Pasul 4: propagarea înapoi

Sortarea topologică ne asigură că prelucrăm nodurile în ordinea corectă: gradientul unui nod este acumulat complet înainte de a-l propaga prin acel nod.

def backward(self):
    topo = []
    visited = set()

    def build_topo(v):
        if v not in visited:
            visited.add(v)
            for child in v._children:
                build_topo(child)
            topo.append(v)

    build_topo(self)
    self.grad = 1.0
    for v in reversed(topo):
        v._backward()

Începeți de la pierdere cu gradientul 1.0, deoarece dL/dL = 1. Parcurgeți în sens invers graful sortat. Funcția _backward a fiecărui nod împinge gradienții spre predecesorii săi.

Pasul 5: stratul și rețeaua

import random

class Neuron:
    def __init__(self, n_inputs):
        scale = (2.0 / n_inputs) ** 0.5
        self.weights = [Value(random.uniform(-scale, scale)) for _ in range(n_inputs)]
        self.bias = Value(0.0)

    def __call__(self, x):
        act = sum((wi * xi for wi, xi in zip(self.weights, x)), self.bias)
        return act.sigmoid()

    def parameters(self):
        return self.weights + [self.bias]


class Layer:
    def __init__(self, n_inputs, n_outputs):
        self.neurons = [Neuron(n_inputs) for _ in range(n_outputs)]

    def __call__(self, x):
        out = [n(x) for n in self.neurons]
        return out[0] if len(out) == 1 else out

    def parameters(self):
        params = []
        for n in self.neurons:
            params.extend(n.parameters())
        return params


class Network:
    def __init__(self, sizes):
        self.layers = []
        for i in range(len(sizes) - 1):
            self.layers.append(Layer(sizes[i], sizes[i + 1]))

    def __call__(self, x):
        for layer in self.layers:
            x = layer(x)
            if not isinstance(x, list):
                x = [x]
        return x[0] if len(x) == 1 else x

    def parameters(self):
        params = []
        for layer in self.layers:
            params.extend(layer.parameters())
        return params

    def zero_grad(self):
        for p in self.parameters():
            p.grad = 0.0

Un obiect Neuron primește intrările, calculează suma ponderată plus biasul și aplică sigmoida. Inițializarea ponderilor folosește factorul sqrt(2/n_inputs), prezentat în original ca mijloc de a preveni saturarea sigmoidei în rețele mai profunde. Un obiect Layer este o listă de obiecte Neuron. Un obiect Network este o listă de obiecte Layer. Metoda parameters() colectează toate obiectele Value antrenabile, pentru a le putea actualiza.

Notă tehnică a traducerii: Factorul sqrt(2/fan_in) este abaterea standard a inițializării He normale pentru activări rectificatoare precum ReLU, nu limita uniformă folosită de cod. Eșantionarea uniformă pe [-sqrt(2/fan_in), sqrt(2/fan_in)] are varianța 2/(3*fan_in); inițializarea He uniformă pentru ReLU folosește limita sqrt(6/fan_in). Pentru sigmoide sau tanh se folosește de regulă inițializarea Xavier/Glorot, care urmărește să păstreze variația activărilor și a gradienților între straturi. Codul original este păstrat neschimbat pentru continuitatea exercițiului.

Pasul 6: antrenarea pe XOR

random.seed(42)
net = Network([2, 4, 1])

xor_data = [
    ([0.0, 0.0], 0.0),
    ([0.0, 1.0], 1.0),
    ([1.0, 0.0], 1.0),
    ([1.0, 1.0], 0.0),
]

learning_rate = 1.0

for epoch in range(1000):
    total_loss = Value(0.0)
    for inputs, target in xor_data:
        x = [Value(i) for i in inputs]
        pred = net(x)
        loss = mse_loss(pred, target)
        total_loss = total_loss + loss

    net.zero_grad()
    total_loss.backward()

    for p in net.parameters():
        p.data -= learning_rate * p.grad

    if epoch % 100 == 0:
        print(f"Epoch {epoch:4d} | Loss: {total_loss.data:.6f}")

print("\nXOR Results:")
for inputs, target in xor_data:
    x = [Value(i) for i in inputs]
    pred = net(x)
    print(f"  {inputs} -> {pred.data:.4f} (expected {target})")

Urmăriți cum scade pierderea. De la predicții aleatoare la ieșiri XOR corecte, totul este determinat de retropropagarea care calculează gradienții și deplasează ponderile în direcția potrivită.

Pasul 7: clasificarea cercului

În lecția 02 ați folosit ponderi alese manual pentru XOR și ponderi aleatoare pentru exemplul de clasificare a cercului. Acum lăsați rețeaua să învețe ponderile cercului.

Notă tehnică a traducerii: Originalul afirmă că ponderile clasificatorului circular au fost reglate manual în lecția 02. În lecția precedentă, reglarea manuală este folosită pentru XOR, în timp ce exemplul cercului utilizează ponderi aleatoare; formularea de mai sus corectează trimiterea încrucișată.

random.seed(7)

def generate_circle_data(n=100):
    data = []
    for _ in range(n):
        x1 = random.uniform(-1.5, 1.5)
        x2 = random.uniform(-1.5, 1.5)
        label = 1.0 if x1 * x1 + x2 * x2 < 1.0 else 0.0
        data.append(([x1, x2], label))
    return data

circle_data = generate_circle_data(80)

circle_net = Network([2, 8, 1])
learning_rate = 0.5

for epoch in range(2000):
    random.shuffle(circle_data)
    total_loss_val = 0.0
    for inputs, target in circle_data:
        x = [Value(i) for i in inputs]
        pred = circle_net(x)
        loss = mse_loss(pred, target)
        circle_net.zero_grad()
        loss.backward()
        for p in circle_net.parameters():
            p.data -= learning_rate * p.grad
        total_loss_val += loss.data

    if epoch % 200 == 0:
        correct = 0
        for inputs, target in circle_data:
            x = [Value(i) for i in inputs]
            pred = circle_net(x)
            predicted_class = 1.0 if pred.data > 0.5 else 0.0
            if predicted_class == target:
                correct += 1
        accuracy = correct / len(circle_data) * 100
        print(f"Epoch {epoch:4d} | Loss: {total_loss_val:.4f} | Accuracy: {accuracy:.1f}%")

Aici folosim SGD online: actualizăm ponderile după fiecare exemplu, în loc să acumulăm întregul lot. Amestecarea datelor la fiecare epocă reduce efectele nedorite ale ordinii exemplelor și corelația dintre actualizări consecutive.

Notă tehnică a traducerii: Afirmațiile originale că SGD online „rupe simetria mai repede”, evită saturarea pe întregul peisaj al pierderii și că amestecarea împiedică memorarea ordinii nu sunt garanții generale. Simetria este ruptă în primul rând prin inițializarea diferită a parametrilor; amestecarea reduce părtinirea indusă de ordine, iar saturarea depinde de activări, ponderi, date și pașii de optimizare.

Fără reglare manuală, rețeaua ajustează o frontieră de decizie circulară pe datele de antrenare. Aceasta este puterea retropropagării: definiți arhitectura, funcția de pierdere și datele, iar algoritmul determină ponderile.

Notă tehnică a traducerii: Acuratețea afișată este măsurată pe aceleași 80 de exemple folosite la antrenare. Ea arată potrivirea datelor de antrenare, nu demonstrează generalizarea; aceasta ar necesita un set separat de validare sau testare.

Folosiți

PyTorch face tot ce am construit mai sus în câteva linii. Ideea de bază este aceeași: autograd construiește un graf computațional în timpul propagării înainte și îl urmărește în sens invers pentru a calcula gradienții.

import torch
import torch.nn as nn

model = nn.Sequential(
    nn.Linear(2, 4),
    nn.Sigmoid(),
    nn.Linear(4, 1),
    nn.Sigmoid(),
)
optimizer = torch.optim.SGD(model.parameters(), lr=1.0)
criterion = nn.MSELoss()

X = torch.tensor([[0,0],[0,1],[1,0],[1,1]], dtype=torch.float32)
y = torch.tensor([[0],[1],[1],[0]], dtype=torch.float32)

for epoch in range(1000):
    pred = model(X)
    loss = criterion(pred, y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

print("PyTorch XOR Results:")
with torch.no_grad():
    for i in range(4):
        pred = model(X[i])
        print(f"  {X[i].tolist()} -> {pred.item():.4f} (expected {y[i].item()})")

loss.backward() corespunde metodei total_loss.backward() pe care ați construit-o. optimizer.step() corespunde actualizării manuale p.data -= lr * p.grad. optimizer.zero_grad() corespunde metodei net.zero_grad(). Este același algoritm, într-o implementare pregătită pentru uz industrial. PyTorch gestionează accelerarea pe GPU, precizia mixtă, checkpointing-ul gradienților și sute de tipuri de straturi. Propagarea înapoi rămâne aceeași aplicare a regulii lanțului pe graful computațional.

Antrenarea execută propagarea înainte, apoi propagarea înapoi și, în final, actualizează ponderile. Inferența obișnuită execută numai propagarea înainte: nu calculează gradienți și nu actualizează ponderi. Distincția contează deoarece inferența este operația predominantă în producție. Atunci când apelați un API pentru un model lingvistic, promptul curge înainte prin rețea, iar la celălalt capăt apar tokenuri; ponderile modelului nu se modifică în acel apel. Înțelegerea retropropagării este importantă deoarece ea a contribuit la formarea fiecărei ponderi din rețea în timpul antrenării.

Livrați

Această lecție produce:

  • outputs/prompt-gradient-debugger.md — un prompt reutilizabil pentru diagnosticarea problemelor de gradient — dispariție, explozie sau NaN — în orice rețea neurală.

Exerciții

  1. Adăugați clasei Value o metodă __sub__, folosind relația a - b = a + (-1 * b). Apoi implementați o metodă __neg__. Verificați corectitudinea gradienților prin compararea cu un calcul manual pentru o expresie simplă precum (a - b)^2.

  2. Adăugați obiectului Value o metodă relu: ieșirea este max(0, x), iar derivata este 1 dacă x > 0 și 0 în caz contrar. Înlocuiți sigmoida cu ReLU în straturile ascunse și antrenați din nou pe XOR. Comparați viteza convergenței. Este posibil să observați o antrenare mai rapidă — o anticipare a lecției 04 — însă rezultatul depinde de inițializare și hiperparametri.

  3. Implementați în Value o metodă __pow__ pentru puteri întregi. Folosiți-o pentru a înlocui mse_loss cu expresia propriu-zisă (predicted - target) ** 2. Verificați dacă gradienții coincid cu cei ai implementării originale.

  4. Adăugați limitarea gradienților în bucla de antrenare: după apelarea metodei backward(), limitați toți gradienții la intervalul [-1, 1]. Antrenați o rețea mai profundă — cel puțin patru straturi cu sigmoidă — și comparați curbele pierderii cu și fără limitare. Aceasta este prima dumneavoastră apărare împotriva gradienților care explodează.

  5. Construiți o vizualizare: după antrenarea pe XOR, afișați gradientul fiecărui parametru din rețea. Identificați stratul care are gradienții cei mai mici. Astfel demonstrați problema gradienților care dispar, prezentată în secțiunea Conceptul.

Termeni-cheie

Termen Ce spun oamenii Ce înseamnă de fapt
Retropropagare „Rețeaua învață” Algoritm care calculează dL/dw pentru fiecare pondere, aplicând regula lanțului în sens invers prin graful computațional
Graf computațional „Structura rețelei” Graf orientat aciclic ale cărui noduri reprezintă operații și ale cărui muchii transportă valori înainte și gradienți înapoi
Regula lanțului „Înmulțiți derivatele” Dacă y = f(g(x)), atunci dy/dx = f'(g(x)) * g'(x) — fundamentul matematic al retropropagării
Gradient „Direcția celei mai abrupte creșteri” Vectorul derivatelor parțiale ale pierderii față de parametri; fiecare componentă indică sensibilitatea pierderii la parametrul corespunzător
Gradient care dispare „Rețelele profunde nu învață” Gradienții se micșorează puternic pe măsură ce se propagă prin multe straturi cu activări saturante precum sigmoida
Propagare înainte „Rularea rețelei” Calcularea ieșirii pornind de la intrări, prin aplicarea succesivă a operațiilor fiecărui strat și păstrarea valorilor intermediare necesare
Propagare înapoi „Calcularea gradienților” Parcurgerea inversă a grafului computațional și acumularea gradienților la fiecare nod folosind regula lanțului
Rată de învățare „Cât de repede învață” Scalar care controlează mărimea pasului la actualizarea ponderilor: w_nou = w_vechi - lr * gradient
Sortare topologică „Ordinea corectă” Ordonare a nodurilor grafului în care fiecare nod apare după toate nodurile de care depinde; parcurgerea inversă asigură acumularea gradienților înainte de propagare
Autograd „Diferențiere automată” Sistem care construiește grafuri computaționale în timpul calculului înainte și calculează automat gradienții, precum motorul din PyTorch

Lecturi suplimentare

  • Rumelhart, Hinton și Williams, „Learning representations by back-propagating errors” (1986) — lucrarea care a popularizat retropropagarea și a permis antrenarea rețelelor multistrat.
  • 3Blue1Brown, seria „Neural Networks” — explicație vizuală excelentă a retropropagării și a fluxului gradientului prin rețele.

Sursă: Originalul în limba engleză

Navigare: ← Lecția 03.02 — Rețele multistrat și propagarea înainte · Faza 3 — Fundamentele învățării profunde · Catalog complet · Lecția 03.04 — Funcții de activare →