RNN: Sieci z Pamięcią

Pamięć o przeszłości: Większość sieci neuronowych widzi dane jako pojedyncze, niezależne punkty. RNN (Recurrent Neural Networks) są inne – one mają pamięć. Rozumieją, że to, co dzieje się teraz, zależy od tego, co stało się przed chwilą. To idealne rozwiązanie dla tekstu, mowy czy giełdy.

1. Jak działa "Pętla Myślowa"?

W tradycyjnej sieci sygnał płynie tylko do przodu. W RNN sygnał zatacza koło – wynik poprzedniego obliczenia wraca do sieci jako dodatkowa informacja przy następnym kroku.

🕰 Stan Ukryty (Hidden State)

To "notatnik" sieci. Przechodząc przez sekwencję (np. czytając zdanie słowo po słowie), RNN zapisuje w nim najważniejsze fakty. Kiedy dociera do końca zdania, stan ukryty zawiera streszczenie wszystkiego, co sieć "przeczytała".

2. Wizualizacja: Przepływ Informacji w Czasie

Poniżej widzisz, jak informacja wędruje przez kolejne kroki czasowe. Zauważ, jak każdy węzeł przekazuje swój "stan" do następnego.

3. Wady i ograniczenia RNN

Choć idea pamięci jest piękna, klasyczne sieci RNN mają dwa poważne problemy techniczne:

1. Krótkowzroczność (Vanishing Gradient)

Klasyczne RNN bardzo szybko zapominają. Jeśli zdanie jest bardzo długie, informacja z pierwszego słowa "wyparuje", zanim sieć dotrze do kropki. Dlatego rzadko używa się czystych RNN, a częściej ich ulepszonych wersji jak LSTM czy GRU.

2. Brak zrównoleglenia (Szybkość)

Ponieważ krok 2 zależy od kroku 1, sieć nie może obliczać wszystkiego naraz. Musi czekać. To sprawia, że trenowanie RNN na wielkich bazach danych trwa znacznie dłużej niż w przypadku sieci CNN czy Transformerów.

4. Implementacja w Pythonie (PyTorch)

rnn_model.py Python / PyTorch
import torch
import torch.nn as nn

class RNNClassifier(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(RNNClassifier, self).__init__()
        self.hidden_size = hidden_size
        
        # Prosta warstwa rekurencyjna
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
        
        # Warstwa wyjściowa decydująca o wyniku
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        # Inicjalizacja "zerowej" pamięci
        h0 = torch.zeros(1, x.size(0), self.hidden_size)
        
        # out: wyniki dla każdego kroku, _ : ostatni stan pamięci
        out, _ = self.rnn(x, h0)
        
        # Bierzemy tylko wynik z ostatniego kroku czasowego
        out = self.fc(out[:, -1, :])
        return out

5. Matematyka Sekwencji

ht = tanh(Wxhxt + Whhht-1)
Obecna pamięć (ht) to połączenie nowego wejścia (xt) i starej pamięci (ht-1).