Perceiver Resampler Forward Pass
Problem Statement
Implement one multi-head Perceiver Resampler block: a fixed set of learned latent tokens cross-attends to image features, with the latents themselves concatenated onto the keys and values, followed by an output projection and a residual connection.
Background
Flamingo's Perceiver Resampler turns a variable-length grid of visual features into a fixed number of tokens. It is not plain cross-attention - the paper's block builds keys and values from the image features with the latents appended:
K=[Xf​;L]WK​,V=[Xf​;L]WV​,Q=LWQ​
so the latents can attend to each other as well as to the image, mixing information across the latent set within a single block. The key/value length is therefore n_feats + n_latents, while the query length - and so the output length - stays n_latents.
The rest is standard multi-head attention. Split the d-wide projections into h heads of width d_h = d / h, run scaled dot-product attention per head (scaling by 1/sqrt(d_h), the head width, not the model width), concatenate the heads back to d, apply W_O, and add the residual:
out=Concat(head1​..headh​)WO​+L
Head splitting is a reshape of the last axis into (h, d_h): head i owns columns **id_h ... (i+1)d_h - 1, contiguously.
Your Task
Implement:
def resampler_forward(latents, feats, Wq, Wk, Wv, Wo, num_heads):
Return the (n_latents, d) output as a nested list, every entry rounded to 4 decimals.
Input Format
- latents - nested list, shape (n_latents, d)
- feats - nested list, shape (n_feats, d)
- Wq, Wk, Wv, Wo - nested lists, each shape (d, d), applied on the right (X @ W)
- num_heads - integer dividing d
Output Format
A nested list of n_latents rows of d floats, each rounded to 4 decimals.
Sample
L = [[1.0, 0.0], [0.0, 1.0]]
X = [[1.0, 1.0], [2.0, 0.0]]
I2 = [[1.0, 0.0], [0.0, 1.0]]
print(resampler_forward(L, X, I2, I2, I2, I2, 1))
Output:
[[2.3395, 0.3302], [0.8302, 1.6698]]
Example:
L = [[1.0, 0.0], [0.0, 1.0]] X = [[1.0, 1.0], [2.0, 0.0]] I2 = [[1.0, 0.0], [0.0, 1.0]] print(resampler_forward(L, X, I2, I2, I2, I2, 1))
[[2.3395, 0.3302], [0.8302, 1.6698]]
With identity projections the keys/values are the four rows [[1,1],[2,0],[1,0],[0,1]] - the two features followed by the two latents. Latent 0 scores them at [1,2,1,0]/sqrt(2), softmaxes to [0.2212,0.4486,0.2212,0.1091], mixes the values to [1.3396,0.3302] and adds the residual [1,0] to give [2.3395,0.3302]. Dropping the latents from the key set would change every weight.
Constraints:
- Keys and values come from
concat([feats, latents], axis=0)- features FIRST, latents second - Queries come from the latents only
- Split into
num_headscontiguous slices of the last axis;d % num_heads == 0 - Scale by
1 / sqrt(d / num_heads), the per-HEAD width - Add the residual
latentsAFTER the output projection - Round every entry to 4 decimals and avoid emitting
-0.0
1. Background Knowledge
The Perceiver Resampler is a critical component in Vision-Language Models (VLMs) like Flamingo, designed to bridge the gap between high-dimensional, variable-length visual features and the fixed-length token sequences expected by language models. Unlike standard cross-attention where queries attend only to keys/values from a different source, the Perceiver Resampler introduces a unique mechanism: latent tokens that serve as both queries and part of the key/value sequence. This allows the latents to not only extract information from the image features but also to mix information among themselves, creating a more coherent and context-aware representation.
The core operation is a modified multi-head attention mechanism. In standard attention, Q, K, and V are derived from separate inputs. Here, the queries Q are derived solely from the latent tokens L. However, the keys K and values V are derived from the concatenation of the image features Xf​ and the latent tokens L. This means each latent token attends to all image features and all other latent tokens. The mathematical formulation is: Q=LWQ​,K=[Xf​;L]WK​,V=[Xf​;L]WV​ where [Xf​;L] denotes vertical concatenation. This structure enables the model to refine the latent representations iteratively, making them robust summaries of the visual input.
Understanding scaled dot-product attention is essential. For each head, the attention weights are computed as softmax(dh​​QKT​), where dh​ is the dimension of each head. The scaling factor dh​​1​ prevents the dot products from growing too large, which would push the softmax function into regions with extremely small gradients. Finally, the outputs from all heads are concatenated and projected via WO​, with a residual connection adding the original latents L to the result, ensuring stable gradient flow.
2. Algorithm Approach
The problem requires implementing a single forward pass of a Perceiver Resampler block using nested lists instead of tensor libraries like PyTorch. The approach involves manually simulating matrix operations and attention mechanisms.
- Matrix Multiplication: Implement a helper function for matrix multiplication (A@B) since standard libraries are not available.
- Projection: Compute Q, Kfeats​, Vfeats​, Klatents​, and Vlatents​ using the provided weight matrices WQ​,WK​,WV​.
- Concatenation: Vertically concatenate the feature keys/values with the latent keys/values to form the full K and V matrices.
- Multi-Head Attention:
- Split the projected matrices into num_heads by reshaping the last dimension.
- For each head, compute the scaled dot-product attention: calculate scores, apply softmax, and multiply by values.
- Concatenate the head outputs back into a single matrix.
- Output Projection & Residual: Multiply the concatenated attention output by WO​ and add the original latents matrix.
- Rounding: Round all final values to 4 decimal places.
3. Step-by-Step Strategy
- Helper Functions:
- Create matmul(A, B) to perform matrix multiplication on nested lists.
- Create transpose(A) to transpose a matrix.
- Create softmax(row) to apply softmax to a 1D list, handling numerical stability by subtracting the max value.
- Compute Projections:
- Calculate Q=matmul(latents,WQ​).
- Calculate Kfeats​=matmul(feats,WK​) and Vfeats​=matmul(feats,WV​).
- Calculate Klatents​=matmul(latents,WK​) and Vlatents​=matmul(latents,WV​).
- Concatenate Keys and Values:
- Form K=Kfeats​+Klatents​ (list concatenation of rows).
- Form V=Vfeats​+Vlatents​.
- Note: Q remains separate with shape (n_latents, d).
- Multi-Head Attention Loop:
- Determine head dimension dh​=d/num_heads.
- Initialize an empty list for the final attention output.
- For each head i from 0 to num_heads−1:
- Extract head-specific slices from Q, K, and V. For a matrix M, head i corresponds to columns i⋅dh​ to (i+1)⋅dh​.
- Compute scores: S=matmul(Qhead​,transpose(Khead​)).
- Scale scores: Multiply each element in S by 1/dh​​.
- Apply softmax row-wise to S to get attention weights A.
- Compute head output: Ohead​=matmul(A,Vhead​).
- Store Ohead​.
- Concatenate Heads:
- Merge the outputs from all heads horizontally. If head i output is Oi​, the combined output for row j is [O0​[j],O1​[j],…].
- Final Projection and Residual:
- Compute out=matmul(combined_heads,WO​).
- Add the original latents to out element-wise.
- Round every element to 4 decimal places.
4. Common Pitfalls
- Incorrect Concatenation: Ensure keys and values are concatenated vertically (adding rows), not horizontally. The sequence length becomes nfeats​+nlatents​.
- Head Splitting Logic: When splitting into heads, ensure you are slicing the columns of the projected matrices, not the rows. Each head processes a subset of the feature dimensions.
- Scaling Factor: Remember to scale by 1/dh​​, where dh​ is the head dimension, not the total model dimension d.
- Softmax Stability: Always subtract the maximum value in each row before applying the exponential function in softmax to prevent overflow.
- Residual Connection: Do not forget to add the original latents to the final output. The problem specifies a residual connection.
- Rounding: Apply rounding only at the very end, after all computations are complete, to avoid cumulative precision errors.
5. Time & Space Complexity
-
Time Complexity:
-
Matrix multiplications dominate the cost. Let NL​ be nlatents​, NF​ be nfeats​, D be the dimension, and H be the number of heads.
-
Projections: O(NL​D2+NF​D2).
-
Attention Scores: O(H⋅NL​⋅(NF​+NL​)⋅D/H)=O(NL​(NF​+NL​)D).
-
Final Projection: O(NL​D2).
-
Overall: O(D2(NL​+NF​)+NL​(NF​+NL​)D).
-
Space Complexity:
-
Storing intermediate matrices Q,K,V and their head splits requires O(D2+NF​D+NL​D) space.
-
Attention score matrix size is O(NL​(NF​+NL​)).
-
Overall: O(D2+NF​D+NL​D+NL​(NF​+NL​)).