← All tutorials

From BLIP to the Q-Former

On this page

1 One text model, three objectives, almost all weights shared 2 The model cleans its own captions 3 Two frozen models need a small trainable module between them 4 Inside the Q-Former, 32 learned queries read the image 5 One set of weights learns three objectives from three masks 6 The second stage feeds the language model soft prompts 7 BLIP-2 beats an 80B model on VQAv2 with 188M trainable parameters 8 Where BLIP-2 falls short 9 Two Hugging Face calls run the whole pipeline 10 References

BLIP-2 lets a frozen language model describe images and answer questions on them. A frozen image encoder reads the picture, a small trained Q-Former compresses it into 32 vectors, and these precede the language model's text. The Q-Former trains in two pretraining stages on image-caption pairs, then runs once per image at inference, before the language model writes.

Cross-attention takes queries from one stream and keys and values from another; learned queries are vectors trained like weights, and a mask blocks chosen query and key pairs.

32 learned queries read the frozen image features and return 32 vectors.
Z = CrossAttn(Q, Ximg)
The Q-Former's output Z is 32 vectors that summarize the image for the language model. It is ordinary Cross-Attention: the 32 learned queries Q ask, and the frozen image features Ximg answer as keys and values. The output takes its row count from the queries, so Z has 32 rows whatever the image size; training moves the queries and the Q-Former's weights, never the image encoder in pre-training.
BLIP's three lossesimageencoderQ-Former32 queriestrainslinearlayertrainslanguagemodelStage 1 learns to read the imageStage 2 learns to prompt the language model
Figure 1. Only the green parts train; Section 1 explains BLIP's three losses.

1 One text model, three objectives, almost all weights shared

BLIP (Li et al. 2022) trains one multimodal mixture of encoder-decoder (MED) on image-caption pairs. A ViT (vision transformer, which cuts an image into patch tokens) reads the image, and one text Transformer, started from BERT-base (a 12-layer text encoder), runs in three modes.

The unimodal encoder reads the caption alone and trains with the image-text contrastive loss (ITC), which pulls a matched image vector and caption vector together and pushes the batch's other captions apart, as in Image-Text Contrastive Learning (CLIP) (Radford et al., 2021). The image-grounded encoder also reads the image and trains with the image-text matching loss (ITM), a head that says matched or not. The image-grounded decoder writes the caption under causal self-attention and trains with the language modeling loss (LM).

ITC ITM LM contrastive matching next word FFN FFN FFN no cross- attention cross- attention cross- attention self-attention bi-directional self-attention bi-directional self-attention causal [CLS] + text [Encode] + text [Decode] + text image encoder (ViT) one pass, used by all three [CLS] compared with the image same color, same weights for example a dog photo, “a dog runs on the sand” same direction as the image? “on the grass” must fail write it word by word
Figure 2. The encoder and the decoder share everything except self-attention; a special first token tells the shared text model which mode it is in.

BLIP's three passes over one image and caption, with one self-attention and one cross-attention layer standing in for the text Transformer, plus the decoder's own causal self-attention.

import torch
import torch.nn as nn

D, N_PATCH, N_TXT, H = 768, 197, 12, 12
img = torch.randn(1, N_PATCH, D)  # ViT patch features
txt = torch.randn(1, N_TXT, D)    # caption token vectors
self_attn = nn.MultiheadAttention(D, H, batch_first=True)
causal_self_attn = nn.MultiheadAttention(D, H, batch_first=True)
cross = nn.MultiheadAttention(D, H, batch_first=True)
causal = torch.ones(N_TXT, N_TXT, dtype=torch.bool).triu(1)

itc, _ = self_attn(txt, txt, txt)  # ITC, text only
itm, _ = cross(itc, img, img)      # ITM, reads img
y, _ = causal_self_attn(txt, txt, txt, attn_mask=causal)
lm, _ = cross(y, img, img)         # LM, causal, reads img
  1. 1Image features, caption tokens, the shared layers and the decoder's own causal self-attention, the three columns of Figure 2.
  2. 2ITC, text attends only to itself, the left column of Figure 2.
  3. 3ITM, text also reads the image, the middle column of Figure 2.
  4. 4LM, causal text reading the image, the right column of Figure 2.

2 The model cleans its own captions

Web captions (alt-text) often miss the picture. CapFilt (captioning and filtering) cleans them with two copies of the MED, each fine-tuned on COCO's human-written captions.

The captioner, the decoder fine-tuned with LM, writes one caption per web image by nucleus sampling (each word drawn from the likeliest words covering 90% of the probability; a higher share allows rarer words and more varied captions, a lower one stays with safe common words); the filter, the encoder fine-tuned with ITC and ITM, drops captions its ITM head calls unmatched. The gain is measured by text recall@1, how often an image's top-ranked caption is the right one.

web image + its web caption image captioner decoder, LM, COCO web caption synthetic caption filter encoder, ITC + ITM, COCO ✗ web, “from bridge near my house” ✓ synthetic, “a flock of birds flying over a lake at sunset” kept captions + human-written pairs pre-train a new model on this 14M images, fine-tuned COCOtext recall@1, 78.4 → 80.6
Figure 3. The model writes and filters its own captions; pair from BLIP Fig. 4.

3 Two frozen models need a small trainable module between them

BLIP-2 (Li et al. 2023) keeps the image encoder and the large language model (LLM) frozen, to save compute and avoid catastrophic forgetting (new training wiping out old knowledge), and trains a small module between them in two stages. Stage 1 pulls out the image features most relevant to the text with BLIP's three objectives, the captioning loss renamed ITG (image-grounded text generation).

imageimageencoderfrozensmallmoduleonly thistrainslanguagemodelfrozentext out
Figure 4. Only the green module trains.

4 Inside the Q-Former, 32 learned queries read the image

The Q-Former (Querying Transformer) has 32 learned queries of size 768 and two submodules, an image transformer and a text transformer, that share only self-attention.

It starts from BERT-base weights, with cross-attention random, and has 188M parameters, queries included.

32 learned queries32 × 768texttokensself-attention, shared by both sidesa mask decides who sees whomcross-attentionevery other blockimage features257 × 1024ViT-L/14, frozenFFN for queriesFFN fortextZ, output queries32 × 768text out
Figure 5. An image of any size leaves as 32 vectors. ViT-L/14 (14-pixel patches) gives 257 features of size 1024.

Its self-attention mask is one of the three masks of Section 5.

One Q-Former block in PyTorch, LayerNorms left out; queries and text go in and come out the same shape.

import torch
import torch.nn as nn

N_Q, D, D_IMG, D_FF, H = 32, 768, 1024, 3072, 12
MHA = nn.MultiheadAttention
self_attn = MHA(D, H, batch_first=True)
cross = MHA(D, H, kdim=D_IMG, vdim=D_IMG, batch_first=True)

def ffn():
    up, down = nn.Linear(D, D_FF), nn.Linear(D_FF, D)
    return nn.Sequential(up, nn.GELU(), down)

ffn_q, ffn_t = ffn(), ffn()  # image and text transformers

def block(x, img, allowed):  # x (B, N_Q + T, D)
    mask = ~allowed  # True = blocked
    x = x + self_attn(x, x, x, attn_mask=mask)[0]
    q, t = x[:, :N_Q], x[:, N_Q:]
    q = q + cross(q, img, img)[0]  # only queries read img
    q, t = q + ffn_q(q), t + ffn_t(t)
    return torch.cat([q, t], dim=1)
  1. 1Self-attention, and cross-attention with 1024-wide keys and values, the two attention boxes of Figure 5.
  2. 2One FFN for each transformer, the two FFN boxes of Figure 5.
  3. 3Masked self-attention over queries and text, the shared self-attention of Figure 5.
  4. 4Only the 32 query rows read the image, the Q-Former card above Section 1, line 1.
  5. 5Each transformer through its own FFN, out to Z in Figure 5.

The output Z is smaller than the frozen features, so the queries keep only what the text needs.

How many feature vectors a ViT with 14-pixel patches gives for a square image P pixels wide.
N = (P14)2 + 1
A bigger image gives the ViT more feature vectors, growing with the square of its width. P14 is the number of 14-pixel patches per side; squaring it counts every patch, and the +1 is one summary vector: N = 257 at the 224 px of pre-training, 677 at 364 px (captioning fine-tuning) and 1,226 at 490 px (VQA fine-tuning, for visual question answering). BLIP-2 takes them from ViT-g/14, the giant ViT (about 1B parameters) from EVA-CLIP, so each is 1,408 wide, not Figure 5’s ViT-L/14 at 1,024. The Q-Former output stays 32 by 768 for all three.

In Figure 6 a region’s patches share its key, all one length. A patch’s weight is the softmax of query times key, and the output is the weighted mix (Cross-Attention, Figure 3). Keys pointing the query’s way get the most weight; a longer query picks more sharply, and near the centre all get about the same. Drag query 1 to try the directions training could give it; training fixes one.

toy image 16 × 16 patches lit patches get more weight ViT-g features in 257 × 1,408 2 Q-Former out 32 × 768 one per query, query 1 is purple 1 learned query 1, drag its tip region keys, toy 2-D, all one length drag limit

Figure 6. 1 Direction picks the patches; 2 any size, 32 vectors.


5 One set of weights learns three objectives from three masks

Stage 1 trains all three objectives at once, each with its own mask over queries and text, so the queries cannot copy the caption and must carry what it needs. Contrastive (ITC) keeps queries and text apart, so each side builds its own summary to compare. Matching (ITM) lets everything see everything, and a head says matched or not. Generation (ITG) lets text see the queries and earlier text, so the queries must carry what the caption needs.

In the contrastive score, t is the output of the text's first token, a summary token.

The contrastive similarity between an image and a caption.
s(I, C) = maxi sim(zi, t)
An image I and a caption C score as high as their best-matching query. sim compares query output zi with the caption summary t: both are projected and normalized to length 1, then dotted, so the score is the cosine of the angle between them. maxi keeps the best of the 32 instead of an average, so one query that found the caption's subject is enough, and different queries are free to specialize in different image parts. Training raises this score for matched pairs and lowers it for the other captions in the batch, the InfoNCE loss.

The generation mask for 32 queries and 6 text tokens.

import torch

N_Q, N_TXT = 32, 6
pos = torch.arange(N_Q + N_TXT)
is_query = pos < N_Q
row_q, col_q = is_query[:, None], is_query[None, :]
past = pos[None, :] <= pos[:, None]  # column <= row
text_sees = col_q | past
allowed = (row_q & col_q) | (~row_q & text_sees)
print(allowed.shape)  # torch.Size([38, 38])
  1. 1Which of the 38 positions are queries, the first 32.
  2. 2Text may see every query and earlier text, the causal part of the generation mask.
  3. 3Query rows see only queries, so the queries never read the caption they help generate.
Mask for the objective
queries text queries text rows may look at blue columns image features cross-attention queries text text sees queries queries see text queries see queries text sees text arrow points to the side that receives

Figure 7. Rows look and columns are seen; blue cells are allowed.


6 The second stage feeds the language model soft prompts

In Stage 2, one fully connected (FC) layer projects Z's 32 vectors to the LLM's embedding size, and they precede the text as soft visual prompts (vectors that act like extra words); only the Q-Former and the FC layer train.

the language model's input, one row of vectors32 prompt vectorsprojected Z (6 drawn)aredcaronatext token embeddingsloss on these onlyfrozen language model
Figure 8. To the language model the image arrives as 32 extra input positions.

OPT (decoder-only) trains with the next-token loss L; FlanT5 (encoder-decoder) trains with a prefix loss, where the caption's first part goes into the encoder with the prompts and the decoder writes the rest.

The decoder-only (OPT) loss, predicting each caption token with the 32 prompts in front.
L = −1T∑t=1T ℓt
The loss L is low when the frozen LLM, reading the 32 prompts, predicts the caption well. It averages a per-token term ℓt over the T caption tokens, and the minus sign turns higher log probabilities into lower loss; averaging keeps long and short captions on one scale.
ℓt = log P(wt | p1…p32, w<t)
Each term is the log of the probability the LLM gives the true caption token wt, seeing the prompts p1 to p32 and the caption so far. A probability of 1 gives 0, and a probability near 0 gives a large negative number. The LLM itself is frozen, so the only way to lower L is for the Q-Former to write prompts that carry what the caption needs.

Stage 2 with OPT-125M as the frozen LLM and random Z as the Q-Former output.

import torch
import torch.nn as nn
from transformers import AutoModelForCausalLM

NAME, IGNORE = "facebook/opt-125m", -100
llm = AutoModelForCausalLM.from_pretrained(NAME)
llm.requires_grad_(False)  # frozen, gradients still pass
proj = nn.Linear(768, llm.config.hidden_size)  # FC layer
embed = llm.get_input_embeddings()

Z = torch.randn(2, 32, 768)  # Q-Former output (B, N_Q, 768)
ids = torch.randint(100, 2000, (2, 8))  # caption token ids
B, N_Q = Z.shape[:2]
x = torch.cat([proj(Z), embed(ids)], dim=1)
skip = torch.full((B, N_Q), IGNORE)
labels = torch.cat([skip, ids], dim=1)
L = llm(inputs_embeds=x, labels=labels).loss  # mean
L.backward()  # reaches proj and, upstream, the Q-Former
  1. 1A frozen LLM and the trainable FC projection; the projection and the Q-Former are the only parts that learn here.
  2. 2Projected Z goes before the caption, Figure 8.
  3. 3-100 labels keep the 32 prompts out of the loss, which covers only the T caption tokens of the loss card in Section 6, line 1.
  4. 4The mean loss L of the Section 6 card, line 1, reaches the projection.
image, 224 × 224 ViT-g/14 encoder frozen 257 × 1408 Q-Former trains ITC, ITM, ITG with the caption gradients 32 × 768 FC layer trains added in Stage 2 32 × LLM width LLM, OPT or FlanT5 frozen added in Stage 2 LM loss on the caption gradients pass through the frozen LLM Parameters trainable 188M, the Q-Former frozen about 1B, the ViT-g

Figure 9. Dashed red arrows are gradients; bars compare trainable and frozen parameters.


7 BLIP-2 beats an 80B model on VQAv2 with 188M trainable parameters

The test is zero-shot visual question answering (VQA), with no training on the test sets, against Flamingo80B, an earlier 80B image-and-language model.

PartWhat BLIP-2 uses
DataBLIP's 129M images. BLIP-large writes captions for each. CLIP's ViT-L/14 ranks them with the web caption, and the top two stay.
Image encodersViT-L/14 from CLIP and ViT-g/14 from EVA-CLIP (Fang et al. 2023), frozen, second-to-last layer features
LLMsOPT and FlanT5, several sizes, frozen
Steps250k in Stage 1, 80k in Stage 2
Hardware16 A100 GPUs; under 6 days for Stage 1, under 3 for Stage 2, largest size
Flamingo80B, 10.2B trainableBLIP-2, 188M trainablezero-shot VQAv256.365.01zero-shot OK-VQA50.645.9203570
Figure 10. BLIP-2 leads on VQAv2 (1) with 54× fewer trainable parameters; Flamingo80B leads on OK-VQA (2), questions needing outside knowledge.

The paper credits Flamingo80B's OK-VQA lead to its 70B language model. It counts 188M trainable parameters in Table 1 and 108M in Table 2.


8 Where BLIP-2 falls short


9 Two Hugging Face calls run the whole pipeline

On the released OPT-2.7B checkpoint, an image and a question go in and an answer comes out.

import torch
from PIL import Image
from transformers import Blip2Processor
from transformers import Blip2ForConditionalGeneration

CKPT = "Salesforce/blip2-opt-2.7b"
processor = Blip2Processor.from_pretrained(CKPT)
model = Blip2ForConditionalGeneration.from_pretrained(CKPT, dtype=torch.float16)
model = model.to("cuda")
print(model.config.num_query_tokens)  # 32

image = Image.open("photo.jpg").convert("RGB")
question = "Question: what is in the picture? Answer:"
inputs = processor(image, question, return_tensors="pt")
inputs = inputs.to("cuda", torch.float16)
ids = model.generate(**inputs, max_new_tokens=20)
print(processor.batch_decode(ids, skip_special_tokens=True))
  1. 1Loads the OPT-2.7B checkpoint in float16, every box of Figure 9 in Stage 2.
  2. 2An image and a question become inputs, the image and text tokens of Figure 8.
  3. 3Generates up to 20 answer tokens after the 32 prompts, the loss card in Section 6, line 2, now used to pick words.
CapFiltOnce, before pretraining, over the web images.
Stage 1Pretraining only; image-caption pairs, three losses every step, image encoder frozen.
Stage 2Pretraining only; the frozen LLM's next-token or prefix loss, gradients passing through it.
InferenceOne Q-Former pass per image; its 32 vectors precede the text for every word generated.
QueriesRows of Q and of Z. More pass more of the image as more LLM tokens, fewer compress the image more; 1 is one vector; 257 matches the ViT's feature count at 224 px, so the count no longer shrinks, though each query still reads a weighted mix of all features. Default 32.
CodeLAVIS BLIP-2 with both stage scripts, Hugging Face BLIP-2.

10 References

  1. Li, J., et al. (2022). BLIP: Bootstrapping Language-Image Pre-training for Unified Vision-Language Understanding and Generation. ICML 2022. arXiv:2201.12086.
  2. Radford, A., et al. (2021). Learning Transferable Visual Models From Natural Language Supervision. ICML 2021. arXiv:2103.00020.
  3. Li, J., et al. (2023). BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models. ICML 2023. arXiv:2301.12597.
  4. Fang, Y., et al. (2023). EVA: Exploring the Limits of Masked Visual Representation Learning at Scale. CVPR 2023. arXiv:2211.07636. (ViT-g width 1408, about 1B parameters.)