PIXELBANKv9.1.0
Menu

Scaled Dot-Product Cross-Attention

Problem Statement

Compute the output of a single-head cross-attention layer given explicit query, key and value matrices - the operation that lets learned queries (or text tokens) read from image features.

Background

Cross-attention is the same computation as self-attention except that the queries come from one stream and the keys/values from another. In a VLM the queries are language-side (or the Q-Former's learned queries) and the keys/values are the vision encoder's patch features:

Attention(Q,K,V)=softmax ⁣(QK⊀dk)V\text{Attention}(Q, K, V) = \text{softmax}\!\left(\frac{QK^\top}{\sqrt{d_k}}\right)V

with Q of shape (n_q, d_k), K of shape (n_kv, d_k), V of shape (n_kv, d_v), and the output of shape (n_q, d_v). The output length follows the queries, not the keys - which is exactly why a resampler with 64 queries turns 576 patch features into 64 tokens.

Two things trip people up:

  • The 1/sqrt(d_k) scale is not cosmetic. Dot products of d_k independent terms grow like sqrt(d_k); without the divisor the softmax saturates and gradients vanish. Divide by sqrt(d_k), never by d_k.
  • Softmax is row-wise over the keys and must be numerically stable: subtract each row's maximum before exponentiating.

Your Task

Implement:

def cross_attention(Q, K, V):

Return the (n_q, d_v) output as a nested list with every entry rounded to 4 decimals.

Input Format

  • Q - nested list, shape (n_q, d_k)
  • K - nested list, shape (n_kv, d_k)
  • V - nested list, shape (n_kv, d_v)

Output Format

A nested list of n_q rows of d_v floats, each rounded to 4 decimals.

Sample

Q = [[1.0, 0.0]]
K = [[1.0, 0.0], [0.0, 1.0]]
V = [[1.0, 2.0], [3.0, 4.0]]
print(cross_attention(Q, K, V))

Output:

[[1.6605, 2.6605]]

Example:

Input:
Q = [[1.0, 0.0]]
K = [[1.0, 0.0], [0.0, 1.0]]
V = [[1.0, 2.0], [3.0, 4.0]]
print(cross_attention(Q, K, V))
Output:
[[1.6605, 2.6605]]
Reasoning:

Scores are [1, 0] divided by sqrt(2) = [0.7071, 0]. Softmax gives weights [0.6698, 0.3302], so the mix of the two value rows is 0.6698*[1,2] + 0.3302*[3,4] = [1.6605, 2.6605]. Skipping the sqrt(2) scale would give weights [0.7311, 0.2689] and the different answer [1.5379, 2.5379].

Constraints:

  • 1 <= n_q, n_kv <= 64, 1 <= d_k, d_v <= 64
  • Scale scores by 1 / sqrt(d_k) where d_k is the QUERY/KEY width, not d_v
  • Softmax runs over the key axis, one row per query, computed stably
  • The output has one row per QUERY
  • Round every entry to 4 decimals and avoid emitting -0.0
solution.py

Test Results

0/0
Run code to see test results.
Scaled Dot-Product Cross-Attention - Medium | PixelBank