Size a BLIP-2 style Q-Former: given the number of learned query tokens, the widths involved and the number of cross-attention blocks, report the output shape, the parameter count, and the compression ratio it achieves over the raw visual tokens.
A linear projector maps every one of the vision encoder's tokens into the LLM, so a 257-token ViT costs 257 LLM positions. A Q-Former breaks that coupling: it owns a fixed set of num_queries learned query embeddings (32 in BLIP-2) that cross-attend to the image features and emit exactly num_queries output tokens - no matter how many image tokens came in. That is the compression ratio:
compression = num_image_tokens / num_queries
Count the parameters with this simplified model:
W_K and W_V read the frozen vision encoder's width d_image (1408 for EVA-ViT-g) and write the Q-Former's own width d_query (768), which is why the two widths appear asymmetrically.
Notice what is missing from the list: num_image_tokens. Sequence length never appears in a parameter count - only in the activation/FLOP cost. The Q-Former's size is identical whether it consumes 257 or 4097 image tokens.
Implement:
def qformer_stats(num_queries, d_query, d_image, num_image_tokens, num_layers, d_llm):
Return [num_image_tokens, num_queries, d_llm, total_params, compression] where the first four are ints and compression is num_image_tokens / num_queries rounded to 4 decimals.
Six positive integers, as named above.
[int, int, int, int, float]
print(qformer_stats(32, 768, 1408, 256, 12, 4096))
Output:
[256, 32, 4096, 43319296, 8.0]
print(qformer_stats(32, 768, 1408, 256, 12, 4096))
[256, 32, 4096, 43319296, 8.0]
One block costs 2*(768768+768) + 2(1408768+768) = 3345408 parameters; 12 blocks give 40144896. Add 32768 = 24576 query embeddings and the 768->4096 projector at 3149824, for 43319296 total. 256 image tokens become 32 output tokens: an 8x compression.
W_K and W_V map d_image -> d_query; W_Q and W_O map d_query -> d_querynum_image_tokenscompression to 4 decimals; return the counts as plain Python ints