Kurs: sieci neuronowe od środka · Lekcja 7 z 12 · 14 min
Głęboka sieć: gdy sygnał nie dociera
W skrócie
Więcej warstw nie znaczy mocniejsza sieć. Bez właściwej budowy sygnał błędu gaśnie, zanim dotrze do pierwszych warstw. Cztery lekarstwa naprawiają to od środka.
Rozgrzewka Dwa pytania z kursu ML, na których ta lekcja się opiera. Minuta, a lekcji nie blokuje.
W lekcji 5 gradient płynął od straty do wag i w każdym węźle mnożył się przez lokalną pochodną. W jednym neuronie to kilka mnożeń. W sieci z 10 warstwami gradient pierwszej warstwy to iloczyn pochodnych wszystkich warstw nad nią. Jeśli każda z tych liczb jest mniejsza od 1, iloczyn maleje wykładniczo. To zanikający gradient: pierwsze warstwy prawie się nie uczą, choć reszta sieci jest gotowa.
Wybierz odpowiedź i pewność.
Odpowiedz, żeby odblokować lab.
Pokaż odpowiedź
Poprawna odpowiedź: Ok. 10%, czyli zgadywanie
10,3%, czyli poziom zgadywania. Tuż po losowaniu wag gradient wag warstwy 1 ma normę 1,4·10⁻³. Ta sama sieć ze skrótami ma gradient 1,65 i po 5 epokach trafia 94,4%.
Spróbuj
- Lab startuje z 10 warstwami bez skrótów: 10,3%. Włącz to, co pozwala tej sieci się uczyć. To cel labu.
- Na górnym wykresie porównaj sygnał wsteczny w warstwie 1 i w ostatniej. Bez skrótów linia opada w stronę wejścia, ze skrótami nie.
- Przesuń suwak na 2 warstwy, potem na 6 i 8. Zwykła sieć traci trafność z każdą warstwą: 79,7%, 28,1%, 13,6%.
- Ustaw 20 warstw: gradient warstwy 1 zwykłej sieci spada do 5,7·10⁻⁶.
Pomysł w prostych słowach
Wyobraź sobie głuchy telefon przez 10 osób, z których każda mówi ciszej niż usłyszała. Na końcu nikt nic nie słyszy. Tak działa droga wstecz w głębokiej sieci bez skrótów. W labie każda warstwa mnoży sygnał wsteczny średnio przez ok. 0,56. Bierze się to z małych wag na starcie i z pochodnej tanh, która nie przekracza 1.
Po 10 warstwach do pierwszej warstwy dociera mniej niż 1% sygnału, który dostała ostatnia warstwa ukryta. Pierwsza warstwa dostaje prawie zerowy gradient, więc jej wagi stoją. Zostają losowe, a reszta sieci próbuje rozpoznawać cyfry z losowego przekształcenia pikseli. Sieć się nie uczy, choć niczego jej nie brakuje poza drogą dla sygnału.
Dlaczego to działa
Lekarstwa są cztery i każde łamie ten iloczyn w innym miejscu.
- ReLU zamiast sigmoidy i tanh. Pochodna sigmoidy nie przekracza 0,25, więc same pochodne 20 warstw dają razem czynnik najwyżej 0,25 do potęgi 20. Przy małych wagach, jak na starcie, gradient traci jeszcze więcej. Duże wagi mogą to zrównoważyć, ale łatwo wtedy o wybuchający gradient albo nasycenie sigmoidy. Pochodna ReLU dla dodatnich wejść wynosi 1.
- Inicjalizacja He. ReLU zeruje mniej więcej połowę wejść, więc wagi muszą być odpowiednio większe, żeby sygnał nie słabł. W widżecie zanikającego gradientu 20 warstw sigmoidy z inicjalizacją Xaviera daje stosunek gradientu pierwszej warstwy do ostatniej 2,4·10⁻¹³, a ReLU z He 2,19.
- Normalizacja (BatchNorm, LayerNorm) po każdej warstwie sprowadza sygnał w przód do stałej skali, więc on nie gaśnie ani nie rośnie. Gradientom w tył zwykle też pomaga, choć tego nie gwarantuje.
- Skróty: warstwa liczy h + F(h) zamiast F(h). Pochodna to 1 + ∂F/∂h, więc nawet gdy ∂F/∂h jest małe, gradient przechodzi przez jedynkę bez strat. W labie gradient warstwy 1 rośnie dzięki skrótom ponad 1000 razy.
Nowoczesne głębokie sieci, w tym transformery, łączą skróty z normalizacją. Bez tych dwóch trudno byłoby wytrenować dziesiątki warstw.
Częsty błąd
Prawda czy fałsz: „Głęboka sieć, która się nie uczy, potrzebuje więcej danych albo więcej epok.” Fałsz. Tu sygnał błędu nie dociera do pierwszych warstw już tuż po losowaniu wag, zanim zobaczą jakiekolwiek dane. Więcej epok prawie nie pomaga. Zwykła sieć z 10 warstwami uczona 20 epok zamiast 5, na pięciu startach wag, ani razu nie przekracza 25%. Ta sama sieć ze skrótami po 5 epokach przekracza 90% na każdym z tych startów. Pomaga zmiana budowy: skróty, ReLU, inicjalizacja, normalizacja.
Quiz
Trzy pytania na sprawdzenie się. Wybierz odpowiedź albo rozwiń odpowiedź pod pytaniem. Z klawiatury: strzałki wybierają, Enter albo spacja zatwierdza.
Najważniejsze wnioski
- Gradient pierwszej warstwy to iloczyn pochodnych wszystkich warstw nad nią; liczby mniejsze od 1 dają iloczyn bliski zera.
- Głęboka sieć bez właściwej budowy uczy się gorzej niż płytka, bo jej pierwsze warstwy stoją.
- Cztery lekarstwa: ReLU, inicjalizacja He, normalizacja i skróty.
- Więcej epok nie naprawi sygnału, który nie dociera; pomaga zmiana budowy.
Głębiej: wzór, kod, praktyka
Matematyka
Sieć bez skrótów: h_{k+1} = tanh(W_k·h_k + b_k). Reguła łańcuchowa: ∂L/∂h_1 = J_1ᵀ·J_2ᵀ· … ·J_{n}ᵀ · ∂L/∂h_{n+1}, gdzie J_k = diag(tanh′(z_k))·W_k.
- Jeśli każdy czynnik zmniejsza normę
c < 1razy, ponwarstwach zostajecⁿ; w labiec ≈ 0,56. - Pochodna sigmoidy
≤ 0,25, więc iloczyn samych pochodnych 20 warstw to najwyżej0,25²⁰. Cały czynnik zawiera jeszcze wagiW_k. W łańcuchu jednej liczby z wagami|w| ≤ 1jest jeszcze mniejszy; duże wagi mogą go podnieść ponad 1: 10 warstw z wagą 6 i biasem −3 daje 57,7. - Wariancja sygnału przez warstwę ReLU o
nwejściach:Var(h_{k+1}) ≈ n · Var(w) / 2 · Var(h_k). Xavier (Var(w) = 2/(n_in + n_out)) daje przyn_in = n_outczynnik 1/2, He (Var(w) = 2/n) czynnik 1. - Skrót:
h_{k+1} = h_k + F(h_k), więc∂h_{k+1}/∂h_k = I + ∂F/∂h_k.
W kodzie (Python)
Skalarny szkic zanikającego gradientu w numpy: jeden sygnał przez 10 warstw z tanh, z jednym wzmocnieniem w na każdej warstwie. Do tego granica dla pochodnych sigmoidy, trzy łańcuchy sigmoid z różnymi wagami i wariancja sygnału przy Xavierze i He:
import numpy as np
# jeden sygnał przez 10 warstw: zwykła h <- tanh(w*h), ze skrótem h <- h + tanh(w*h)
w, depth = 0.58, 10 # 0.58 ≈ sqrt(1/3): typowe wzmocnienie warstwy po inicjalizacji jak w PyTorch
for name, skip in [("plain", False), ("residual", True)]:
h, grad = 1.0, 1.0
for _ in range(depth):
local = w * (1 - np.tanh(w * h) ** 2) # pochodna tanh(w*h) po h
grad *= 1 + local if skip else local # reguła łańcuchowa: mnożymy lokalne pochodne
h = h + np.tanh(w * h) if skip else np.tanh(w * h)
print(f"{name:8s}: signal {h:.3g}, gradient dh10/dh0 = {grad:.3g}")
# pochodne sigmoidy dają razem najwyżej 0.25 ** 20; cały gradient mnoży się jeszcze przez wagi
print(f"sigmoid, 20 layers: derivative factor at most x{0.25 ** 20:.2g}")
sigmoid = lambda z: 1 / (1 + np.exp(-z))
for w, b in [(1, 0), (6, -3), (6, 0)]: # 10 warstw h <- sigmoid(w*h + b), start h = 0.5
h, grad = 0.5, 1.0
for _ in range(10):
s = sigmoid(w * h + b)
grad *= w * s * (1 - s) # waga razy pochodna sigmoidy
h = s
print(f"sigmoid, 10 layers, w={w}, b={b}: dh10/dh0 = {grad:.3g}, signal {h:.3g}")
# wariancja sygnału w 20 warstwach ReLU po 32 neurony: ReLU zeruje połowę, więc warstwa mnoży ją przez n * var_w / 2
n = 32
for name, var_w in [("Xavier", 2 / (n + n)), ("He", 2 / n)]:
print(f"{name:6s}: x{n * var_w / 2:.2f} per layer, x{(n * var_w / 2) ** 20:.2g} after 20 layers")
Wynik
plain : signal 0.00371, gradient dh10/dh0 = 0.00274
residual: signal 10, gradient dh10/dh0 = 2.31
sigmoid, 20 layers: derivative factor at most x9.1e-13
sigmoid, 10 layers, w=1, b=0: dh10/dh0 = 3.48e-07, signal 0.659
sigmoid, 10 layers, w=6, b=-3: dh10/dh0 = 57.7, signal 0.5
sigmoid, 10 layers, w=6, b=0: dh10/dh0 = 1.39e-17, signal 0.997
Xavier: x0.50 per layer, x9.5e-07 after 20 layers
He : x1.00 per layer, x1 after 20 layers
Szkic na jednej liczbie daje ten sam rząd wielkości co prawdziwa sieć w labie: gradient warstwy 1 bez skrótów rzędu 10⁻³, ze skrótami powyżej 1. Ze skrótem sygnał w przód rośnie, bo każda warstwa coś do niego dodaje. Dlatego w praktyce skróty idą w parze z normalizacją. Granica 0,25²⁰ dotyczy samych pochodnych. Łańcuch sigmoid z wagą 1 traci jeszcze więcej niż 0,25¹⁰. Z wagą 6 i biasem −3 sygnał siedzi w środku sigmoidy i gradient rośnie 57,7 razy. Z wagą 6 bez biasu sigmoida się nasyca i zostaje 1,4·10⁻¹⁷. Uruchomione w numpy 2.1.3.
Pobierz notebook (.ipynb). Otwórz go w Jupyterze albo wgraj do Google Colab; brakujące dane pobierze sam.
Dla praktyka
- Głęboka sieć uczy się gorzej niż płytsza? Najpierw sprawdź normy gradientów warstw na pierwszej paczce, jak w labie. Spadek o rzędy wielkości w stronę wejścia to zanikający gradient.
- Standardowy zestaw: ReLU albo GELU, inicjalizacja He (w PyTorch
kaiming_normal_), normalizacja i skróty omijające co jedną lub dwie warstwy. - Gradient rosnący w stronę wejścia to odwrotny kłopot, wybuchający gradient. Pomaga przycinanie normy gradientu i mniejszy krok.
- Zanim dodasz warstwy, sprawdź, czy płytsza sieć już nie wystarcza. Głębokość pomaga dopiero wtedy, gdy sygnał ma którędy płynąć.
Więcej w Atlasie
Hasła encyklopedii o pojęciach z tej lekcji: