CaptionEvalKit-for-VLMs

June 22, 2026 ยท View on GitHub

logo

Reproducible, all-in-one image captioning evaluation for VLMs.

  • For metric developers:
    • ๐Ÿค” Need a reliable way to reproduce Kendall's tau for captioning metrics?
    • ๐Ÿ˜„ Evaluate metrics and reproduce reported results with a single command!
  • For VLM developers:
    • ๐Ÿค” Tired of preparing separate dependency environments for each metric?
    • ๐Ÿ˜„ Score VLM-generated captions using a comprehensive set of established captioning metrics.

CaptionEvalKit currently supports:

  • LLM-free metrics: Polos, CLIPScore, PAC-S, RefCLIPScore, RefPAC-S, and more
  • LLM-as-a-Judge metrics: FLEUR, RefFLEUR, VELA, and EXPERT
  • Classic captioning metrics: BLEU, ROUGE-L, METEOR, CIDEr, SPICE, and JaSPICE
  • Benchmarks: Composite, Flickr8k-Ex, Flickr8k-CF, Polaris, Nebula, and LongCap-Arena
Screenshot 2026-06-13 at 2 23 30

Table of Contents

Install

Requirements: Python 3.10+, git, and uv. Java is also required for METEOR/SPICE through pycocoevalcap. JaSPICE requires Docker; CaptionEvalKit builds and starts the local JaSPICE server automatically when needed.

From PyPI or a built wheel:

pip install capevalkit
capevalkit doctor
capevalkit list-metrics

From a source checkout:

git clone --recursive https://github.com/YuigaWada/CaptionEvalKit-for-VLMs.git
cd CaptionEvalKit-for-VLMs
uv tool install --editable "$PWD" --force
capevalkit list-metrics
Runtime Cache

Wheel installs use CAPEVALKIT_HOME as a runtime cache root. The default is ~/.cache/capevalkit.

~/.cache/capevalkit/
  runtime/<lock-digest>/
    metrics/
    metrics/upstreams/
    benchmarks/expected/
    overlays/
  uv/
  huggingface/

Set a different location when needed:

CAPEVALKIT_HOME=/scratch/capevalkit capevalkit doctor

Source checkouts use the repository tree directly and keep submodules in metrics/upstreams/.

For Metric Developers

Benchmark existing metrics, or evaluate your own metric without adopting a fixed metric signature.

When changing upstream submodule revisions for a release, regenerate the runtime lock:

python scripts/generate_upstream_lock.py
CLI

Run one metric on one benchmark:

capevalkit benchmark \
  --metric clipscore \
  --benchmark composite \
  --output outputs/clipscore/composite.json

Run the same metric across benchmarks:

capevalkit suite \
  --metrics clipscore \
  --benchmarks composite,flickr8k-ex,flickr8k-cf,nebula,polaris \
  --output-dir outputs/clipscore

To wire a metric through its own CLI runner, add metrics/mymetric/metric.toml:

[metric]
name = "mymetric"
python = ">=3.10,<3.12"
module = "capevalkit.metrics.mymetric"

[repository]
dir = "metrics/upstreams/mymetric"
uv_project = "metrics/upstreams/mymetric"

[runner]
command = ["python", "score.py"]

Add a minimal metrics/upstreams/mymetric/pyproject.toml:

[project]
name = "mymetric"
version = "0.1.0"
requires-python = ">=3.10,<3.12"
dependencies = []

Make metrics/upstreams/mymetric/score.py accept:

--predictions PREDICTIONS.jsonl
--references REFERENCES.jsonl
--output OUTPUT.json

Then benchmark it:

capevalkit benchmark \
  --metric mymetric \
  --benchmark composite \
  --output outputs/mymetric/composite.json
import capevalkit.api as capeval

class MyMetric:
    def __call__(self, samples):
        return {
            sample.id: float(bool(sample.prediction and sample.references))
            for sample in samples
        }

result = capeval.evaluate_metric(
    benchmark="flickr8k-cf",
    metric=MyMetric(),
    metric_name="MyMetric",
    output="outputs/mymetric/flickr8k-cf.json",
)

The callable receives CaptionSample objects and returns {sample_id: score}. Your metric can keep any internal signature.

For VLM Developers

Evaluate saved captions from files, or run your caption model on your own images.

CLI

predictions.jsonl:

{"id": "0001", "caption": "A dog runs through grass.", "image": "0001.jpg"}
{"id": "0002", "caption": "A person rides a bicycle.", "image": "0002.jpg"}

references.jsonl:

{"id": "0001", "references": ["A dog runs outside.", "A dog is in a grassy field."]}
{"id": "0002", "references": ["A cyclist rides on a road.", "A person rides a bike."]}
capevalkit score \
  --metric clipscore \
  --predictions predictions.jsonl \
  --references references.jsonl \
  --image-dir images \
  --output outputs/clipscore.json
{
  "CLIPScore": 0.73,
  "RefCLIPScore": 0.81,
  "per_item": {
    "0001": {"CLIPScore": 0.70, "RefCLIPScore": 0.78}
  }
}

Run these examples with uv run python from the repository, or install capevalkit into your own Python environment.

import capevalkit.api as capeval

def predict(batch):
    return ["A dog runs through grass." for _ in batch.images]

results = capeval.evaluate_caption_model(
    images=["images/0001.jpg", "images/0002.jpg"],
    metrics=["cider", "clipscore"],
    predict=predict,
    references=[
        ["A dog runs outside.", "A dog is in a grassy field."],
        ["A cyclist rides on a road.", "A person rides a bike."],
    ],
    batch_size=8,
    output_dir="outputs/my-model",
)

If captions are already generated, pass image-caption pairs directly:

import capevalkit.api as capeval

results = capeval.evaluate_captions(
    pairs=[
        {
            "id": "0001",
            "image": "images/0001.jpg",
            "caption": "A dog runs through grass.",
            "references": ["A dog runs outside.", "A dog is in a grassy field."],
        },
        {
            "id": "0002",
            "image": "images/0002.jpg",
            "caption": "A person rides a bicycle.",
            "references": ["A cyclist rides on a road.", "A person rides a bike."],
        },
    ],
    metrics=["cider", "clipscore"],
    output_dir="outputs/my-captions",
)

For manual caption-model control:

import capevalkit.api as capeval

def predict(batch):
    return ["A dog runs through grass." for _ in batch.images]

with capeval.CaptionEvalRun(
    images=["images/0001.jpg", "images/0002.jpg"],
    metrics=["cider", "clipscore"],
    references=[
        ["A dog runs outside.", "A dog is in a grassy field."],
        ["A cyclist rides on a road.", "A person rides a bike."],
    ],
    output_dir="outputs/my-model",
) as run:
    for batch in run.iter_batches(batch_size=8):
        run.record(batch.ids, predict(batch))

    results = run.evaluate()

Reproduce Reported Results

Preview the default reproducibility suite:

capevalkit all_reproduce --dry-run

Run one verified pair:

capevalkit all_reproduce \
  --metrics clipscore \
  --benchmarks composite

Run a launch smoke test for every default pair:

capevalkit all_reproduce --smoke --jobs 4 --gpu-jobs 1

--smoke runs one sample per pair and checks launch/output writing only. Omit it for full correlations.

Reproduction Status

Legend: โœ… reproduced, โš ๏ธ not reproduced, - no default target. For LongCap-Arena, unreproduced targets are also shown as -.

MetricCompositeFlickr8k-EXFlickr8k-CFNebulaPolarisLCA TestALCA TestB
bleuโœ…โœ…โœ…โœ…โœ…--
ciderโœ…โœ…โœ…โœ…โœ…--
clipscoreโœ…โœ…โœ…โœ…โœ…--
expertโœ…โœ…โœ…โœ…โœ…--
fleurโš ๏ธโš ๏ธโœ…----
meteorโœ…โœ…โœ…โœ…โœ…--
pacscoreโœ…โœ…โœ…โœ…โœ…--
polosโœ…โœ…โœ…โœ…โœ…--
refclipscoreโœ…โœ…โœ…โš ๏ธโš ๏ธ--
reffleurโœ…โœ…โœ…----
refpacscoreโœ…โœ…โœ…โš ๏ธโš ๏ธ--
rougeโœ…โœ…โœ…โœ…โœ…--
spiceโœ…โœ…โœ…โœ…โœ…--
vela-----โœ…โœ…

Supported Metrics

MetricUpstreamNotes
bleupycocoevalcapBLEU-1 to BLEU-4
rougepycocoevalcapROUGE-L
meteorpycocoevalcapJava METEOR through upstream
ciderpycocoevalcapCIDEr
spicepycocoevalcapSPICE
jaspiceJaSPICEJapanese SPICE-style metric; starts the JaSPICE Docker server automatically
expertEXPERTreference-free LLaVA-based metric with structured-explanation training
clipscoreCLIPScoreimage-caption CLIPScore
refclipscoreCLIPScorereference-aware CLIPScore
pacscorePACScorePAC-S
refpacscorePACScorereference-aware PAC-S
polosPolosmodel-based reference-aware metric
fleurFLEURLLaVA-based reference-free metric
reffleurFLEURreference-aware FLEUR
velaVELAlong-caption metric for desc, rel, flu

Supported Benchmarks

BenchmarkSource
compositeHugging Face yuwd/Composite
flickr8k-exHugging Face yuwd/Flickr8k-HumanEval, expert split
flickr8k-cfHugging Face yuwd/Flickr8k-HumanEval, CrowdFlower split
nebulaHugging Face Ka2ukiMatsuda/Nebula
polarisHugging Face yuwd/Polaris
longcaparena-testa-{desc,rel,flu}Hugging Face Ka2ukiMatsuda/LongCap-Arena
longcaparena-testb-{desc,rel,flu}Hugging Face Ka2ukiMatsuda/LongCap-Arena

Data and Assets

Benchmark datasets are cached on first use under <runtime-root>/.hf-cache/benchmarks/. In a source checkout, <runtime-root> is the repository root; in a wheel install, it is $CAPEVALKIT_HOME/runtime/<lock-digest>.

DatasetLoaded from
CompositeHugging Face yuwd/Composite
Flickr8k-EX / Flickr8k-CFHugging Face yuwd/Flickr8k-HumanEval
NebulaHugging Face Ka2ukiMatsuda/Nebula
PolarisHugging Face yuwd/Polaris
Spica correctionsHugging Face hiranohachiman/Spica
LongCap-ArenaHugging Face Ka2ukiMatsuda/LongCap-Arena

Model files and checkpoints are downloaded on first use by the corresponding metric runner or upstream library. First-use downloads and runtime setup print Preparing..., Downloading..., and cache-completion status lines. Direct HTTP assets and checkpoints also report byte progress.

Metric familyModel or checkpoint source
CLIPScoreOpenAI CLIP loader cache
PACScorePACScore checkpoint URL, fetched on first PACScore run
Polosupstream Polos model cache, fetched on first Polos run
FLEURHugging Face liuhaotian/llava-v1.5-13b
EXPERTHugging Face liuhaotian/llava-v1.5-13b, hjkim811/EXPERT-llava-13b-lora
VELAHugging Face Qwen/Qwen2.5-3B-Instruct, BeichenZhang/LongCLIP-L, Ka2ukiMatsuda/vela

Set IC_EVAL_REFRESH_HF_CACHE=1 to refresh cached benchmark rows and extracted images.

Local data layout

If you pass a non-repository data root, use this layout:

data/
  composite/
    en_test_composite_da2.csv
    images/
  flickr8k/
    flickr8k.json
    crowdflower_flickr8k.json
    images/
  nebula/
    images/
  polaris/
    images/

Development

uv run python -m unittest discover -s tests

Repository map:

.
โ”œโ”€โ”€ capevalkit/
โ”‚   โ”œโ”€โ”€ api.py                 public Python API endpoint
โ”‚   โ”œโ”€โ”€ interfaces/            CLI entrypoint, Python API, presenters
โ”‚   โ”œโ”€โ”€ application/           use cases and verification services
โ”‚   โ”œโ”€โ”€ domain/                evaluation/reproduction policies and value objects
โ”‚   โ””โ”€โ”€ infrastructure/        runtime, metric execution, assets, manifests, benchmark loaders
โ”œโ”€โ”€ metrics/
โ”‚   โ”œโ”€โ”€ */metric.toml          metric manifests
โ”‚   โ””โ”€โ”€ upstreams/*            upstream metric repositories
โ”œโ”€โ”€ overlays/
โ”‚   โ””โ”€โ”€ metrics/upstreams/*    uv overlays for upstream repositories
โ””โ”€โ”€ benchmarks/
    โ””โ”€โ”€ expected/              default all_reproduce expected values

Citation

If you use this toolkit, cite the original metric and benchmark papers for the implementations and reported values you rely on.