ConCor-1 / README.md
UWGZQ's picture
ConCor-1: release checkpoint, remote code, processor, example
4f08932 verified
|
Raw
History Blame Contribute Delete
11.1 kB
---
library_name: transformers
license: apache-2.0
pipeline_tag: image-segmentation
base_model:
- Qwen/Qwen3.5-0.8B
tags:
- vision-language-grounding
- concept-correspondence
- referring-expression-segmentation
- phrase-grounding
- open-vocabulary-segmentation
---
# ConCor-1
**Vision-Language Grounding as Bidirectional Concept Correspondence**
Jieyu Zhang\*, Ziqi Gao\*, Luke Zettlemoyer, Ranjay Krishna
(\*equal contribution) — University of Washington, Allen Institute for AI, Microsoft SuperIntelligence, FAIR at Meta
ConCor-1 recovers **all** correspondences between visually referential text spans and
instance-level image segments in an image–text pair. Unlike conventional grounding, the
text spans are **not** given as queries: the model decides which parts of the text are
visually referential, groups co-referring mentions, segments the corresponding objects,
and decides how many correspondences exist.
This single formulation covers phrase grounding, referring-expression grounding and
open-vocabulary detection/segmentation — a caption, a referring expression and a
category list are all just "the text" for ConCor-1.
## How it works
ConCor-1 uses the pretrained **Qwen3.5-0.8B** vision-language model as a *contextual
image–text encoder* rather than as a generator, and appends **385 learnable bridge
tokens** to the multimodal sequence:
```
[<vision_start>, <image_pad> × N_v, <vision_end>, text tokens × N_t, bridge tokens × 385]
```
Each bridge token represents one candidate image–text correspondence. Three lightweight
heads read its final hidden state:
| Head | Prediction | Implementation |
|---|---|---|
| `presence_head` | is this a valid correspondence? | SwiGLU MLP → scalar logit |
| `text_segmentation_head` | binary mask over the input text tokens | bridge/text features → 256-d correspondence space → bilinear scorer |
| `vision_segmentation_head` | binary mask over the image | patch expander (4 MLPs, undoing 2×2 patch merging) + pre-merger ViT feature fusion + 2-block convolutional decoder → 4-px cell grid → bilinear scorer |
Bridge tokens are organised into **multi-scale spatial groups** — grids from 1×1 to
10×10, so `Q = Σ s² = 385` — which gives them location and scale priors (coarse levels
for large instances, fine levels for small ones). The backbone's *full-attention* layers
run with a **bidirectional** mask so bridge tokens can access the complete multimodal
context, while the linear-attention layers keep Qwen3.5's pretrained behaviour. The
model is therefore **not autoregressive**: `use_cache`, `past_key_values` and
`generate()` are not supported.
Parameters (Table 4 of the paper):
| Component | Params |
|---|---|
| Qwen3.5-0.8B backbone | 852.99 M |
| Presence head | 0.52 M |
| Text segmentation head | 1.11 M |
| Vision segmentation head | 12.01 M |
| Bridge-token embeddings | 0.145 M |
| **ConCor-1 total** | **866.79 M** (+1.62 %) |
## Usage
The example below is a real validation sample, bundled as `example.png`: image
`000000000285` from COCO `val2017` with its COCONut-PanCap caption.
```python
import torch
from PIL import Image
from transformers import AutoModel, AutoProcessor
model_id = "UWGZQ/ConCor-1" # or a local path to this repository
processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
model = AutoModel.from_pretrained(
model_id,
trust_remote_code=True,
dtype=torch.bfloat16,
attn_implementation="flash_attention_2", # "sdpa" also works
).to("cuda").eval()
image = Image.open("example.png").convert("RGB")
text = (
"This image depicts a close-up of a brown bear in a natural outdoor setting. "
"The background consists of lush green grass. In the foreground, a large brown bear "
"is positioned centrally."
)
inputs = processor(images=image, text=text, return_tensors="pt").to("cuda")
with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):
outputs = model(**inputs)
correspondences = processor.post_process_correspondences(
outputs, text=text, target_sizes=[(image.height, image.width)]
)[0]
for correspondence in correspondences:
print(
f"{correspondence['presence_score']:.3f}",
correspondence["text_phrases"], # phrases of this correspondence's text mask
correspondence["text_spans"], # character spans into `text`
correspondence["mask"].shape, # bool array, (height, width)
)
```
Output — two correspondences, which is exactly what this sample is annotated with:
```
1.000 ['a brown bear', 'a large brown bear'] [[33, 45], [140, 158]] (640, 586)
0.999 ['green grass'] [[108, 119]] (640, 586)
```
Note the first line: the caption mentions the bear twice, and both mentions land in a
*single* text mask — which is why a text mask is a set of character spans rather than one
span. The ground-truth annotation for this sample groups the same two mentions
(`[[33, 45], [140, 158]]`) and pairs them with one bear mask.
`example_inference.py` wraps this up as a script, including a mask-overlay renderer:
```bash
python example_inference.py \
--image example.png \
--text "This image depicts a close-up of a brown bear in a natural outdoor setting. The background consists of lush green grass. In the foreground, a large brown bear is positioned centrally." \
--output overlay.png
```
### Inference thresholds
Post-processing follows the paper: keep bridge tokens above a presence threshold,
threshold the text and image mask probabilities, then remove duplicate correspondences
with NMS (a candidate is suppressed only when *both* its image-mask IoU and its
text-span IoU with a higher-scoring candidate exceed the threshold).
| Threshold | Default | Argument |
|---|---|---|
| presence | 0.10 | `presence_threshold` |
| text mask | 0.45 | `text_threshold` |
| image mask | 0.45 | `image_threshold` |
| NMS IoU | 0.50 | `nms_iou_threshold` |
Image masks are produced by reshaping the per-bridge logits onto their 4-px cell grid,
bilinearly upsampling **in logit space** to `target_sizes`, and then applying the
sigmoid and threshold.
### Other input modes
```python
# A category list instead of a caption (open-vocabulary detection/segmentation).
inputs = processor(images=image, text="bear . grass . tree . person", return_tensors="pt")
# -> 0.995 ['bear'] [[0, 4]] ; 0.980 ['grass'] [[7, 12]]
# the absent categories ("tree", "person") produce no correspondence
# A batch of image-text pairs (right-padded internally).
inputs = processor(images=[image_a, image_b], text=[text_a, text_b], return_tensors="pt")
results = processor.post_process_correspondences(
outputs, text=[text_a, text_b],
target_sizes=[(image_a.height, image_a.width), (image_b.height, image_b.width)],
)
# Text only: which spans are visually referential, and how do they group?
inputs = processor(images=None, text=text, return_tensors="pt") # image_mask_logits is None
```
### Raw model outputs
`model(**inputs)` returns a `ConCor1Output`:
| Field | Shape | Meaning |
|---|---|---|
| `presence_logits` | `(B, 385)` | correspondence presence |
| `text_mask_logits` | `(B, 385, N_text)` | per-bridge mask over the text tokens |
| `image_mask_logits` | `(B, 385, N_cells)` | per-bridge mask over image cells, row-major |
| `image_mask_grid_hw` | `(B, 2)` | `(height, width)` of each sample's cell grid |
So `image_mask_logits[b, q, : h * w].reshape(h, w)` is bridge `q`'s mask map, where one
cell covers 4 px of the processed image.
## Training and evaluation data
Training converts grounding, segmentation and referring-expression datasets into a
unified correspondence format (image masks + text masks + correspondence labels), using
an LLM rewriting pipeline that groups co-referring mentions and drops mentions without a
visual referent: GoldG (Flickr30k + GQA, pseudo-masks from SAM3), COCONut-PanCap,
GroundedRef-train (LLM-written referring/compositional captions over COCO panoptic),
plus COCO, COCONut, LVIS, ADE20K instance segmentation and the counting subset of
PixMo-Point — 678 k caption-style images and 709 k instance-segmentation images.
Evaluation uses Flickr30k (4 k pairs), COCONut-PanCap (3 k) and GroundedRef (2 k),
scored along three axes — text segmentation, image segmentation and joint correspondence
precision/recall/F1. See the paper for the numbers, ablations and baselines.
This checkpoint is the released main model: 100 k steps, 385 bridge tokens, a fixed
~1.0 M-pixel image budget (`min_pixels = max_pixels = 1003520`, i.e. 1024 visual tokens).
## Files
| File | Purpose |
|---|---|
| `configuration_concor1.py` | `ConCor1Config` |
| `modeling_concor1.py` | `ConCor1ForConceptCorrespondence` and its heads |
| `processing_concor1.py` | `ConCor1Processor`: sequence construction + correspondence post-processing |
| `example_inference.py` | image + text → correspondences, with mask overlay |
| `example.png` | the example image: COCO `val2017/000000000285`, from the COCONut-PanCap validation split |
| `convert_original_checkpoint.py` | converts a research training checkpoint into this repository |
| `model.safetensors` | weights: bf16 backbone, fp32 prediction heads |
The prediction heads are stored and loaded in **fp32** (via
`_keep_in_fp32_modules_strict`) exactly as the training checkpoint holds them, while the
backbone is bf16. Every head casts its inputs to its own parameter dtype, so the model
runs with or without `torch.autocast`. With `attn_implementation="flash_attention_2"`
under `torch.autocast(bfloat16)`, this repository reproduces the research code's
presence, text-mask and image-mask logits **bit-for-bit** (verified single-sample and
batched). `sdpa` and running without autocast stay within bf16 kernel noise
(binary-mask IoU ≈ 0.98–0.995 against the exact path).
## Requirements
```
torch >= 2.8
transformers >= 5.3 # needs the Qwen3.5 architecture
flash-attn >= 2.8 # optional; use attn_implementation="sdpa" instead
fla / causal-conv1d # Qwen3.5's gated-delta-net kernels
```
## Limitations
Mask quality remains behind specialized detection/segmentation models — the image-mask
head is deliberately lightweight and optimised for joint correspondence prediction
rather than segmentation quality alone. The model depends on correspondence-style
supervision that had to be converted from datasets not natively annotated that way,
which introduces noise. Grounding errors can propagate to downstream systems, and
performance may be uneven across categories, visual domains and languages, so validate
carefully before using it in human-facing or safety-critical settings.
## Demo
An interactive ZeroGPU demo lives at
[`UWGZQ/ConCor-1-demo`](https://huggingface.co/spaces/UWGZQ/ConCor-1-demo).
## Citation
```bibtex
@article{zhang2026concor,
title = {Vision-Language Grounding as Bidirectional Concept Correspondence},
author = {Zhang, Jieyu and Gao, Ziqi and Zettlemoyer, Luke and Krishna, Ranjay},
year = {2026}
}
```
The backbone is [Qwen3.5-0.8B](https://huggingface.co/Qwen/Qwen3.5-0.8B) (Apache 2.0).