3.5.12 · D5Sequence Models

Question bank — Handling variable-length sequences

2,732 words12 min readBack to topic

This is a stress-test for your understanding of handling variable-length sequences. Each line below is a Question ::: Answer reveal. Cover the answer, commit to a response and a reason, then check. If your reason is wrong even when your yes/no is right, you found a real gap.

Before we start, one shared vocabulary reminder so nothing here is a mystery.

The recurring trap of this whole topic is this: padding is a storage convenience, but the maths must behave as if the padding were never there. Almost every item below is a variation on that one idea.

The two figures below carry the visual intuition that the reveals refer back to — look at them first.

Figure 1 — what padding and masking actually look like on a batch.

Figure — Handling variable-length sequences

Each row is one sequence, padded up to . Green cells are real tokens (), gray cells are pads (). The mask on the right is the exact same grid written as s and s — that is all a mask is.

Figure 2 — why , not , is the correct mask for softmax attention.

Figure — Handling variable-length sequences

The left bars are raw attention scores over three keys where the third is a pad. The middle panel shows the wrong fix (multiply the score by ): after softmax the pad still steals weight. The right panel shows the correct fix (add before softmax): the pad's weight collapses to zero and the two real weights renormalise to sum to . Keep this picture in mind for SE3, SE4 and EC6.


True or false — justify

TF1. "Padding changes what the model outputs at the real (non-pad) positions."
False if masking is done correctly. Pads exist only to make the tensor rectangular; a correct mask (Figure 1) zeroes their contribution to loss and attention, so real positions get identical results with or without them.
TF2. "A longer sequence should always have a larger loss than a shorter one."
False. Loss quality is per-token. We divide by the mask sum (the true length) so loss is a per-token average — a 3-word sentence and a 40-word one are compared fairly, not penalised by length.
TF3. "Attention needs padding baked into its formula to work on variable lengths."
False. For a single sequence of true length , the sum — where is the value vector at position and its softmax weight — already runs over exactly its real tokens. Padding only enters when we batch (raising the row length to ), and we then neutralise it with a mask (Figure 2), never inside the core formula. See Attention Mechanism.
TF4. "Packing a sequence with pack_padded_sequence gives a different final hidden state than running the padded sequence through the RNN."
False (with correct masking). Packing is a speed optimisation that skips computing over pads; the RNN visits exactly the same real tokens in the same order, so the hidden states at real positions match. See Recurrent Neural Networks.
TF5. "Using as the pad token is safe because zeros don't affect a weighted sum."
False. A zero input still produces a non-zero hidden state through the bias and , and a zero embedding can still receive attention weight. Zeros are harmless only when a mask explicitly removes them — the value alone is not a shield.
TF6. "In self-attention, masking only matters for the keys, not the queries."
Half-true, context-dependent. For ignoring pad positions being attended to (the keys — the positions being looked at), we mask keys. But we usually also skip loss on pad query positions (the positions doing the asking). Both matter; which one you mask depends on whether the pad is what you look at or what you predict for.
TF7. "Dynamic unrolling and dynamic control flow mean the model has different weights for different lengths."
False. The whole point is parameter sharing: the same are reused at every time step and for every length. "Dynamic" refers to how many steps run, not which weights.
TF8. "Transformers, unlike RNNs, don't need any masking for padding."
False. Transformers still pad to a common length for batching, and still need a padding mask to stop attention from spending weight on pad tokens. They additionally use a causal mask in decoders — masking is arguably more central to them, not less. See Masking in Deep Learning.
TF9. "In multi-head attention the padding mask must be recomputed separately for each head."
False. The padding mask depends only on which positions are pads, not on the head. One mask of shape (batch, key-positions) is broadcast across all heads and all query positions. The Q/K/V (query/key/value) projections differ per head, but they never move a pad token to a non-pad slot, so the same mask is valid everywhere.

Spot the error

SE1. "To pad a batch, I extend every sequence to length 100 (a fixed constant I chose in advance)."
Error: pad to the batch's longest sequence , not a global constant. A fixed 100 wastes memory when the batch's max is 12, and breaks when a sequence exceeds 100. Pad length should be dynamic per batch.
SE2. "My masked loss is ."
Error: dividing by instead of . The correct form is ; dividing by the padded length shrinks short-sequence losses artificially, so the model under-learns from short examples.
SE3. "Before softmax I multiply the pad scores by to remove them."
Error: you must add , not multiply by (Figure 2, middle vs right). A score of still gives , a real chunk of attention; only truly removes it while letting the denominator renormalise.
SE4. "I applied softmax first, then zeroed the pad weights afterward."
Error: order is wrong and the result no longer sums to 1. Zeroing after softmax leaves the surviving weights summing to less than 1, silently scaling down the output. Mask the scores before softmax so normalisation still divides by the correct total (Figure 2, right).
SE5. "I packed my batch but forgot to sort sequences by length — PyTorch's older API happily accepted it."
Error: classic pack_padded_sequence (without enforce_sorted=False) assumes descending length order. Unsorted input scrambles batch_sizes, so the RNN pairs the wrong tokens with the wrong hidden states.
SE6. "I masked the loss but let padding flow through the RNN and used the last time step's hidden state as my sentence summary."
Error: the "last" step for a padded sequence is a pad step. You must gather the hidden state at index (the last real token), or the summary is contaminated by pads.
SE7. "Bidirectional RNN over a padded batch — I just read left-to-right and right-to-left as normal."
Error: the backward pass starts at the padding. Without masking/packing, the reverse direction begins by encoding pad tokens into the real ones. Bi-RNNs need packing or careful masking on both directions. See LSTM and GRU.
SE8. "To be safe I used the literal float('-inf') for my mask value in half-precision (fp16)."
Error: on GPUs/TPUs a literal produces NaN the moment a row is fully masked (softmax of all is ), and also breaks fp16 arithmetic elsewhere. Practitioners use a large finite negative number instead — e.g. add roughly in fp32 or the smallest finite fp16 value — so underflows to without ever forming a true .

Why questions

WHY1. Why do we bother padding at all instead of feeding each sequence one at a time?
Because hardware (GPUs) is fastest on rectangular blocks processed in parallel. One-at-a-time is correct but slow; padding lets a whole batch flow through together.
WHY2. Why divide the masked loss by the mask sum rather than a constant?
So the loss is a genuine per-real-token average. The mask sum equals the true length , making every sequence contribute on equal footing regardless of how much padding it carries.
WHY3. Why does attention scale scores by , and how does this relate to variable lengths?
Here is the query/key vector dimension (defined above). Scaling keeps the score variance near so softmax doesn't saturate — it depends on the feature dimension , not on the length. And that is exactly the point for variable lengths: because the stabiliser is length-free, the same attention block behaves identically whether or , so no per-length retuning is needed. (Length only ever enters through masking, never through the scaling.)
WHY4. Why is packing a "memory and compute" win, while dynamic unrolling is only a compute win?
Packing physically stores no pad values (less memory) and skips computing over them (less compute). Dynamic unrolling still holds the padded tensor in memory; it merely avoids stepping past each sequence's true end.
WHY5. Why can <PAD> sometimes get a learned embedding, yet we still must mask it?
A learned pad embedding is convenient for a uniform lookup table, but it still carries a real vector that attention/loss would otherwise use. The embedding's existence doesn't excuse it from being masked out of the maths.
WHY6. Why does attention have quadratic complexity in length while an RNN is linear?
Attention compares every position with every other ( scores), giving . An RNN walks the sequence once, one step per token, giving . This trade-off is central when choosing between seq2seq RNNs and transformer stacks.
WHY7. Why do we prefer masking over simply removing pad rows from the tensor mid-computation?
Removing rows makes the tensor ragged again, defeating the rectangular-batch speed win. Masking keeps the shape fixed while neutralising the pads' effect — best of both worlds.

Edge cases

EC1. What is the mask, loss, and RNN behaviour for a sequence of length in a batch padded to ?
Mask is ; loss divides by (only the real token counts); the RNN produces one meaningful hidden state and 49 pad steps that must be masked/packed away.
EC2. What happens if an entire sequence is empty ()?
The mask sum is , so the per-sequence loss divides by zero. Handle it explicitly — drop empty sequences before batching, or clamp the denominator to — never let a zero-length row reach the loss.
EC3. If every sequence in a batch has the same length ( for all ), do we still need masks?
No padding means no pad positions, so the mask is all s and does nothing. Masking is harmless but redundant here — the machinery only earns its keep when lengths differ.
EC4. A batch has one length-500 sequence among many length-5s. What does padding cost, and how does bucketing help — with numbers?
Padding forces everyone to : a batch of 32 stores token-slots for only real ones — about 96% wasted. Bucketing groups similar lengths so the length-5s pad only to ( slots, near-zero waste) and the length-500 sits in its own bucket. Trade-off: buckets that are too fine hurt batch parallelism and shuffling randomness, so you tune bucket width to balance pad-waste against throughput. See Batch Processing.
EC5. In a causal (autoregressive) decoder, a position is masked from the future and from padding. What does the combined mask allow at the last real token?
It permits attention to all earlier real tokens, forbids all future positions, and forbids all pad positions. Concretely, the combined mask is the intersection of two constraints — the lower-triangular causal mask (no looking ahead) and the padding mask (no looking at pads) — so a position may attend only where both say "allowed". See Masking in Deep Learning.
EC6. If a query position is itself a pad token, does masking its keys make its output valid?
No — its output is meaningless regardless, because the query vector is a pad. We simply don't use the outputs at pad query positions (loss is masked there), so their garbage never propagates.
EC7. Attention over a sequence where all keys are masked (a fully-pad row) — what does softmax return, and how do frameworks survive it?
Softmax of all is NaN (Figure 2 shows why the denominator must stay positive). Safeguards: keep at least one unmasked position, add a finite large-negative instead of true , or special-case fully-pad rows to output zeros. This is the same NaN trap as SE8.
Recall One-line summary to lock in

Padding is a shape trick; masking is the promise that the shape trick never changes the answer. Answer ::: Every trap on this page is a case where padding was allowed to leak into the maths — the fix is always to mask (add / weight by the mask / divide by the true length ), never to trust that -valued pads are automatically harmless.