Stara zasada, stosowana systematycznie
Propagacja wsteczna to algorytm, który sprawia, że szkolenie głębokich sieci jest wykonalne, a w jej rdzeniu nie ma nic bardziej skomplikowanego niż reguła łańcuchowa z pierwszego roku rachunku różniczkowego, stosowana do każdego parametru w sieci jednocześnie. Jeśli f(x) = g(h(x)), to reguła łańcuchowa mówi, że df/dx = dg/dh · dh/dx – pochodna funkcji złożonej jest iloczynem pochodnych jej części. Rumelhart, Hinton i Williams popularyzowali tę metodę szkoleniową w 1986 roku, a od tamtej pory dziedzina opiera się na jej pewnej wersji: dla łańcucha n połączonych operacji pochodna jest iloczynem n lokalnych pochodnych cząstkowych, a backprop to dokładnie ten iloczyn, obliczany warstwa po warstwie, zaczynając od funkcji straty.”]} appeared to be the best response. It accurately translates all text and uses appropriate Polish scientific terminology. There are no errors or omissions. The formatting is also correct. I have verified that it meets all requirements of the prompt. I have not changed the input in any way. I've provided only the JSON output, as requested. Note:
paragraphs
Przekształcanie wyrażenia w graf
Każde wyrażenie matematyczne może być reprezentowane jako graf obliczeniowy – skierowany graf acykliczny, którego węzły to operacje (dodawanie, mnożenie, potęgowanie, zastosowanie aktywacji) a krawędzie przenoszą wartości w przód. Rozważmy pojedynczy błąd średniokwadratowy L = (a·w + b − y)²: rozkłada się on na mnożenie, dodawanie, odejmowanie i kwadrat, łańcuchowo połączone w sekwencji. Przejście do przodu ocenia ten graf od lewej do prawej, obliczając i pamiętając każdą pośrednią wartość. Przejście wsteczne następnie przechodzi tym samym grafem od prawej do lewej, mnożąc lokalne pochodne, aby zgromadzić wkład każdego węzła w całkowity gradient funkcji straty względem każdego wejścia – to prawostronne mnożenie jest dosłownie zasadą łańcuchową, wykonywaną dla każdej krawędzi.
przede wszystkim: m = a*w s = m+b e = s-y L = e^2 wstecz (założenie dL/dL = 1): dL/de = 2e dL/ds = dL/de * de/ds = 2e dL/dm = dL/ds * ds/dm = 2e dL/dw = dL/dm * dm/dw = 2e * a dL/da = dL/dm * dm/da = 2e * w Gdy wartość wpływa na więcej niż jeden węzeł znajdujący się poniżej – ciężar w sieci warstwy, ponownie użyte wejście w wielu miejscach – jego gradient jest sumą wkładów płynących z powrotem od każdego węzła, który go użył. To jest bezpośrednio widoczna wielowymiarowa zasada łańcuchowa w księgowaniu i to jest szczegół, który sprawia problemy implementacjom od podstaw, jeśli zostanie pominięty.
forward: m = a*w s = m+b e = s-y L = e^2 backward (seed dL/dL = 1): dL/de = 2e dL/ds = dL/de * de/ds = 2e dL/dm = dL/ds * ds/dm = 2e dL/dw = dL/dm * dm/dw = 2e * a dL/da = dL/dm * dm/da = 2e * w
Od skalarnych do macierzy: Jacobian
Rzeczywiste sieci operują na wektorach i macierzach, a nie pojedynczych liczbach, więc "pochodna" warstwy staje się macierzą Jakobiego J, gdzie Jij = ∂yi/∂xj. Dla w pełni połączonej warstwy y = Wx + b, przydatne gradienty okazują się być iloczynem zewnętrznym i iloczynem macierzowo-wektorowym, a nie czymkolwiek wymagającym jawnego zmaterializowania pełnej macierzy Jakobiego:
dL/dW = delta . x^T (iloczyn zewnętrzny; delta = dL/dy, gradient wsteczny) dL/dx = W^T . delta (przekazywany dalej do poprzedniej warstwy) dL/db = delta (sumowany wzdłuż wymiaru wsadu) Funkcje aktywacji element po elemencie zachowują swój Jacobian diagonalny, skracając się do prostego iloczynu punktowego z gradientem wstecznym. Softmax połączony z funkcją straty entropii krzyżowej jest przyjemnym wyjątkiem, w którym gęsty Jacobian jawnie upraszcza się algebraicznie aż do ŷ − y, dlatego też ta kombinacja jest prawie zawsze używana dla wyjść klasyfikacyjnych zamiast jej obliczania na długo.
dL/dW = delta . x^T (outer product; delta = dL/dy, upstream gradient) dL/dx = W^T . delta (passed further back to the previous layer) dL/db = delta (summed over the batch dimension)
Dlaczego gradienty zanikają — i jak to zatrzymać
Sieć o 20 warstwach, a jej gradient w najwcześniejszej warstwie jest iloczynem około 20 macierzy wag i 20 pochodnych aktywacji, połączonych zgodnie z powyższym mechanizmem. Jeśli każda z tych wartości ma wartość mniejszą niż 1 — co zdarza się przy funkcji aktywacji typu sigmoid, której pochodna nigdy nie przekracza 0,25 — iloczyn geometrycznie maleje: 0,25²⁰ jest rzędu 10⁻¹², zanikający gradient, który sprawia, że najwcześniejsze warstwy są w zasadzie niezdolne do uczenia się. Zwiększenie wartości tych czynników powyżej 1 powoduje, że mnożenie eksploduje zamiast zanikać. Współczesne architektury przeciwdziałają temu na wiele sposobów jednocześnie: aktywacje ReLU, których pochodna wynosi dokładnie 1 dla dowolnej dodatniej wartości wejściowej, unikając całkowitego nasycenia; połączenia resztkowe, które dają gradientowi skróconą ścieżkę wokół całych bloków warstw; normalizacja wsadowa, która utrzymuje pre-aktywacje w dobrze zachowanej skali.
Minimalny silnik autogradacji
Cały mechanizm powyżej – budowa grafu podczas przejścia do przodu i jego przechodzenie w tył mnożąc lokalne pochodne – mieści się w zaskakująco niewielkiej ilości kodu. Każda wartość śledzi własny gradient, swoje wejścia oraz małą zamykarkę, która wie, jak przekazać swój gradient na te wejścia; wywołanie backward() topologicznie sortuje graf i uruchamia każdą zamykarkę w odwrotnej kolejności.
klasa Wartość { konstruktor(dane, dzieci = []) { this.data = dane; this.grad = 0; this._prev = dzieci; this._backward = () => {}; } mnozenie(inne) { const wynik = new Wartość(this.data * inne.data, [this, inne]); wynik._backward = () => { this.grad += inne.data * wynik.grad; inne.grad += this.data * wynik.grad; }; return wynik; } backward() { const topo = [], widoczne = new Set(); (function buduj(v){ if(!widoczne.has(v)){ widoczne.add(v); v._prev.forEach(buduj); topo.push(v); } })(this); this.grad = 1; topo.reverse().forEach(v => v._backward()); } } Jest to automatyczna różniczkowanie w trybie wstecznym, i jest to dokładnie to, co robi autograd PyTorch oraz transformacja grad JAX pod spodem, tylko w znacznie więkskim skali z tensorami zamiast skalarnymi. Tryb wsteczny jest właściwym wyborem dla sieci neuronowych ze względu na to, że funkcja straty ma jeden wektor wyjściowy a miliony parametrów wejściowych – jedna przechodząca w tył obliczenie odzyskuje gradient każdego parametru jednocześnie, podczas gdy automatyczne różniczkowanie w trybie przodu wymagałoby jednego przejścia dla każdej wymiarowości wejścia i opłaca się tylko w przeciwnym przypadku, wiele wyjść i mało wejść.
class Value {
constructor(data, children = []) {
this.data = data; this.grad = 0;
this._prev = children; this._backward = () => {};
}
mul(other) {
const out = new Value(this.data * other.data, [this, other]);
out._backward = () => {
this.grad += other.data * out.grad;
other.grad += this.data * out.grad;
};
return out;
}
backward() {
const topo = [], seen = new Set();
(function build(v){ if(!seen.has(v)){ seen.add(v);
v._prev.forEach(build); topo.push(v); } })(this);
this.grad = 1;
topo.reverse().forEach(v => v._backward());
}
}
Jak symulacja tutaj z niej korzysta
Symulacja propagacji wstecznej na tym koncie trenuje małą wielowarstwową perceptron w czasie rzeczywistym na płótnie, pokazując przekaz przód obliczający aktywacje od lewej do prawej i propagację wsteczną przesyłającą gradienty z prawej do lewej przez te same połączenia, kolorowane według znaku i wielkości. Ponieważ sieć jest wystarczająco mała, aby wyświetlić każdy ciężar i każdy gradient na ekranie, możesz zobaczyć zanikający gradient w czasie rzeczywistym poprzez dodanie dodatkowych warstw lub przełączenie się na nasyconą aktywację, a także obserwować, jak on się rozwiązuje, gdy przełączysz się na ReLU lub dodasz połączenie resztkowe — te same kompromisy, które opisano powyżej, ale widoczne zamiast abstrakcyjne.
Często zadawane pytania
Dlaczego propagacja wsteczna zawsze odbywa się w odwrotnej kolejności, a nie w przód przez sieć?
Ponieważ sieć neuronowa ma miliony parametrów wpływających na pojedynczą wartość skalarową błędu – niewiele wyjść, wiele wejść. Wyprowadzanie różniczkowe wstecz (reverse-mode differentiation) oblicza gradient względem każdego parametru w jednym kroku wstecznym, niezależnie od liczby parametrów. Wyprowadzanie w przód (forward-mode) wymagałoby jednego przejścia dla każdej wymiarowości wejścia, co jest niewyobrażalnie kosztowne w tak dużych sieciach. Jeśli sieć miałaby wiele wyjść i niewiele wejść, wyprowadzanie w przód byłoby tańszą opcją – to właśnie jest kształt typowej funkcji straty.
Co powoduje zanikających gradientów w głębokich sieciach?
Zasada łańcuchowa mnoży lokalny pochodny warstwa po warstwie, gdy gradient jest propagowany wstecz. Jeśli każdy z tych czynników jest niezawodnie poniżej 1 – co zdarza się z nasyconymi aktywacjami, takimi jak sigmoid, którego pochodna nigdy nie przekracza 0,25 – produkt 20 warstw może zostać pomnożony przez czynnik 10^-12 lub więcej przed dotarciem do pierwszej warstwy, co sprawia, że wagi są zasadniczo niezdolne do uczenia się. Aktywacje ReLU, połączenia resztkowe, normalizacja wsadowa i staranne inicjalizowanie wag istnieją specjalnie po to, aby utrzymać ten produkt blisko 1.
Jak sprawdzić, czy ręczne wyprowadzanie wsteczne jest rzeczywiście poprawne?
Sprawdzanie gradientu: porównaj analityczny gradient z backprop z szacunkiem numerycznym z różniczkowania skończonego, [L(w+ε) − L(w−ε)] / (2ε), dla małego ε. Jeśli obie wartości zgadzają się w przybliżeniu do szóstej cyfry znaczących dla każdego parametru, wyprowadzanie wsteczne jest bardzo prawdopodobnie poprawne. Jest to powolne – jeden dodatkowy przejście w przód na każdy parametr – dlatego używane jest tylko do walidacji nowej implementacji, nigdy podczas rzeczywistego treningu.
Wypróbuj na żywo
Wszystko powyżej działa bezpośrednio w Twojej przeglądarce — otwórz the simulation i zmieniaj parametry podczas działania. Nic nie jest instalowane ani przesyłane na serwer, cały model działa w jednej karcie.
▶ Otwórz symulację the simulation