Worked examples — Bidirectional RNNs
This page is the "practice arena" for Bidirectional RNNs. The parent note built the machinery: a forward RNN reading left→right, a backward RNN reading right→left, and a way to glue their hidden states together. Here we drill that machinery against every kind of input it can meet — clean sequences, the two edge timesteps, length-one sequences, all-zero inputs, the difference between concatenation and summation, and one exam-style twist.
Everything is tiny on purpose. All numbers are hand-sized so you can check every arithmetic step yourself (and the Verify blocks re-check them by machine).
Notation refresher (nothing new, just naming the pieces)
We will use these symbols. Each is defined in words first.
- — the input at position , a small list of numbers (a word's embedding). Picture it as an arrow of numbers dropped into the machine at step .
- — when we compare two different sentences in the same example, the parenthesised superscript is just a label naming which sentence (sentence vs sentence ). So means "the input at position 3 in sentence a." The superscript is not a power and never means multiplication.
- — the forward hidden state: the forward RNN's memory after it has read words through . The little right-arrow means "built while moving right."
- — the backward hidden state: memory after reading words down to . Left-arrow = "built while moving left."
- — the length of the sequence (how many words).
- — the hidden-state size: how many numbers make up one hidden state. If the memory is a list of three numbers; if it is a single number. We introduce this name only so we can say "how big is the memory" in one symbol.
- — a squashing function that takes any number and gently bends it into the range to . We use it (not, say, plain addition) because without a squash the memory could blow up to huge values as it accumulates step after step.
- — concatenation: literally lay the two lists end to end into one longer list.
To keep arithmetic by hand possible, every example uses scalar hidden states (), so a "hidden state" is a single number, and the weights are single numbers too. The BiRNN recurrence in scalar form is:
Unless a problem says otherwise, our shared "toy weights" are:
The figure below is the map for the whole page — refer back to it as you work each example.
Figure 1 — the BiRNN wiring over three words (Cells A, B, C at a glance). Alt-text: three input boxes along the bottom; an orange forward chain of circles with rightward arrows (); a teal backward chain of circles with leftward arrows (); and a plum output box above each position fed by both chains.

How to read this figure. The bottom row is the three input words . The orange row is the forward chain: its arrows point rightward, showing memory built left→right (). The teal row is the backward chain: its arrows point leftward (, i.e. ). At each position the plum box on top is the stapled output , fed by both an orange arrow (from below) and a teal arrow. Notice the two chains flow in opposite directions but share the same inputs — that single picture is Cell A (interior), Cell B (leftmost column) and Cell C (rightmost column) all at once.
The scenario matrix
Every worked example below is tagged with a cell from this table. Together the ten examples touch every cell. ("" below just means the size of the input, ignoring its sign — e.g. .)
| Cell | Scenario class | What's tricky about it |
|---|---|---|
| A | Interior timestep, normal input | Both neighbours exist — the "textbook" step |
| B | Left boundary | Forward uses ; backward has already crossed the whole sentence |
| C | Right boundary | Backward uses ; forward has crossed everything |
| D | Length-one sequence | Both directions collapse to a single input; are they equal? |
| E | All-zero / degenerate input | What memory survives when everywhere? |
| F | Negative-valued input | Sign flows through ; check it stays in |
| G | Combine rule: concat vs sum | Same states, two glue rules, different downstream size |
| H | Real-world word problem (NER) | Future context flips a label |
| I | Limiting behaviour (large input size $ | x |
| J | Exam twist: causal vs bidirectional | Why a BiRNN cannot be used to predict the next word |
Example 1 — Interior timestep (Cell A)
Step 1 — compute first (we need the previous memory). . Why this step? depends on ; the forward chain forces us to build memory in order, left to right.
Step 2 — feed and into the cell. . Why this step? This is the actual interior recurrence: past memory () plus fresh input (), squashed.
Verify: the squash means , so it is smaller than the raw input — matches the forecast if you guessed "smaller." Numerically . ✓
Example 2 — Left boundary (Cell B)
Step 1 — forward part. From Example 1, . Why this step? At the previous state is the seed , so the forward memory knows only word 1.
Step 2 — backward part, which must start at . (uses seed ). Why this step? The backward RNN begins at the far right, so we cannot get without walking leftward from .
Step 3 — march backward to , then . . . Why this step? Each leftward step folds one more future word into the backward memory.
Step 4 — staple. .
Verify: the forward number saw 1 word; the backward number (by construction) has absorbed words 3, 2, and 1 — the whole future including the present. That asymmetry at the boundary is exactly why bidirectional is powerful at position 1. ✓
Example 3 — Right boundary (Cell C)
Step 1 — forward, march to . (Example 1). Then . Why this step? At the right boundary the forward memory has crossed the entire past (words 1–3).
Step 2 — backward at . From Example 2, — it used the seed , so it knows only word 3. Why this step? is where the backward RNN starts, so its memory is freshest (least context) here.
Step 3 — staple. .
Verify: mirror image of Cell B — now the forward half carries the whole sentence and the backward half sees one word. Both boundaries are covered. ✓
Example 4 — Length-one sequence (Cell D)
Step 1 — forward. . Step 2 — backward. . Why these steps? With there are no neighbours, so both recurrences reduce to .
Step 3 — but note the general case. They are equal only because our two directions share the same weight values. In a real BiRNN the forward and backward weights are independent, so even at you would generally get .
Verify: with identical toy weights both equal . The forecast is conditionally right — equal here, not equal in general. ✓
Example 5 — Degenerate all-zero input (Cell E)
Step 1 — forward chain. . Then , and . Step 2 — backward chain. Symmetrically . Why this step? , and with zero input every term inside is zero, so the memory can never leave the origin.
Verify: all . This is the sanity anchor: no input, no signal. If your implementation ever produces nonzero states from all-zero inputs and zero bias, you have a bug (likely a nonzero bias). ✓
Example 6 — Negative input, sign through tanh (Cell F)
Step 1 — pre-activation. . Step 2 — squash. . Why these steps? is an odd function: negative in, negative out, but bounded strictly between and .
Verify: keeps the sign of the input yet obeys . Negative inputs are safe — no special case needed. ✓
Example 7 — Concatenate vs sum (Cell G)
Step 1 — concatenation. , a 2-number list. Why? Concatenation keeps past and future memories side by side, so a downstream layer can weight them independently.
Step 2 — summation. , a 1-number value. Why? Summation is cheaper (half the downstream weights) but blends the two memories, so the model can no longer tell "how much came from the future."
Verify: concat length is ; sum length is ; and . Both rules are legitimate; concatenation is the default in LSTM and GRU Cells-based BiRNNs. ✓
Example 8 — Real-world NER: future context flips the label (Cell H)
We model the evidence signal as a scalar: let the embedding of the word after "Jordan" drive the backward state. Use ("dunked" ≈ action/person cue) and ("soon" ≈ travel/place cue). To isolate that future cue we set (the word "Jordan" itself contributes nothing distinguishing). Toy weights throughout, seed .
Step 1 — backward state at position 3. (a) . (b) . Why this step? This is the first future cue the backward RNN encodes, and the two sentences already differ in sign here.
Step 2 — carry the cue leftward into position 2 (using so only the future cue shows). (a) . (b) . Why this step? At position 2 (the word "Jordan") the backward memory now carries an opposite-signed signal for the two sentences. The forward memory at position 2, by contrast, has only read words 1–2 and cannot see the deciding future word at all.
Step 3 — staple to get the two combined states. Suppose the forward halves are (near) identical for both sentences, say (same first two words modelled as a shared cue). Then and . Why this step? This is the vector a downstream classifier actually receives; the only coordinate that differs between the two sentences is the backward one.
Step 4 — read off the decision. The classifier sees (push toward PERSON) for sentence (a) vs (push toward LOCATION) for sentence (b). Same word "Jordan," opposite future, opposite label.
Verify: ; the position-2 backward signals are and — equal magnitude, opposite sign. Their difference is , exactly the separation a forward-only model cannot produce (its position-2 states are identical). This is the NER story of Named Entity Recognition, made numeric. ✓
Example 9 — Limiting behaviour: saturation (Cell I)
Step 1 — pre-activation for each. With and , the value inside is just itself: , , . Why this step? Stripping the recurrence to one step lets us watch alone.
Step 2 — squash each. . . (to machine precision). Why this step? saturates: once past a modest magnitude, multiplying the input by ten barely moves the output — it is already glued to the ceiling of .
Step 3 — read the consequence for learning. Near saturation the slope is almost : at it is about . A nearly-flat slope means gradients passing through this unit nearly vanish — the same vanishing-gradient pressure that motivates the gated cells in LSTM and GRU Cells.
Verify: the three states are strictly increasing yet all pinned below (), and the slope at is tiny (). The memory is bounded no matter how large the input — the whole point of the squash. ✓
Example 10 — Exam twist: why not use a BiRNN for next-word prediction (Cell J)
Step 1 — expand what contains. , and by definition was built from . Why this step? We must see exactly which inputs leak into the output before trusting it.
Step 2 — spot the leak. already contains — the very word we are supposed to predict. Concretely for in a length-5 sentence, the backward state's input set is , which includes index . The model would be "predicting" an answer it has literally read. That is label leakage; the training accuracy is meaningless and it cannot run at test time (the future does not exist yet).
Step 3 — state the rule. BiRNNs require the full sequence available up front (tagging, translation encoders in Encoder-Decoder Architecture, classification). For generation you need a causal (forward-only) model, or the masked self-attention of Transformers.
Verify: the backward state's input set contains index (checked for : ), contradicting the causality any predictor of position must obey. Hence the answer is no. ✓
Recall Self-check
At the last position , which half of has seen only one word? ::: The backward half (it just started, seeded by ). With all-zero inputs and zero bias, what is every hidden state? ::: Exactly , because propagates. Why can't a BiRNN predict the next token? ::: Its backward state already contains , the target — label leakage. Concatenation vs summation: which preserves "how much came from the future"? ::: Concatenation (it keeps the two memories separate).
Related deep contexts: Attention Mechanism, Sequence-to-Sequence Models, and the Hinglish walkthrough 3.5.06 Bidirectional RNNs (Hinglish).