Kurs: sieci neuronowe od środka · Lekcja 11 z 12 · 15 min
Sekwencje: od pamięci RNN do uwagi
W skrócie
RNN czyta po jednym symbolu i niesie pamięć w stanie. Sygnał błędu słabnie z każdym krokiem wstecz, GRU go przepuszcza, a uwaga patrzy wprost na każde słowo.
Rozgrzewka Dwa pytania z kursu ML, na których ta lekcja się opiera. Minuta, a lekcji nie blokuje.
Zdanie, nagranie i kurs akcji to sekwencje: liczy się w nich kolejność. Sieć rekurencyjna (RNN) czyta sekwencję po jednym elemencie. Po każdym kroku zapisuje to, co przeczytała, w stanie h: wektorze kilku liczb. Następny krok dostaje nowy symbol i poprzedni stan. W każdym kroku działają te same wagi. To współdzielenie wag z lekcji 10, tylko w czasie zamiast w przestrzeni.
Wybierz odpowiedź i pewność.
Odpowiedz, żeby odblokować lab.
Pokaż odpowiedź
Poprawna odpowiedź: Blisko 50%, czyli zgadywanie
W tym labie 49,0% ze 400 ciągów testowych, czyli zgadywanie; na każdym z pięciu startów wag poniżej 55%. GRU z tyloma samymi neuronami trafia w 100% na każdym. Krótsze ciągi zwykła RNN pamięta: na pięciu startach od 5 do 15 kroków.
Spróbuj
- Lab startuje przy 10 symbolach. Na tym starcie wag zwykła RNN zgaduje już tutaj (46,8%), choć na 4 z 5 startów pamięta 10 symboli. GRU pamięta. Górny wykres zbiera każdą sprawdzoną długość.
- Cel: znajdź najdłuższy ciąg, który zwykła RNN jeszcze pamięta. Potem idź w drugą stronę i znajdź długość, przy której zawodzi także GRU.
- Dolny wykres pokazuje, ile sygnału błędu z końca ciągu dociera do pierwszego symbolu, zanim sieć zacznie się uczyć. Oś jest logarytmiczna.
Pomysł w prostych słowach
Żeby nauczyć się zadania, sieć musi powiązać odpowiedź na końcu z symbolem na początku. Propagacja wsteczna z lekcji 5 niesie błąd od końca do początku, krok po kroku. W zwykłej RNN każdy krok mnoży ten sygnał przez wagi i przez pochodną tanh, która nie przekracza 1. Po 9 takich mnożeniach do pierwszego symbolu dociera w tym labie 2,5·10⁻³ sygnału. Sieć dostaje słaby sygnał, że liczył się początek, więc uczy się tego dużo wolniej. Sama mała liczba nie przesądza o wyniku: na startach 4 i 5 dociera 1,4·10⁻³ i 2,5·10⁻³, a w 200 krokach RNN uczy się tam 10 symboli bezbłędnie.
GRU (gated recurrent unit) ma bramkę aktualizacji z: liczbę od 0 do 1 dla każdego neuronu. Nowy stan to mieszanka: (1 − z) starego stanu i z nowej propozycji. Gdy z jest blisko 0, stan przechodzi dalej prawie bez zmian, a sygnał błędu wraca tą samą drogą. W labie bramka startuje z przesunięciem −2, więc bez innych sygnałów z ≈ 0,12: „domyślnie zachowuj”. Do pierwszego symbolu dociera wtedy 0,189 sygnału, ok. 76 razy więcej niż w RNN. LSTM robi to podobnie: stan jej komórki pamięci zmienia się przez dodawanie, a bramka zapominania decyduje, ile z niego zostaje.
Dlaczego to działa
W zwykłej RNN sygnał w kroku 1 to iloczyn 9 czynników, gdy ciąg ma 10 symboli. Zwykle każdy jest mniejszy od 1, więc iloczyn maleje wykładniczo z długością ciągu. To to samo zanikanie gradientu co w głębokiej sieci z lekcji 7: ciąg 10 symboli działa jak 10 warstw z tymi samymi wagami. GRU daje gradientowi drogę z czynnikiem 1 − z, bliskim 1, jak skrót w sieci rezydualnej.
Granica jest też w GRU, a jej miejsce zależy od długości treningu. Lab uczy każdą sieć przez 200 kroków Adama. W tym labie GRU pamięta bezbłędnie do 30 kroków, a przy 50 i 80 zgaduje (47,8% i 52,8%). Na pięciu startach wag, przy 200 krokach treningu, GRU pamięta bezbłędnie do 20 kroków, a przy 30 trafia co najmniej w 90%. Przy 80 krokach nie pamięta na 4 z 5 startów. Zwykła RNN od 20 kroków nie pamięta na żadnym.
Przy 1000 krokach treningu granice się przesuwają: zwykła RNN pamięta 10 symboli na 5 z 5 startów, a 20 na 2 z 5; GRU pamięta 50 symboli na 5 z 5 startów, a 80 na 3 z 5. Słaby sygnał nie zamyka drogi, tylko ją wydłuża. GRU wciąż sięga dalej, bo jej sygnał słabnie wolniej.
Uwaga: każde słowo patrzy wprost
RNN musi przenieść początek zdania przez stan, krok po kroku. Uwaga (attention) działa inaczej: przy każdym słowie liczy, jak bardzo pasuje do niego każde inne słowo, i bierze ich ważoną średnią. Droga od pierwszego słowa do dziesiątego to jeden krok, a nie 9.
Najpierw słowa muszą stać się wektorami. Osadzenia (embeddings) dają każdemu słowu kilka liczb tak, że słowa z podobnych otoczeń mają podobne wektory. W zabawce poniżej wektory powstały z 48 zdań. Król − mężczyzna + kobieta daje wektor najbliższy słowu „królowa” (cos 0,913). To efekt zdań napisanych równolegle, a nie wiedza o świecie.
Uwaga porównuje wektory iloczynem skalarnym. Każde słowo ma zapytanie (czego szuka) i klucz (co oferuje). Wyniki zapytania z kluczami po softmaksie z lekcji 4 to wagi uwagi. W zabawce poniżej wagi ustawiono ręcznie, bez treningu. W zdaniu „Kot zjadł rybę, bo był głodny” słowo „był” daje 51% uwagi słowu „Kot” i tylko 17% słowu „rybę”, bo szuka rzeczownika męskiego.
Częsty błąd
Prawda czy fałsz: „RNN pamięta całe zdanie, bo czyta je w całości.” Fałsz. RNN czyta po jednym symbolu, a o początku wie tylko to, co przetrwało w stanie. Do pierwszego z 10 symboli dociera w RNN 2,5·10⁻³ sygnału błędu, a w GRU 0,189. Na pięciu startach wag GRU dostaje przy 10 symbolach co najmniej 15 razy więcej sygnału niż RNN. Uwaga omija ten problem, bo każde słowo patrzy bezpośrednio na każde inne.
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
- RNN czyta po jednym symbolu i niesie pamięć w stanie, z tymi samymi wagami w każdym kroku.
- Sygnał błędu słabnie z każdym krokiem wstecz: przy 10 symbolach do początku dociera 2,5·10⁻³ w RNN i 0,189 w GRU.
- Bramka GRU przepuszcza stan i gradient prawie bez zmian, ale i GRU ma granicę długości.
- Granice z labu to granice 200 kroków treningu: przy 1000 krokach obie sieci sięgają dalej, a GRU wciąż dalej niż RNN.
- Uwaga łączy każde słowo z każdym innym w jednym kroku: wagi to softmax iloczynów zapytań z kluczami.
Głębiej: wzór, kod, praktyka
Matematyka
RNN: h_t = tanh(W x_t + U h_(t−1) + b), wyjście o = v · h_T + c.
- Sygnał wstecz:
∂h_t/∂h_(t−1) = diag(1 − h_t²) · U, więc∂h_T/∂h_1to iloczyn T − 1 takich macierzy. Gdy ich normy są mniejsze od 1, iloczyn maleje wykładniczo z T. - GRU:
z = σ(W_z x + U_z h + b_z),r = σ(W_r x + U_r h + b_r),n = tanh(W_n x + U_n (r ⊙ h) + b_n),h_t = (1 − z) ⊙ h_(t−1) + z ⊙ n. Pochodna po starym stanie ma składnikdiag(1 − z), przy z blisko 0 bliski macierzy jednostkowej. Przesunięcie −2 daje σ(−2) ≈ 0,12. - Uwaga:
A = softmax(Q Kᵀ / √d), wynikA V, gdzieQ = E W_Q,K = E W_K,V = E W_V. Każdy wiersz A sumuje się do 1. W zabawce d = 4, więc dzielnik to 2. - Analogia:
cos(a − b + c, w) = (a − b + c) · w / (‖a − b + c‖ · ‖w‖). Wynik to słowo o największym cosinusie spośród innych niż a, b, c.
W kodzie (Python)
Dwie rzeczy z lekcji w numpy: sygnał błędu przez 10 kroków jednego neuronu i jedna głowa uwagi z macierzami zabawki:
import numpy as np
# 1) the error signal from the last of 10 symbols back to the first: 9 multiplications
x = np.array([1.0, -0.5, 0.3, 0.8, -1.0, 0.2, 0.6, -0.3, 0.9, -0.7])
w, h, rnn = 0.9, 0.0, []
for xt in x:
h = np.tanh(w * h + xt) # a one-neuron RNN: the same w at every step
rnn.append(w * (1 - h ** 2)) # dh_t / dh_(t-1) = w * tanh'
z = 0.12 # the GRU update gate near 0 = keep the state
print(f"RNN, signal at step 1: {np.prod(rnn[1:]):.1e}")
print(f"GRU, copy path (1 - z)^9: {(1 - z) ** 9:.3f}")
# 2) self-attention: each word looks at every word in one step (the attention-toy matrices)
words = ["Kot", "zjadł", "rybę", "bo", "był", "głodny"]
E = np.array([[1, 0, 0, 0, 1, 0], [0, 1, .3, 0, 0, 1], [.6, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 0], [0, .7, 0, .2, 0, 1], [0, .8, .3, 0, 0, 1]])
WK = np.zeros((6, 4)); WK[0, 0], WK[1, 2], WK[2, 1], WK[4, 3] = 1.5, 2, 2, 1.5
WQ = np.zeros((6, 4)); WQ[0, 2], WQ[1, 0], WQ[1, 1], WQ[2, 2], WQ[3, 0], WQ[3, 2], WQ[5, 3] = 1.5, 1.5, .5, 1, .5, .5, 1.5
S = (E @ WQ) @ (E @ WK).T / np.sqrt(4)
A = np.exp(S - S.max(axis=1, keepdims=True))
A /= A.sum(axis=1, keepdims=True)
print("'był' looks at:", " ".join(f"{word} {a:.2f}" for word, a in zip(words, A[4])))
print("each row sums to 1:", np.allclose(A.sum(axis=1), 1))
Wynik
RNN, signal at step 1: 2.4e-02
GRU, copy path (1 - z)^9: 0.316
'był' looks at: Kot 0.51 zjadł 0.09 rybę 0.17 bo 0.07 był 0.08 głodny 0.08
each row sums to 1: True
Jeden neuron z wagą 0,9 przepuszcza do pierwszego kroku 2,4·10⁻² sygnału, a sama droga kopiowania GRU 0,316. To inne liczby niż w labie, bo tam jest 8 neuronów i wylosowane wagi, ale kierunek jest ten sam. Wiersz „był” to wagi z zabawki: 0,51 dla „Kot”. 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
- Do tekstu sięga się dziś zwykle po transformer, czyli uwagę (lekcja 12). RNN i GRU mają sens przy strumieniach danych na żywo: koszt jednego kroku jest stały, a uwaga porównuje każdą parę słów.
- Gdy uczysz sieć rekurencyjną, zacznij od GRU albo LSTM, nie od zwykłej RNN. Ustaw obcinanie gradientu (gradient clipping), bo w RNN gradient może też wybuchać.
- Zanim uwierzysz, że model korzysta z dalekiego kontekstu, sprawdź go na sztucznym zadaniu pamięci, jak w labie.
- Osadzenia słów bierz z gotowego modelu wytrenowanego na dużym korpusie. Kilkadziesiąt zdań wystarcza tylko na zabawkę.
Więcej w Atlasie
Hasła encyklopedii o pojęciach z tej lekcji: