3.5.6 · D3Sequence Models

Worked examples — Bidirectional RNNs

3,247 words15 min readBack to topic

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.

Figure — Bidirectional RNNs

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).