Paper · 2021-11-29 · cs.CV, cs.AI, cs.CL
ZeroCap: Zero-Shot Image-to-Text Generation for Visual-Semantic Arithmetic
Writes captions with a frozen GPT-2 medium steered by CLIP, with no training. Before each token, five gradient steps shift the key/value cache so CLIP scores the growing sentence against the image, with a fluency term. Author-reported on COCO: CLIP-S 0.87 against 0.77 for ClipCap; about 30% R@5 on a 320-relation benchmark the authors built.
Image captioningVision-language pre-trainingZero-shot learningControllable text generationimage-captioningzero-shotclipgpt-2guided-decodingkv-cachevisual-arithmetic
arXivPDFCodeMarkdown15 min read
AI-compiled by claude-haiku-5-5 on 2026-11-06, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Image captioning usually needs a model trained on sets of pictures with human-written captions. This paper writes captions without any such training. It takes two large models that already exist: a text model that writes sentences one word at a time, and a model that scores how well a sentence matches a picture.
While the text model writes, the method nudges its internal memory of the sentence so far, a little at each word, toward wording that the picture model rates as a better match. A second term keeps the wording natural. Because the steering works on the picture model's embedding, you can also add or subtract pictures, or pictures and words, and read back a sentence describing the result.
The captions use a wider vocabulary than the supervised methods, and none of them appear in the COCO training captions. On standard word-overlap metrics against human captions they score lower, which the authors put down to those metrics rewarding the annotators' own style.
TL;DR
- Zero-shot captioner: a frozen GPT-2 medium is steered at inference time by a frozen CLIP ViT-B/32 via gradient steps on the key/value cache §3 app. B.
- No weights are trained. The cost is paid per image at generation time §3 §5.
- Author-reported: on COCO it beats all three baselines on CLIP-S and vocabulary size, but trails them on BLEU, METEOR, CIDEr and SPICE tab. 1.
- The same loop supports image and text arithmetic that outputs a sentence §4.
Problem
Supervised captioners are trained on curated caption sets such as MS-COCO and Visual Genome, which are small and cannot cover every image §1 §2. The authors argue that CLIP was trained on far more web data but is only a matching score, so it cannot generate text on its own [abstract] §1. Reported earlier attempts to train a captioner on the same web data struggled §1. The goal is a method that uses an existing image-text matching model and an existing language model without any further training §1 §3 Overview.
Method
Overview. The language model (GPT-2, 24 layers) predicts each next token from a prompt, such as "Image of a", and from a cache of the keys and values of all tokens so far eq. 1 §3 Language models app. B. The language model's weights are not changed. Instead, at each step the cache is shifted so that the next-token distribution moves toward the image §3 Overview §3 Inference. The conclusions state that all layers and all attention heads are modified §7.
Per-token loop. For each new token the method runs the following steps. The shift is recomputed from scratch for every token §3 Inference.
- Compute the next-token distribution from the unmodified cache. This is the reference distribution for the fluency term eq. 2.
- Start a cache shift at zero and run five gradient steps on it §3 Inference.
- In each step, take the next-token distribution from the shifted cache, then compute the CLIP loss and the fluency loss eq. 2.
- Update the shift with a normalised gradient step of size α = 0.3 eq. 5.
- After the five steps, use the shifted cache to produce the next-token distribution and pick the token §3 Inference.
CLIP loss. Only the 512 most likely next tokens are scored §3 CLIP loss. Each candidate forms a sentence: the caption so far plus that token. CLIP's text encoder embeds each sentence. The cosine similarity to the image embedding is turned into a target distribution with a softmax at temperature τ_c = 0.01 eq. 3 app. B. The loss is the cross-entropy between that target and the shifted next-token distribution eq. 4. Minimising it pushes probability toward tokens whose sentences match the image better §3 CLIP loss.
Fluency term. A cross-entropy term keeps the shifted next-token distribution close to the unshifted one, with weight λ = 0.2 eq. 2 app. B. The text calls it cross-entropy against the unshifted distribution; see Paper vs code.
Beam search. Decoding uses five beams §5 app. B. Each beam is an independent caption. The text says the beam with the lowest CLIP loss is reported app. B; see Paper vs code for how the released code selects one.
Repetition and length control. The method lowers the probability of tokens generated in the last four steps by a factor of two §5. A factor f_e is applied to the end token starting at step t_e §5. Values are listed under Hyperparameters.
Arithmetic. An image is embedded with CLIP's image encoder and a text with its text encoder §4 §5.2. Embeddings are combined by sum, for example E(I_1) + E(I_2) §4, or by difference, as in the analogy X ≈ C + B − A §7. The combined vector replaces the image embedding in the CLIP loss, so the same loop writes a sentence for it §4 eq. 3. Examples in the text: subtracting a real airplane photo from a caricature gives "a caricature illustration"; adding an apple to a phone gives "Apple's iPhone released" §5.2; a day-to-night direction taken from text prompts steers a breakfast image to "Nighttime dinner" §5.2 fig. 9. The authors also build a visual-relations benchmark (see Results) §5.2.
Pseudocode, paper's form. The released code differs in several places; see Paper vs code.
# Zero-shot caption decoding [§3 Overview] [§3 Inference] [app. B]
# Input: image_feature (CLIP image embedding, normalised)
# prompt (e.g. "Image of a")
# Output: caption text
def caption(image_feature, prompt, beam_size):
# Beam search over continuations of the prompt [§5]
beams = [prompt] * beam_size
for step in range(max_length):
# Unmodified next-token distribution [eq. 2] reference
probs_before = next_token_probs(beams, cache)
# Shift the key/value cache by gradient steps [§3 Inference]
shifted_cache = shift_cache(cache, probs_before, image_feature)
# Next-token distribution from the shifted cache [§3 Overview]
probs = next_token_probs(beams, shifted_cache)
# Raise the end-token probability from step t_e [§5]
probs = end_token_factor(probs, step)
beams = extend_beams(beams, probs, beam_size)
return best_beam(beams)# Cache shift, five steps [§3 Inference] [eq. 2] [eq. 5]
# Input: cache, probs_before, image_feature
# Output: shifted cache
def shift_cache(cache, probs_before, image_feature):
# The shift starts at zero [§3 Inference]
shift = zeros_like(cache)
for _ in range(5):
probs = next_token_probs_with(cache + shift)
# CLIP cross-entropy over the top 512 candidates [eq. 3] [eq. 4]
clip_loss = clip_cross_entropy(probs, image_feature)
# Fluency: distance from the unshifted distribution [eq. 2]
fluency_loss = 0.2 * fluency_term(probs, probs_before)
grad = gradient(clip_loss + fluency_loss, wrt=shift)
# Eq. 5 normalisation per layer, alpha = 0.3 [eq. 5]
# Sign follows minimising eq. 2; see Paper vs code, item 2
for layer in layers:
step = 0.3 * grad[layer] / norm(grad[layer]) ** 2
shift[layer] = shift[layer] - step
return cache + shift# CLIP cross-entropy over the top candidates [§3 CLIP loss] [eq. 3]
# Input: probs (next-token distribution), image_feature
# Output: scalar loss
def clip_cross_entropy(probs, image_feature):
# Keep the 512 most likely next tokens [§3 CLIP loss]
top_ids = top_k_indices(probs, 512)
# Each candidate is the caption so far plus that token [§3 CLIP loss]
sentences = [caption_so_far + token_text(i) for i in top_ids]
similarity = cosine(clip_text(sentences), image_feature)
# Target distribution, temperature tau_c = 0.01 [eq. 3] [app. B]
target = softmax(similarity / 0.01)
# Cross-entropy against the shifted distribution [eq. 4]
return -sum(target * log(probs[top_ids]))Hyperparameters
| Name | Value | Where | Notes |
|---|---|---|---|
| language model | GPT-2 medium, 24 layers, 345M parameters | §3 Language models app. B | HuggingFace implementation app. B; frozen §3 Overview |
| vision encoder | CLIP ViT-B/32 (OpenAI) | app. B | ViT-B/32 equivalent; OpenAI implementation app. B |
| prompt | "Image of a" | app. B | "Image of text that says" for OCR-style captions app. B §5.1 OCR |
| fluency weight λ | 0.2 | eq. 2 app. B | "set to 0.2 early on" §3 CLIP-guided; not tuned afterwards §3 CLIP-guided |
| CLIP temperature τ_c | 0.01 | eq. 3 app. B | applied to cosine similarity ZeroCLIP.py |
| candidate tokens | top 512 | §3 CLIP loss | others set to zero potential §3 CLIP loss |
| gradient steps per token | 5 | §3 Inference | |
| step size α | 0.3 | eq. 5 app. B | paper form normalises by squared norm; code differs, see Paper vs code |
| beams | 5 | §5 app. B | "five beams and 512 candidate tokens" in about 3 s on a Titan X §5 |
| repetition | probability of last-four-step tokens divided by 2 | §5 app. B | the released code's default is off ZeroCLIP.py |
| captioning: end factor f_e | 1.04 | §5 app. B | the released code's default is 1.01 run.py |
| captioning: end start step t_e | 3 | §5 | the released code hard-codes step 1 ZeroCLIP.py |
| arithmetic: end factor f_e | 1.06 | §5 | |
| arithmetic: end start step t_e | 1 | §5 | |
| arithmetic: fusion/grad-norm | not stated in paper | - | the released arithmetic entry point sets 0.95 for both predict_arithmetic.py |
| end token | not stated in paper | - | the released code uses "." ZeroCLIP.py |
| precision | not stated | - | |
| hardware | single Titan X GPU | §5 | timing only; training is absent |
| random seed | not stated | - | the released code defaults to 0 run.py |
Results
All results are author-reported. Captioning is evaluated on COCO. The paper uses COCO's test set in its text §5.1 and COCO's validation set (Karpathy splits) in its appendix app. B; see Pitfalls. Table 1 compares against ClipCap, CLIP-VL and VinVL, with the paper's own method last tab. 1.
Captioning, COCO tab. 1. Supervised metrics are listed as printed; the caption says BLEU-1 but the column header says B@4 (see Pitfalls).
| Method | B@4 | METEOR | CIDEr | SPICE | CLIP-S^Ref | Vocab | %Novel | CLIP-S |
|---|---|---|---|---|---|---|---|---|
| ClipCap | 32.15 | 27.1 | 108.35 | 20.12 | 0.81 | 1650 | 66.4% | 0.77 |
| CLIP-VL | 40.2 | 29.7 | 134.2 | 23.8 | 0.82 | 2464 | 85.1% | 0.77 |
| VinVL | 41.0 | 31.1 | 140.9 | 25.2 | 0.83 | 1125 | 77.9% | 0.78 |
| This paper | 2.6 | 11.5 | 14.6 | 5.5 | 0.79 | 8681 | 100% | 0.87 |
Text claims: CLIP-S 87% against 77% for the supervised method, and the vocabulary of 8681 against 2464 for CLIP-VL, with no generated sentence in the training set §5.1. The supervised methods lead on the human-reference metrics because they were trained on annotations from the same process §5.1. The CLIP-S metric is attributed to CLIPScore §5.1; no formula appears in the compiled text (see Pitfalls).
Human study, COCO app. C. Fifty images, forty annotators. Preference shares: this method 70.5%, variant A1 8.9%, and variant A2 20.6%. A1 and A2 use CLIP directly, without steering the language model app. C.
Human study, web images app. C. Fifty web images, scored 1 to 5 on human-likeness and visual grounding. Human-like: 3.79 against 3.17 for ClipCap. Grounding: 3.98 against 3.21 for ClipCap app. C.
Ablation, optimising the language model directly tab. 3 app. C. A1 chooses tokens to maximise the CLIP score. A2 uses a score that mixes the CLIP score with a language-model score. Perplexity is measured with GPT-Neo app. C.
| Method | CLIP-S | Perplexity |
|---|---|---|
| A1 | 0.98 | 8.61 |
| A2 | 0.91 | 6.04 |
| This paper | 0.87 | 5.50 |
Visual-relations benchmark tab. 2 §5.2. The authors build 320 relations over five templates: building to country, country to capital, food to country, leader to country, and CEO to company §5.2. Metrics: BLEU-1, Recall@5 (the answer appears among the first five generated words), and a CLIP-score §5.2. The text says success is about 30% at R@5 for most relations §5.2.
| Relation | Method | B@1 | R@5 | C-s |
|---|---|---|---|---|
| Building → Country | ClipCap | 0.003 | 0.035 | 0.24 |
| Building → Country | This paper | 0.1 | 0.32 | 0.7 |
| Country → Capital | ClipCap | 0.0 | 0.0 | 0.22 |
| Country → Capital | This paper | 0.14 | 0.32 | 0.68 |
| CEO → Company | ClipCap | 0.004 | 0.05 | 0.18 |
| CEO → Company | This paper | 0.1 | 0.3 | 0.64 |
| Food → Country | ClipCap | 0.0 | 0.0 | 0.24 |
| Food → Country | This paper | 0.03 | 0.33 | 0.66 |
| Leader → Country | ClipCap | 0.008 | 0.24 | 0.26 |
| Leader → Country | This paper | 0.1 | 0.28 | 0.68 |
The benchmark is linked in the README but its licence is not stated in the paper. This card does not reproduce its data README.md.
Qualitative results are shown in figures and described in words §5.1 §5.2 fig. 3 fig. 4 fig. 5 fig. 6 fig. 7 fig. 8 fig. 9. OCR-style captions read printed text such as "The University of Stanford" from a sign §5.1 OCR. Knowledge-style captions name people, places, shows and films §5.1 External knowledge.
Paper vs code
Code: raw/code/2111.14447 at b99f0af59aa98f9447c5e691e702fd5c9cccd5fb. The
repository has no LICENSE file, so no verbatim code is quoted here.
-
Fusion step, not in the paper. The code mixes the shifted and unshifted next-token distributions as p^0.99 × p_before^0.01, then renormalises. The paper's equations have no such step ZeroCLIP.py eq. 2. The arithmetic entry point uses 0.95 [code: predict_arithmetic.py#L79].
-
Update rule. Eq. 5 is printed with a plus sign, a squared norm and a per-layer normalisation eq. 5. The text says the objective of eq. 2 is minimised §3 Inference, so the sign has to be a descent step. The code subtracts the step, scales by α = 0.3, and divides by the norm raised to 0.9 (0.95 for arithmetic) ZeroCLIP.py run.py predict_arithmetic.py. It normalises separately for each key and each value tensor of each layer, per batch element, and adds 1e-15 to the norm ZeroCLIP.py.
-
Beam weights, computed but unused. The code computes min-max weights from the fluency and CLIP losses per beam ZeroCLIP.py, but the step at lines 297 to 306 does not multiply by them. The weights have no effect. The paper does not describe them in its live text. The batched module drops the block entirely; its
norm_gradtakes no weights [code: model/ZeroCLIP_batched.py#L319-L345]. -
Fluency term. The code's expression is Σ p log p − Σ p log p_before, with p the shifted distribution, times λ ZeroCLIP.py. That is KL(p ‖ p_before), not the cross-entropy against the unshifted distribution that the paper names in eq. 2.
-
Which candidates. The top 512 are taken from the shifted distribution inside the shift loop ZeroCLIP.py [code: model/ZeroCLIP.py#L366-L367]. The paper does not say which distribution.
-
Distance vs similarity. The paper calls D_CLIP a "cosine distance" eq. 3. The code uses cosine similarity, higher is closer, with a softmax at the temperature ZeroCLIP.py.
-
Which caption is reported. The paper reports the beam with the lowest CLIP loss app. B.
run.pyinstead encodes each continuation without the prompt and takes the highest cosine similarity run.py. The continuations exclude the prompt, because only generated tokens are decoded ZeroCLIP.py ZeroCLIP.py. -
Beam ordering. The code ranks beams by the length-normalised score, which is the sum of log probabilities divided by length [code: model/ZeroCLIP.py#L173-L174] ZeroCLIP.py. The paper does not state a beam ranking in its live text.
-
Repetition penalty. The paper halves the probability of tokens from the last four steps §5. The code divides the logits of the last four context tokens by a factor, which halves a positive logit, not a probability [code: model/ZeroCLIP.py#L343-L345]. The default factor is 1, so the penalty is off in every entry point ZeroCLIP.py run.py.
-
End-token factor. The code multiplies the end-token logit by f_e, not the probability ZeroCLIP.py. The start step is hard-coded to 1 ZeroCLIP.py, so t_e is never used. The default f_e is 1.01 run.py and the paper's captioning value is 1.04 §5. The README uses 1.04 for OCR and real-world examples and the default for the COCO example README.md README.md.
-
Start-token and forbidden-token rules, not in the paper. At step 0 the end token's logit is divided by 1.6 ZeroCLIP.py. Tokens listed in
forbidden_tokens.npyare pushed down by a factor of 20, dividing positive logits and multiplying negative ones [code: model/ZeroCLIP.py#L355-L357] ZeroCLIP.py. The paper does not describe this list, and this card has not inspected the file. -
Capitalised-token penalty, paper only. Appendix E says a negative prior is applied to capitalised subwords app. E. The code builds the list of such tokens ZeroCLIP.py, but no other line uses it. It is not implemented in the released code.
-
The reset flag is needed. The README always passes
--reset_context_deltaREADME.md.run.pydefaults the flag to off [code: run.py#L15-L16]. In that casecontextis never assigned before it is read, soget_next_probswould fail ZeroCLIP.py. This is read from the code; it was not run. -
Arithmetic inputs. Single images are normalised before use [code: model/ZeroCLIP.py#L110-L111]. The weighted sum in
get_combined_featureadds raw embeddings and normalises only the result [code: model/ZeroCLIP.py#L131-L136]. The paper does not state this. -
Batched variant.
--multi_gpuuses a batched module with the same loss over the top 512 candidates. It decodes the prefix with special tokens skipped ZeroCLIP_batched.py. The paper says nothing about this. -
Entry-point defaults. The arithmetic entry point defaults to 3 beams and a maximum of 3 tokens predict_arithmetic.py [code: predict_arithmetic.py#L65]. The paper's five beams §5 are the captioning default run.py.
Pitfalls
-
Dataset mismatch. The text says COCO's test set §5.1. The appendix says COCO's validation set, Karpathy splits, for both qualitative and quantitative work app. B. Which split Table 1 uses is therefore not clear.
-
Hand-chosen settings. The paper says its settings "produced concise, fluent, and image-related captions" based on a human assessment app. B, and that λ was set early in development and not changed afterwards [§3 CLIP-guided]. The images used for that assessment are not stated. The method trains no weights, but its settings were not fixed in advance.
-
Table 1 BLEU label. The caption says B@1, but the column is B@4 tab. 1.
-
CLIP-S and the benchmark CLIP-score are not defined in the compiled text. The paper cites CLIPScore §5.1 but gives no formula. The visual-relations CLIP-score is described only as a "cosine distance" with "more details ... in the supplementary" §5.2, and that supplement does not define it either. The source contains a commented-out formula that rescales by 2 after subtracting 0.5, and a weight of 2.5 for CLIP-S. Neither is in the compiled paper, so this card does not use them.
-
Continuation-only scoring. The reported captions exclude the prompt [code: model/ZeroCLIP.py#L206-L210]. The CLIP and perplexity scores in the released entry points therefore score the continuation alone. A reproduction that scores the prompt plus the continuation may rank beams differently.
-
Compute grows with candidates and beams. Each token needs five backward passes through GPT-2 medium over the shifted cache, plus CLIP text encoding of up to 512 candidate sentences per beam ZeroCLIP.py. The paper reports that five beams and 512 candidates take about three seconds on one Titan X, and that inference time is proportional to the number of candidates and beams §5.
-
Gradient normalisation sensitivity. Eq. 5 and the code normalise by different powers of the norm (2 in the paper, 0.9 or 0.95 in the code). The step size then means different things. Follow the code for numbers that reproduce the examples, and state which one you used.
-
Unshifted reference. The fluency loss and the fusion step both use the unshifted distribution. The code computes it once per token, before the shift loop ZeroCLIP.py. An implementation that recomputes it inside the loop changes the fluency term.
-
Known bias. The authors say the models are oriented toward Western knowledge and some relations fail §6. The web-scale noise also yields entity names from the data source, such as "Flickr" and photographers' names, which the capitalised-token penalty was meant to suppress app. E. That penalty is not in the released code (Paper vs code, item 12). Biased outputs are reported qualitatively app. E and are not measured.
-
Known failure modes on the visual-relations benchmark. Popular over correct answers (Sydney over Canberra), synonyms ("Canadian" for "Canada"), related places (Sinai for Egypt) and wrong relations ("Berkeley" from a UK relation) app. F §5.2.
-
Single reference per relation. The benchmark has one answer per sample, so a correct variant can be marked wrong. The paper's example: "US" is a miss when the reference is "USA" §5.2.
-
Version. This card follows arXiv v2, updated 2022-03-31 [raw metadata].
Lineage
- Builds on: CLIP (contrastive image-text pretraining) and GPT-2 (text generation), both named in the paper but not linked here §1.
- Compared against: ClipCap (a CLIP-prefix captioner that fine-tunes GPT-2), CLIP-VL and VinVL, all named in Table 1 tab. 1 §5.1.
- Related prior work on guiding language models: PPLM, which steers a model with an attribute classifier and no further training §2.
Source and license
Adapted from “ZeroCap: Zero-Shot Image-to-Text Generation for Visual-Semantic Arithmetic” by Yoad Tewel, Yoav Shalev, Idan Schwartz, Lior Wolf (arXiv:2111.14447), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/YoadTew/zero-shot-image-to-text at commit b99f0af59aa9 (none (no LICENSE file in repository)). Any quoted code is shown under that license.