Sieć to funkcja, którą można różniczkować
Pozbaczmy metafory biologicznej i sieć neuronowa typu feed-forward to długa łańcuch reakcji: transformacja afiniczna (mnożenie przez macierz współczynników, dodawanie błędu) i nieliniowość zastosowana element po elemencie. Sieć z dwoma ukrytymi warstwami jest niczym
a⁰ = x // wejście z¹ = W¹ a⁰ + b¹ a¹ = φ(z¹) // warstwa ukryta 1 z² = W² a¹ + b² a² = φ(z²) // warstwa ukryta 2 z³ = W³ a² + b³ ŷ = σ(z³) // wyjście L = strata(ŷ, y) // pojedyncza wartość Nieliniowość nie jest ozdobą. Usuń ją i cała konstrukcja zawali się: suma transformacji afinicznych to jedna transformacja afiniczna, a setna warstwa ma dokładnie tę samą ekspresyjną moc co jedna. To φ daje ci wszystko.
Trenowanie polega na znalezieniu wag, które minimalizują L. L jest funkcją skalarnej o kilku tysiącach lub miliardach parametrów, więc naturalnym narzędziem jest metoda gradientu – delikatnie przesuwaj każdy parametr w kierunku jego pochodnej cząstkowej względem L. Jedynym pytaniem jest jak uzyskać miliony tych pochodnych cząstkowych bez wykonywania milionów oddzielnych obliczeń. To właśnie wsteczna propagacja odpowiada na to pytanie.
a⁰ = x // the input z¹ = W¹ a⁰ + b¹ a¹ = φ(z¹) // hidden layer 1 z² = W² a¹ + b² a² = φ(z²) // hidden layer 2 z³ = W³ a² + b³ ŷ = σ(z³) // output L = loss(ŷ, y) // a single number
Zasada łańcuchowa, zastosowana w sposób ekonomiczny
Można obliczyć ∂L/∂w za pomocą różnic skończonych: zmodyfikuj w, uruchom sieć ponownie i obserwuj zmianę L. Kosztuje to jeden pełny przebieg do przodu dla każdego parametru – niewyjałowicie droga i zawodne obliczeniowo. Zamiast tego, backpropagation stosuje zasadę łańcuchową od tyłu z funkcji strat i oblicza każdy gradient w sieci w jednym przebiegu wstecznym, którego koszt jest zbliżony do kosztu jednego przebiegu do przodu.
Kluczem jest propagowanie jednej wartości pośredniej, konwencjonalnie zapisywanej jako δ – wrażliwości straty na preaktywację z:
δᴸ = ∂L/∂zᴸ = ∇_ŷ L ⊙ σ'(zᴸ) // warstwa wyjściowa δˡ = (Wˡ⁺¹ᵀ δˡ⁺¹) ⊙ φ'(zˡ) // każda wcześniejsza warstwa ∂L/∂Wˡ = δˡ (aˡ⁻¹)ᵀ // gradient wag ∂L/∂bˡ = δˡ // gradient biasów Przeczytaj uważnie środkową linię, ponieważ to jest cały algorytm: aby uzyskać sygnał błędu na warstwie, weź sygnał błędu z warstwy powyżej, przesła go wstecz przez transpozycję macierzy wag tej warstwy i zmodyfikuj go przez pochodną aktywacji tej warstwy. Przebieg do przodu wysyła aktywacje w górę przez W; przebieg wsteczny wysyła błędy w dół przez Wᵀ. Gradient dla każdego ciężaru jest wtedy po prostu iloczynem zewnętrznym przychodzącego sygnału błędu na jego wyjściu i aktywacji, która weszła do jego wejścia.
Wnioski są następujące. Po pierwsze, musisz buforować aktywacje z przebiegu do przodu – przebieg wsteczny potrzebuje każdego aˡ⁻¹ i każdej zˡ. Dlatego też szkolenie głębokiej sieci kosztuje znacznie więcej pamięci niż jej uruchomienie. Drugi, backpropagation nie jest algorytmem uczenia się; to obliczenie gradientu. Uczenie się odbywa się przez optymalizator, który konsumuje te gradienty.
δᴸ = ∂L/∂zᴸ = ∇_ŷ L ⊙ σ'(zᴸ) // output layer δˡ = (Wˡ⁺¹ᵀ δˡ⁺¹) ⊙ φ'(zˡ) // every earlier layer ∂L/∂Wˡ = δˡ (aˡ⁻¹)ᵀ // gradient of the weights ∂L/∂bˡ = δˡ // gradient of the biases
Aktywacje i zanikający gradient
Sigmoid, φ(z) = 1/(1 + e^(-z)), był klasycznym wyborem, a jego pochodna jest przyczyną, dla której głębokie sieci przez lata uważano za nieuczalne. Ta pochodna to φ(z)(1 − φ(z)), która ma maksymalną wartość 0,25 przy z = 0 i spada do prawie zera, gdy neuron się saturuje. Każdy warstwa backpropagation mnoży sygnał błędu przez jeden z tych czynników — więc przez dziesięć warstw sigmoid gradient jest przeskalowany najwyżej o 0,25¹⁰ ≈ 10⁻⁶ nawet w najlepszym przypadku. Jest to problem zanikającego gradientu: wczesne warstwy otrzymują zasadniczo bez sygnału i nigdy nie uczą się.
φ(z) = 1/(1+e^(-z)) φ'(z) = φ(1−φ) ∈ (0, 0.25] saturuje φ(z) = tanh(z) φ'(z) = 1 − tanh²z ∈ (0, 1] zero-centered φ(z) = max(0, z) φ'(z) = 1 if z > 0 ∈ {0, 1} ReLU else 0 może umrzeć φ(z) = max(αz, z) φ'(z) = 1 or α lekkie ReLU, α ≈ 0.01 ReLU rozwiązuje to brutalną siłą: dla jakiegokolwiek dodatniego wejścia jego pochodna jest dokładnie 1, więc sygnał błędu przechodzi niezmieniony bez względu na to, ile warstw go przecina. Jego własna patologia to umierający ReLU — jednostka pchnięta trwale ujemnie ma zerowy gradient na zawsze i nigdy nie może się z niego wyleczyć — które lekkie ReLU i jego krewni łagodzią, dając negatywnej stronie mały nachylenie.
φ(z) = 1/(1+e^-z) φ'(z) = φ(1−φ) ∈ (0, 0.25] saturates
φ(z) = tanh(z) φ'(z) = 1 − tanh²z ∈ (0, 1] zero-centred
φ(z) = max(0, z) φ'(z) = 1 if z > 0 ∈ {0, 1} ReLU
else 0 can die
φ(z) = max(αz, z) φ'(z) = 1 or α leaky ReLU, α ≈ 0.01
Inicjalizacja nie jest szczegółem
Zainicjuj wszystkie wagi zerami, a każda neuronka w warstwie oblicza to samo, otrzymuje ten sam gradient i pozostaje identyczna na zawsze: sieć nigdy nie łamie symetrii i zachowuje się jak pojedyncza jednostka na warstwę. Dlatego wagi muszą być losowe. Ale ich skala ma tak samo duże znaczenie, ponieważ wariancja sygnału jest mnożona warstwa po warstwie – zbyt duża powoduje wybuchające aktywacje, a zbyt mała sprawia, że upadają do zera. Dwa standardowe schematy utrzymują tę wariancję na poziomie 1:
Xavier / Glorot Var(W) = 2 / (fan_in + fan_out) for tanh, sigmoid
He / Kaiming Var(W) = 2 / fan_in for ReLU
(the factor 2 compensates for ReLU zeroing half
the activations, which halves the variance)
Od gradientów do uczenia się
Z gradientami w ręku, zasada aktualizacji jest ostatnią decyzją. Prosta stochastyczna regresja gradientu (SGD) wykonuje krok w kierunku przeciwnym do gradientu mini-partii. Momentum akumuluje wykładniczo zanikającą średnią z przeszłych gradientów, co tłumi oscylacje na stromych ścianach doliny i przyspiesza jej dno. Adam utrzymuje bieżące szacunki pierwszej i drugiej chwili gradientu oraz dzieli przez pierwiastek kwadratowy z drugiego – nadaje każdemu parametrowi własną efektywną szybkość uczenia się.
SGD w -= lr * g Momentum v = μ*v + g ; w -= lr * v μ ≈ 0.9 Adam m = β₁*m + (1−β₁)*g β₁ ≈ 0.9 v = β₂*v + (1−β₂)*g² β₂ ≈ 0.999 (koryguje bias m̂, v̂ przez 1−βᵗ) w -= lr * m̂ / (sqrt(v̂) + ε) ε ≈ 1e-8 Szybkość uczenia się pozostaje jedynym najważniejszym hiperparametrem: zbyt duża powoduje rozpad lub oscylacje straty, zbyt mała zatrzymuje trening na płaskim dnie. A gdy sieć w ogóle nie uczy się, pierwszym co należy sprawdzić, to nie architektura, ale same gradienty – porównaj kilka analitycznych gradientów z centralną różnicą skończoną, (L(w+ε) − L(w−ε)) / 2ε, na małej sieci z podwójną precyzją. Implementacja wstecznej propagacji z błędem znaku lub pominiętym transpozycją wygląda identycznie jak źle dostrojona, a tylko sprawdzanie gradientów rozróżnia obie.
SGD w -= lr * g
Momentum v = μ*v + g ; w -= lr * v μ ≈ 0.9
Adam m = β₁*m + (1−β₁)*g β₁ ≈ 0.9
v = β₂*v + (1−β₂)*g² β₂ ≈ 0.999
(bias-correct m̂, v̂ by 1−βᵗ)
w -= lr * m̂ / (sqrt(v̂) + ε) ε ≈ 1e-8
Często zadawane pytania
Czy wsteczna propagacja jest to samo co zejście gradientu?
Nie. Wsteczna propagacja oblicza gradient funkcji straty względem każdego wagi, w jednym przejściu wstecznym kosztującym około tyle, ile jeden przejazd do przodu. Z kolei zejście gradientu (lub SGD, Adam, momentum) to osobny krok wykorzystujący te gradienty do aktualizacji wag. Można sparować wsteczną propagację z dowolnym optymalizatorem.
Co to jest problem zanikającego gradientu?
Przejście wsteczne mnoży sygnał błędu przez pochodną aktywacji na każdym warstwie. Pochodna funkcji sigmoid nigdy nie przekracza 0,25 i zbliża się do zera, gdy jednostka saturuje, więc przez wiele warstw iloczyn skurcza się do zera, a wczesne warstwy przestają uczyć się. ReLU unika tego problemu, ponieważ jego pochodna wynosi dokładnie 1 dla dodatnich wartości.
Dlaczego nie mogę zainicjalizować wszystkich wag zerami?
Ponieważ wtedy każda neuron w warstwie oblicza ten sam wynik i otrzymuje ten sam gradient, więc pozostają one identyczne na zawsze – warstwa nigdy nie łamie symetrii i zasadniczo zawiera jedną jednostkę. Wagi muszą być losowe i przeskalowane (Xavier dla tanh, He dla ReLU), aby wariancja sygnału nie eksplodowała ani nie uległa kolapsowi na kolejnych warstwach.
Wypróbuj na żywo
Wszystko powyżej działa bezpośrednio w Twojej przeglądarce — otwórz Neural Network — Backpropagation 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ę Neural Network — Backpropagation