Recurrent Neural Networks (RNN): Explained Visually
A Recurrent Neural Network (RNN) is a neural network that reads data one step at a time and keeps a small memory of what it has seen so far. That memory is called the hidden state. Because of it, an RNN can understand order: it knows that "dog bites man" and "man bites dog" are different. RNNs were the first neural networks that worked well for text, speech and time series.
- Why normal neural networks cannot handle sequences
- How an RNN carries memory with a hidden state
- The RNN equation, with a worked example
- One-to-many, many-to-one and many-to-many RNNs
- Backpropagation through time, and why gradients vanish or explode
- How to fix it: clipping, LSTM and GRU
Contents
Why do we need RNNs?
Many kinds of data are sequences, where order matters:
- Text: words in a sentence
- Speech: sound frames over time
- Finance: daily stock prices
- Sensors: temperature every minute
A normal feed-forward neural network has three problems with sequences:
- Fixed input size. It expects exactly, say, 10 inputs. Sentences can have 3 words or 300.
- No sense of order. It treats input 1 and input 7 as unrelated features.
- No sharing. It learns separately what "good" means at position 2 and at position 9.
An RNN fixes all three with one idea: use the same small network at every step, and pass a memory from one step to the next.
When you read a story, you do not forget page 1 when you reach page 2. You carry a running summary in your head and update it with each new sentence. An RNN does the same thing: the hidden state is its running summary.
How an RNN works
At each time step t, the RNN takes two inputs: the current item xt (for example, a word) and its previous memory ht-1. It mixes them and produces a new memory ht:
If we need an output at that step (for example, the next word), we add:
There are only three weight matrices, and they are reused at every step:
| Weight | Connects | Job |
|---|---|---|
| Wx | input → hidden | How the current word changes the memory |
| Wh | hidden → hidden | How the old memory carries forward |
| Wy | hidden → output | How the memory turns into a prediction |
Unrolling the RNN
It is the same cell used again and again. To see what happens over time, we draw one copy per word. This is called unrolling. Type any sentence below and press Play for the forward pass. The four coloured squares in each cell are the hidden state (mint is positive, pink is negative). Then switch to Backward pass to see the training signal travel back.
In backward mode, drag the Wh size slider down to about 0.5 and press Play. The gradient bars shrink to almost nothing before they reach the first word. Now drag it to 2.0. The gradients grow. This is the vanishing and exploding gradient problem, using real numbers computed in your browser.
A worked example by hand
Let us use a tiny RNN with one number as memory. Let Wx = 0.5, Wh = 0.8 and b = 0. Input sequence: x = [1, 0, 0]. That is, one signal at the start, then nothing.
| Step | Calculation | ht |
|---|---|---|
| t = 1 | tanh(0.5 × 1 + 0.8 × 0) = tanh(0.5) | 0.462 |
| t = 2 | tanh(0.5 × 0 + 0.8 × 0.462) = tanh(0.370) | 0.354 |
| t = 3 | tanh(0.5 × 0 + 0.8 × 0.354) = tanh(0.283) | 0.276 |
The first input is still remembered at step 3, but it fades a bit every step. Over 30 steps it would be close to zero. Keep that in mind; it is the key to the vanishing gradient problem below.
Types of RNN architectures
Read the whole sequence, give one output at the end.
Example: sentiment analysis. "The food was cold and the service was slow" → Negative. Only the final hidden state is used.
Give one input, produce a sequence.
Example: image captioning. One image → "a dog playing in the park". Music generation from a single starting note.
One output for every input.
Example: named entity recognition. "Sundar Pichai visited Delhi" → [PERSON, PERSON, O, LOCATION]. Also part-of-speech tagging.
Read the whole input first, then write a different-length output.
Example: translation. "How are you?" (3 words) → "Aap kaise hain?" (3 words, but they could differ). The encoder RNN summarises, the decoder RNN writes. This design led directly to attention and Transformers.
Training an RNN: backpropagation through time
An RNN is trained like any neural network: compute a loss, then use gradients to adjust Wx, Wh and Wy. Because the network is unrolled over time, the gradient must travel backwards through every time step. This is called Backpropagation Through Time (BPTT).
Each step back, the gradient is multiplied by Wh and by the derivative of tanh (which is at most 1, often around 0.5–0.8). After k steps, the gradient reaching an early word is roughly:
The vanishing and exploding gradient problem
Raising a number to a large power gives one of two results:
for w in (0.5, 0.9, 1.0, 1.5):
print(f"factor {w}: after 10 steps = {w**10:.4f}, after 50 steps = {w**50:.2e}")
factor 0.5: after 10 steps = 0.0010, after 50 steps = 8.88e-16 factor 0.9: after 10 steps = 0.3487, after 50 steps = 5.15e-03 factor 1.0: after 10 steps = 1.0000, after 50 steps = 1.00e+00 factor 1.5: after 10 steps = 57.6650, after 50 steps = 6.38e+08
- Vanishing gradients (factor below 1): early words get almost no learning signal. The RNN cannot learn that "France" at the start predicts "French" at the end.
- Exploding gradients (factor above 1): updates become huge, the loss jumps to NaN, and training breaks.
Explore it yourself:
How to fix it
| Problem | Fix | How it helps |
|---|---|---|
| Exploding | Gradient clipping | Cap the gradient size (for example, at 1.0) before each update |
| Exploding / vanishing | Careful initialisation | Start Wh as an orthogonal or identity-like matrix so the factor is near 1 |
| Vanishing | LSTM or GRU | Gates create a memory path where the gradient is multiplied only by a learned forget gate |
| Both | Truncated BPTT | Backpropagate only through the last 50–100 steps |
| Very long text | Attention / Transformers | Connect every word to every other word directly, with no long chain |
RNN in Python
Forward pass from scratch in NumPy
import numpy as np
np.random.seed(0)
vocab = {"I": 0, "love": 1, "deep": 2, "learning": 3}
x_size, h_size = len(vocab), 3
W_x = np.random.randn(h_size, x_size) * 0.5 # input -> hidden
W_h = np.random.randn(h_size, h_size) * 0.5 # hidden -> hidden (the "memory" weights)
b = np.zeros(h_size)
def one_hot(word):
v = np.zeros(x_size); v[vocab[word]] = 1
return v
h = np.zeros(h_size) # empty memory at the start
for word in ["I", "love", "deep", "learning"]:
h = np.tanh(W_x @ one_hot(word) + W_h @ h + b)
print(f"{word:>9} -> h = {h.round(3)}")
I -> h = [ 0.707 0.732 -0.052]
love -> h = [0.464 0.18 0.069]
deep -> h = [ 0.599 0.591 -0.02 ]
learning -> h = [0.881 0.437 0.533]The final hidden state [0.881 0.437 0.533] is a summary of the whole sentence. A classifier on top of it could decide the sentiment.
Using nn.RNN in PyTorch
import torch
import torch.nn as nn
rnn = nn.RNN(input_size=50, hidden_size=64, batch_first=True)
x = torch.randn(8, 20, 50) # 8 sentences, 20 words each, 50 numbers per word
output, h_n = rnn(x)
print(output.shape) # hidden state at every step
print(h_n.shape) # final hidden state only
print(sum(p.numel() for p in rnn.parameters()))
torch.Size([8, 20, 64]) torch.Size([1, 8, 64]) 7424
The parameter count is 64×50 (Wx) + 64×64 (Wh) + 64 + 64 (two biases) = 7,424. The same weights are reused for all 20 words, so a longer sentence does not add parameters.
RNN vs feed-forward network vs LSTM
| Feature | Feed-forward NN | RNN | LSTM |
|---|---|---|---|
| Input length | Fixed | Any | Any |
| Memory of past inputs | None | Hidden state | Cell state + hidden state |
| Weights shared over time | No | Yes | Yes |
| Long-range memory | — | Weak | Strong |
| Training speed | Fast (parallel) | Slow (step by step) | Slower (4× the maths) |
Applications of RNNs
- Next-word prediction on mobile keyboards
- Speech recognition and voice assistants
- Time-series forecasting: sales, weather, energy demand
- Music generation note by note
- Handwriting recognition from pen strokes
- Video understanding, frame by frame
- An RNN processes a sequence one step at a time and keeps a hidden state as memory.
- The core equation is ht = tanh(Wxxt + Whht-1 + b), and the same weights are used at every step.
- Training uses Backpropagation Through Time. Repeated multiplication makes gradients vanish (below 1) or explode (above 1).
- Fixes: gradient clipping, good initialisation, truncated BPTT and, most importantly, LSTM/GRU gates.
- Transformers later replaced RNNs for most NLP, but RNN ideas are still asked in nearly every ML interview.
Test yourself
Score: 0 / 51. What does the hidden state of an RNN represent?
2. An RNN with hidden_size=64 reads a sentence of 10 words, and then one of 100 words. How do its parameters change?
3. The gradient factor per step is 0.6. Roughly how much signal reaches a word 20 steps back?
4. Which is the best simple fix for exploding gradients?
5. Sentiment analysis of a movie review is which RNN type?
Frequently asked questions
What is the difference between an RNN and a CNN?
A CNN looks for local patterns with sliding filters and is best for images and grids. An RNN processes data in order and carries memory forward, so it suits sequences such as text, speech and time series. CNNs can also be used on text, but they do not have a running memory.
Why does an RNN use tanh instead of ReLU?
tanh keeps the hidden state between -1 and 1, so the memory cannot grow without limit as it passes through many steps. ReLU is unbounded, which can make values and gradients explode in a recurrent loop unless weights are initialised very carefully.
What is backpropagation through time?
It is normal backpropagation applied to the unrolled RNN. The loss gradient flows backwards from the last time step to the first, and the gradients for the shared weights are added up across all steps.
Are RNNs still used today?
Plain RNNs are rarely used in production. LSTM and GRU are still used for time series, speech and small on-device models. New "linear RNN" and state-space models (such as Mamba) bring back recurrent ideas to make long-context models faster.
What is a bidirectional RNN?
Two RNNs read the sequence, one forwards and one backwards, and their hidden states are joined. Each word then sees context from both sides. It is useful when the whole sequence is available, such as in tagging and classification.
Real interview walkthroughs and AI explained simply on our YouTube channel.