Visualize how memory is passed forward to perform Sentiment Analysis (a Sequence-to-Vector task).
Standard neural networks have a major limitation: they have no memory of the past. They process each input on its own. This is a problem for language, where the order of words is important. For example, "dog bites man" and "man bites dog" use the same words but have fully different meanings.
Recurrent Neural Networks (RNNs) solve this by adding a memory loop. As an RNN reads a sentence word by word, it passes a summary of what it has seen so far to the next step. This summary is called the hidden state. At each timestep the cell takes two inputs. The current word's embedding and the previous hidden state. And produces an updated hidden state through a learned transformation (a matrix multiply followed by a tanh). The same weights are shared across every timestep, so the network learns one rule for "how to update memory given a new word". Rather than a separate rule per position. The final hidden state, which has seen every word in order, is usually used as the sentence representation for downstream tasks like classification or translation. The limitation is that the hidden state is a fixed-size vector carrying the entire history. So data from early words gets step by step overwritten. The [vanishing gradient](../deep_learning/vanishing_vs_exploding_gradient.html) problem, which [LSTMs](what_is_lstm.html) and GRUs were designed to fix. This page unrolls the RNN step by step, shows the hidden state evolving as each word is read, and lets you inspect what the network remembers at each position.Visualize how memory is passed forward to perform Sentiment Analysis (a Sequence-to-Vector task).
Discover how Recurrent Neural Networks (RNNs) use an internal memory loop to understand the order and context of words in a sentence.
Standard neural networks have a major limitation: they have no memory of the past. They process each input on its own. This is a problem for language, where the order of words is important. For example, "dog bites man" and "man bites dog" use the same words but have fully different meanings.
Recurrent Neural Networks (RNNs) solve this by adding a memory loop. As an RNN reads a sentence word by word, it passes a summary of what it has seen so far to the next step. This summary is called the hidden state. At each timestep the cell takes two inputs. The current word's embedding and the previous hidden state. And produces an updated hidden state through a learned transformation (a matrix multiply followed by a tanh). The same weights are shared across every timestep, so the network learns one rule for "how to update memory given a new word". Rather than a separate rule per position. The final hidden state, which has seen every word in order, is usually used as the sentence representation for downstream tasks like classification or translation. The limitation is that the hidden state is a fixed-size vector carrying the entire history. So data from early words gets step by step overwritten. The [vanishing gradient](../deep_learning/vanishing_vs_exploding_gradient.html) problem, which [LSTMs](what_is_lstm.html) and GRUs were designed to fix. This page unrolls the RNN step by step, shows the hidden state evolving as each word is read, and lets you inspect what the network remembers at each position.The visualization above "unrolls" the RNN loop, showing it as a sequence of the same cells, one for each word (or "time step"). Here’s how the data flows:
A important concept in RNNs is shared weights. The same set of calculations (the same "brain") is used at every single time step. This is incredibly efficient. Instead of learning a new set of rules for the first word, second word, etc., the RNN learns a single set of rules for how to update its memory based on a new word and its previous memory. The "Parameters" calculation in the visualization shows the size of these shared weights.
A recurrent network processes a sequence step by step. At each step it combines the current input with what it remembers from before:
hᵗ = tanh(Wₕₕ hᵗ₋₁ + Wₓₕ xᵗ + b)
hₜ is the hidden state — a fixed-size vector summarising all of it seen so far. xₜ is the current word's embedding. The same weight matrices are used at every step, which is parameter sharing along the time axis.
Walking through "the cat sat":
| Step | Input | State contains |
|---|---|---|
| 1 | "the" | A determiner has appeared |
| 2 | "cat" | A noun phrase, subject-like |
| 3 | "sat" | A subject and its verb |
Each state is a function of the current word and the previous state. So data propagates forward by being repeatedly transformed.
That single design choice — one weight matrix reused at every step — is what lets an RNN handle sequences of any length with a fixed number of parameters.
| Shape | Input → output | Example |
|---|---|---|
| Many-to-one | Sequence → one label | Sentiment classification |
| Many-to-many, aligned | Sequence → sequence of the same length | Part-of-speech tagging |
| Many-to-many, unaligned | Sequence → different-length sequence | Translation (encoder-decoder) |
| One-to-many | One input → sequence | Image captioning |
For classification, use the final hidden state — or better, pool over all states. Since the final one is biased towards the end of the sequence.
For an encoder-decoder, the encoder's final state is the "thought vector" passed to the decoder. That design is exactly where attention was invented. Compressing a whole sentence into one fixed vector is a bottleneck, and letting the decoder look back at every encoder state instead removed it.
Vanishing gradients. Backpropagation through 50 steps multiplies 50 Jacobian factors. If they average below 1, the gradient reaching step 1 is in effect zero. So the network cannot learn that step 1 mattered. Tanh's derivative is at most 1 and usually well below it. So decay is the default.
No parallelism. Step t needs hₜ₋₁, so the steps must run in order. A GPU's thousands of cores sit mostly idle, and training time scales with sequence length. This is a hardware-utilisation problem, and it is the reason transformers displaced RNNs at scale rather than any single quality issue.
The first was addressed by gating (LSTM, GRU). The second was not addressable within the recurrent framework at all — which is why the field moved to attention.
An RNN reads one token at a time and updates one state. Where you take the output from decides whether you have a classifier, a tagger, a generator or a translator. Four architectures from the same loop.
Building on the same setup, the next block shows the left cases.
The following block covers the left operations.
The next block continues with the left demonstrations.
Use the simulation to see how an RNN performs sentiment analysis (classifying a sentence as positive or negative):
RNNs are designed for sequential data like text. Their core feature is the hidden state, a memory that is passed through time. This allows the network to remember previous inputs and understand context. By using shared weights, they can fast process sequences of any length. This makes them a cornerstone of modern Natural Language Handling.
Training an RNN means unrolling it. A 50-word sentence becomes a 50-layer feed-forward network that happens to share weights, and ordinary backpropagation is applied to that unrolled graph.
The gradient for a shared weight is the sum of its gradients from every time step. Since it was used at all of them.
Two practical results:
Memory grows with sequence length, because every step's activations must be stored for the backward pass. Long sequences exhaust memory quickly.
Truncated BPTT is the standard remedy: backpropagate only k steps back (usually 20–50) rather than to the beginning. It bounds memory and compute, at the cost of not learning dependencies longer than k.
Gradient clipping is in effect mandatory here. The same multiplication that vanishes gradients can explode them. And a single large gradient destroys the weights in one step.
import torch.nn as nn
rnn = nn.LSTM(300, 256, batch_first=True) # prefer LSTM/GRU over nn.RNN
out, (h, c) = rnn(packed_input)nn.RNN — the plain version — is almost never the right choice; use an LSTM or GRU. This cost little more and train far better.
Three details that matter with real text:
Pack variable-length sequences. pack_padded_sequence tells the RNN to stop at each sequence's real end. Without it, the final hidden state summarises the padding rather than the sentence.
Sort or bucket by length when batching, so batches contain similar lengths and less padding is wasted.
Consider bidirectionality for classification and tagging — running one RNN forward and another backward and concatenating gives each position both left and right context. Not available for generation or streaming.
Are RNNs still used? Yes, in streaming inference with bounded memory, on-device models, small datasets and classical time-series work. Not for large-scale language modelling.
Why does the same weight matrix apply at every step? Parameter sharing along time. The same reason a convolution shares filters across space. It lets one model handle any length.
How long a sequence can an LSTM handle? Hundreds of steps in practice; thousands is unreliable. Transformers handle far longer, at quadratic cost.
What is the hidden state size? A hyperparameter, usually 128–512. Larger means more memory capacity and more parameters.
Can I stack RNN layers? Yes, and two is usually the practical limit. Deeper stacks are hard to train.
Why did transformers replace them? Parallelism across the sequence and constant path length between positions. Both are structural gains that gating cannot provide.
Say the answer out loud before you reveal it — recalling it is what makes it stick, and rereading it is not.
What does this module say about “Why language needs memory”?
What does this module say about “The Unrolled RNN: Step-by-Step Handling”?
What does this module say about “Shared Weights: The Key to Learning”?
Feed a sentence word by word into a recurrent network and watch the hidden state carry context from each step to the next.