Skip to main content

Florence-2: A Unified Model for Many Vision Tasks

How Florence-2 turns detection, segmentation, grounding, captioning, and OCR into one sequence-to-sequence problem driven by task prompts, expressing spatial outputs as location tokens in the text vocabulary.

TL;DR

  • Florence-2 unifies captioning, object detection, referring expression segmentation, and OCR inside a single sequence-to-sequence model with no task-specific heads — the task prompt alone selects the behavior.
  • All spatial outputs (bounding boxes, polygons, region coordinates) are expressed as location tokens (<loc_0> … <loc_999>) appended to the standard text vocabulary, so the decoder generates spatial predictions identically to how it generates words.
  • A DaViT vision encoder produces visual tokens that are concatenated with prompt tokens and fed into a transformer encoder-decoder, trained on FLD-5B — 5.4 billion annotations across nine task categories on 126 million images.
  • Florence-2 achieves strong zero-shot transfer across spatial and semantic tasks without fine-tuning, outperforming much larger specialist models on several benchmarks after pre-training alone.

One model, many tasks via prompts

Traditional vision pipelines assign a separate model head to each task: one for detection, one for segmentation, one for captioning. Florence-2 collapses this into a single model where the task prompt token — <CAPTION>, <OD>, <REFERRING_EXPRESSION_SEGMENTATION>, <OCR> — fully determines the output format. The image encoder, the transformer encoder-decoder, and all weights remain identical across tasks.

This design means the model learns shared representations that generalize across tasks rather than task-specific feature extractors. Captioning encourages semantic understanding; detection forces spatial precision; OCR requires character-level recognition. Each task regularizes the others, producing a backbone that is simultaneously richer and more compact than any individual specialist.

The task marker identifies the task, with extra input when required, such as a referring expression. In the released processor, that marker is expanded into a natural-language instruction before tokenization. The model then generates the appropriate structured output: a sentence for captions, an interleaved sequence of label tokens and location tokens for detection, a polygon token sequence for segmentation, or plain text for <OCR> and text with quadrilateral tokens for <OCR_WITH_REGION>.

Spatial outputs as location tokens

Object detection and segmentation require predicting coordinates — bounding boxes, polygon vertices, reference points. In prior work these were typically produced by task-specific regression heads that output continuous values and are trained with dedicated losses (L1, GIoU, dice). Florence-2 removes this entirely.

The vocabulary is extended with 1,000 special tokens <loc_0> through <loc_999>. Width and height each have 1,000 bins. The pinned Microsoft processor uses loc_N = clamp(floor(coord / (axis_size / 1000)), 0, 999), with width for x and height for y. A bounding box (x₁, y₁, x₂, y₂) thus becomes the four-token sequence <loc_x1><loc_y1><loc_x2><loc_y2>, which the decoder generates with the same cross-entropy loss used for text.

Decoding uses each bin center: coord = (loc_N + 0.5) × axis_size / 1000. For coordinates within the image, including its edges, reconstruction error is at most half a bin, or 0.05% of that axis. Polygon sequences can contain more vertices, but each coordinate retains this finite quantization resolution. OCR with regions uses eight tokens for four clockwise vertices, rather than the four tokens of an object box.

This formulation has a key downstream benefit: the model never needs a hand-designed detection head. Detection, grounding, and segmentation simply become different output vocabularies that the decoder learns to produce conditioned on the task prompt.

Implementation

The location vocabulary now gives us a concrete target sequence. The offline reference below walks from task dispatch through annotated box serialization, one correctly shifted training loss, and task-specific parsing back into original image coordinates.

Authored offline CPU spatial-token protocol with annotated boxes and seeded fixed logits. No pretrained detector, tokenizer, image encoder, model predictions, native GPU performance or benchmark reproduction. The 1,000 spatial bins stay intact; synthetic text IDs and two short sequences reduce only the loss fixture. Release conventions are pinned to Microsoft's processor/model revision 5ca5edf5bd01. Equations are authored explanations of the cited operations.

1. Dispatch the task before reading its output

Original paper

Paper excerpt · PDF p. 4§3 · Task formulation; Appendix A, Table 13

Task instructions choose the response format.

Math and tensor shapes

(p,g)=D(\tau,u)
g\in\{\mathrm{text},\mathrm{box},\mathrm{quad},\mathrm{polygon}\}

D is the authored dispatch notation. Six supported task markers select text, labeled boxes, OCR quadrilaterals or polygons. The released processor expands a marker into a natural-language instruction before tokenization; segmentation additionally takes a referring expression. Dispatch is not inference. This subset rejects unknown tasks and inappropriate extra input. OCR alone is plain text; OCR_WITH_REGION needs eight coordinates per text region.

One task marker → prompt string and declared response grammar

Pinned Microsoft source convention

Python implementation

1. Dispatch the task before reading its output
python
# A supported subset of the release processor's task dispatch.
TASKS = {
    "<CAPTION>": ("text", "What does the image describe?"),
    "<OD>": ("boxes", "Locate the objects with category name in the image."),
    "<DENSE_REGION_CAPTION>": (
        "boxes",
        "Locate the objects in the image, with their descriptions.",
    ),
    "<OCR>": ("text", "What is the text in the image?"),
    "<OCR_WITH_REGION>": (
        "ocr_quad",
        "What is the text in the image, with regions?",
    ),
    "<REFERRING_EXPRESSION_SEGMENTATION>": (
        "polygon",
        "Locate {input} in the image with mask",
    ),
}
GRAMMARS = {
    "boxes": "label + four location tokens (x0,y0,x1,y1), repeated",
    "text": "plain text, without spatial tokens",
    "ocr_quad": "text + eight location tokens (four clockwise vertices)",
    "polygon": "clockwise x,y pairs; <sep> parts and <poly>...</poly> groups",
}


def task_prompt(task, text=""):
    """Dispatch a supported task and construct its released text prompt.

    Args:
        task: Exact task marker from TASKS.
        text: Referring expression for the segmentation task only.

    Returns:
        A dictionary with task, prompt, format, and declared grammar.

    Raises:
        ValueError: For unsupported tasks or incorrect task input.
    """
    if not isinstance(task, str) or task not in TASKS:
        raise ValueError("Unsupported task marker.")
    if not isinstance(text, str):
        raise ValueError("Task input must be text.")
    kind, template = TASKS[task]
    needs_input = "{input}" in template
    if needs_input and (not text.strip() or "<" in text or ">" in text):
        raise ValueError("A nonempty referring expression is required.")
    if not needs_input and text:
        raise ValueError("This task takes no additional text.")
    return {
        "task": task,
        "prompt": template.format(input=text),
        "format": kind,
        "grammar": GRAMMARS[kind],
    }

2. Floor each coordinate on its own image axis

Original paper

Paper excerpt · PDF p. 4§3 · Region representation, 1,000 bins

Spatial targets use discrete vocabulary entries.

Math and tensor shapes

s=(W,H,W,H)
\tilde q_j=\left\lfloor c_j/(s_j/1000)\right\rfloor
q_j=\operatorname{clip}_{[0,999]}(\tilde q_j)

First floor the scaled coordinate, then clamp its integer bin. The paper specifies 1,000 bins; the pinned release supplies floor and clamp. Divide x by width/1000 and y by height/1000, so rectangular images retain the correct axes. An exact right or bottom edge maps to bin 999, rather than an invalid bin 1000. Out-of-image coordinates clamp to the endpoint bins; the reconstruction bound applies to in-image coordinates, not the original outside values.

Pixel boxes [N, 4] → integer bins [N, 4]; smoke image 640 × 360

Pinned Microsoft source convention

Python implementation

2. Floor each coordinate on its own image axis
python
def quantize(boxes, size):
    """Floor and clamp [N,4] boxes using width for x and height for y.

    Coordinates outside the image are clamped, matching BoxQuantizer. The
    half-bin error bound applies to coordinates inside the closed image.
    """
    scales = image_scale(size)
    values = box_tensor(boxes)
    return (values / (scales / BINS)).floor().clamp(0, BINS - 1).long()

3. Serialize each annotation as label and four location tokens

Original paper

Paper excerpt · PDF p. 4§3 · Box, quadrilateral and polygon representations

Each region format has its own coordinate order.

Math and tensor shapes

b=(x_0,y_0,x_1,y_1)
L(q)=\mathrm{loc}(q_0)\Vert\cdots\Vert\mathrm{loc}(q_3)
r=\mathrm{label}\Vert L(q)

The strict serializer accepts annotated object boxes or dense-region captions and concatenates their labels and tokens. It makes no predictions and does not simulate a BPE tokenizer: labels remain strings, while the smoke loss uses declared synthetic label IDs. OCR quadrilaterals instead require four clockwise vertices, eight location tokens. Polygon output uses clockwise x,y pairs, with separate parts/groups represented by <sep> and <poly>…</poly> in the processor. Those two grammars are declared separately and this box serializer does not implement them.

Two annotated [x0, y0, x1, y1] boxes → one label/token body

Pinned Microsoft source convention

Python implementation

3. Serialize each annotation as label and four location tokens
python
def serialize(labels, boxes, size, task="<OD>"):
    """Serialize annotated boxes, not inferred boxes, as label + four tokens.

    This strict subset requires nonempty labels without token delimiters.
    Polygon parts and OCR quadrilaterals have separate grammars and are not
    accepted by this four-coordinate serializer. No BPE tokenizer is modeled.
    """
    if task_prompt(task)["format"] != "boxes":
        raise ValueError("The box serializer requires a box task.")
    bins = quantize(boxes, size)
    if not isinstance(labels, (list, tuple)) or len(labels) != len(bins):
        raise ValueError("One label is required per box.")
    pieces = []
    for label, corners in zip(labels, bins.tolist()):
        if (
            not isinstance(label, str)
            or not label.strip()
            or "<" in label
            or ">" in label
            or label != label.strip()
        ):
            raise ValueError("Labels must be stripped text without brackets.")
        pieces.append(label + "".join(f"<loc_{bin_id}>" for bin_id in corners))
    return "".join(pieces)

4. Shift decoder inputs once, then ignore padding in the loss

Original paper

Paper excerpt · PDF p. 4§3 · Equation 1, common autoregressive objective

The release trains aligned logits against target labels.

Math and tensor shapes

d_0=\mathrm{EOS},\quad d_t=y_{t-1}
S=\{(b,t):y_{b,t}\ne\mathrm{PAD}\}
\ell_{b,t}=-\log p_{b,t}(y_{b,t})
\mathcal L=\frac1{|S|}\sum_{(b,t)\in S}\ell_{b,t}

The release uses decoder-start ID 2 (EOS) and PAD ID 1. Replace target padding with -100, shift those labels right into decoder inputs, and restore ignored inputs to PAD. Logits at position t predict the unshifted target at t; do not apply a second causal-LM slice. The paper sums token losses; the released CrossEntropyLoss averages valid targets. Fixed seeded logits expose that alignment without a model. An independent scalar log-sum-exp and analytic derivative oracle checks loss and every logit gradient. Ignored positions have zero direct logit gradient. All-padding batches return differentiable zero as an explicit authored extension.

Targets / decoder inputs [2, 11]; fixed logits [2, 11, 1010]

Pinned Microsoft source convention

Python implementation

4. Shift decoder inputs once, then ignore padding in the loss
python
def teacher_forcing(target_ids, logits):
    """Right-shift decoder inputs once; score same-position target labels.

    Args:
        target_ids: CPU int64 [B,T] IDs, with PAD=1 only in suffix padding.
        logits: Finite CPU floating [B,T,V] fixed prediction logits.

    Returns:
        Decoder input IDs, labels with PAD replaced by -100, and mean CE.
        All-padding batches return differentiable zero (authored extension).

    Raises:
        ValueError: For invalid shapes, types, IDs, or padding placement.
    """
    if (
        not isinstance(target_ids, torch.Tensor)
        or target_ids.dtype != torch.long
        or target_ids.ndim != 2
        or min(target_ids.shape) < 1
        or target_ids.device.type != "cpu"
    ):
        raise ValueError("Targets must be nonempty CPU int64 [B,T].")
    if (
        not isinstance(logits, torch.Tensor)
        or logits.ndim != 3
        or logits.shape[:2] != target_ids.shape
        or logits.shape[2] <= 2
        or not logits.is_floating_point()
        or logits.device.type != "cpu"
        or not torch.isfinite(logits).all()
    ):
        raise ValueError("Logits must be finite CPU float [B,T,V].")
    if (target_ids < 0).any() or (target_ids >= logits.shape[2]).any():
        raise ValueError("Target IDs must belong to the logit vocabulary.")
    padding = target_ids == PAD
    if (padding[:, :-1] & ~padding[:, 1:]).any():
        raise ValueError("Padding must be a suffix.")
    labels = target_ids.masked_fill(padding, -100)
    decoder_inputs = torch.full_like(target_ids, PAD)
    decoder_inputs[:, 0] = DECODER_START
    decoder_inputs[:, 1:] = labels[:, :-1]
    decoder_inputs.masked_fill_(decoder_inputs == -100, PAD)
    if (~padding).any():
        loss = functional.cross_entropy(
            logits.reshape(-1, logits.shape[-1]),
            labels.reshape(-1),
            ignore_index=-100,
            reduction="mean",
        )
    else:
        loss = logits.sum() * 0
    return decoder_inputs, labels, loss

5. Parse the complete body and decode bin centers

Original paper

Paper excerpt · PDF p. 4§3 · Task-specific region formats and Figure 2

Post-processing returns coordinates in the original image space.

Math and tensor shapes

\hat c_j=(q_j+\tfrac12)\frac{s_j}{1000}
|\hat c_j-c_j|\le\frac{s_j}{2000}
0\le c_j\le s_j

Decode the center of each bin, not q/999 times the image size. At the image boundaries the error is exactly half a bin: 0.32 px horizontally and 0.18 px vertically for 640 × 360. This authored strict body parser consumes every character, checks bin range and corner order, and rejects missing tokens, trailing fragments and BOS/EOS/PAD wrappers. Microsoft’s permissive regex parser can skip unmatched spans; this is not a drop-in reproduction of that policy. Caption/OCR text uses a separate path. Polygon and OCR-region parsing deliberately raise an unsupported-format error instead of interpreting four coordinates as a mask or quadrilateral.

Box body → {task: {bboxes: [N, 4], labels: [N]}}

Pinned Microsoft source convention

Python implementation

5. Parse the complete body and decode bin centers
python
def dequantize(bins, size):
    """Decode integer [N,4] bins to centers in original image coordinates."""
    scales = image_scale(size)
    values = torch.as_tensor(bins)
    if (
        values.device.type != "cpu"
        or values.ndim != 2
        or values.shape[1] != 4
        or values.dtype == torch.bool
        or values.is_floating_point()
        or values.is_complex()
        or (values < 0).any()
        or (values >= BINS).any()
    ):
        raise ValueError("Location bins must be integer [N,4] in [0,999].")
    decoded = (values.to(torch.float64) + 0.5) * (scales / BINS)
    return box_tensor(decoded)


def parse_dequantize(sequence, task, size):
    """Parse the complete box/text body; reject every malformed remainder.

    Unlike the release regex parser, this authored strict interface never
    skips unmatched spans or accepts out-of-range bins. Caller supplies the
    body only, without BOS/EOS/PAD wrappers. Polygon/OCR-region formats are
    declared but deliberately unsupported, rather than read as boxes.
    """
    kind = task_prompt(
        task, "object" if task == "<REFERRING_EXPRESSION_SEGMENTATION>" else ""
    )["format"]
    image_scale(size)
    if not isinstance(sequence, str):
        raise ValueError("Sequence body must be text.")
    if kind in ("polygon", "ocr_quad"):
        raise NotImplementedError("This interface only parses boxes/text.")
    if kind == "text":
        if "<" in sequence or ">" in sequence:
            raise ValueError("Text output cannot contain special tokens.")
        return {task: sequence}
    record = re.compile(r"([^<>]+)((?:<loc_[0-9]+>){4})")
    labels, bins, cursor = [], [], 0
    while cursor < len(sequence):
        match = record.match(sequence, cursor)
        if match is None or match[1] != match[1].strip():
            raise ValueError("Malformed label/box sequence.")
        labels.append(match[1])
        bins.append([int(value) for value in re.findall(r"[0-9]+", match[2])])
        cursor = match.end()
    corners = torch.tensor(bins, dtype=torch.long).reshape(-1, 4)
    boxes = dequantize(corners, size).tolist()
    return {task: {"bboxes": boxes, "labels": labels}}

Execute the source behind these highlights. Smoke JSON records source SHA-256, pinned revision, seed, shapes, serialized annotated boxes, parsed coordinates, decoder inputs, 14 valid targets, finite mean loss, gradients and the rectangular half-bin reconstruction error.

florence2.pyrequirements-florence2.txt

python -m pip install -r requirements-florence2.txt
python florence2.py --smoke --seed 7

This protocol supplies the targets and output fields. The shared model described next learns to produce those targets from an image and its instruction.

A single seq2seq architecture

Florence-2’s backbone is a standard encoder-decoder transformer with one non-standard component: the image encoder.

DaViT image encoder. Instead of a plain ViT, Florence-2 uses a DaViT (Dual Attention Vision Transformer) that applies both channel attention and spatial self-attention in each block. The hierarchical design produces multi-scale features that are then flattened into a sequence of visual tokens. The paper initializes the image encoder from UniCL and the multimodal encoder-decoder from BART, then trains on FLD-5B (Section 6.1).

Prompt concatenation. The processor expands a task marker such as <OD> into its instruction text, which is tokenized with a standard BPE tokenizer. The visual tokens from DaViT are prepended to the prompt tokens, forming a joint sequence that is passed to the transformer encoder.

Transformer encoder-decoder. A standard cross-attention encoder-decoder with masked self-attention in the decoder. The decoder attends over the encoded visual+prompt sequence via cross-attention and generates the output sequence autoregressively. Output tokens are drawn from the extended vocabulary (text + 1,000 location tokens). The training objective is cross-entropy over this combined vocabulary.

FLD-5B training data. The model is trained on the Florence Large-scale Dataset (FLD-5B), which contains 5.4 billion annotations on 126 million images across nine task types: image-level (caption, classification), region-level (object detection, dense region caption, region proposal, referencing), and pixel-level (semantic segmentation, expression segmentation, OCR). FLD-5B was assembled by an automated annotation pipeline that combined public datasets, internet-crawled data, and machine-generated annotations, then filtered for quality.

Why it mattered

Before Florence-2, building a vision system that handled detection, grounding, segmentation, captioning, and OCR required five separate models with five separate training pipelines and five separate inference stacks. Florence-2 compressed all of this into a single compact model trained with one objective.

The location-token formulation was the key insight: by treating coordinates as vocabulary items rather than continuous regression targets, spatial tasks become text generation tasks. This allowed Florence-2 to inherit all the benefits of large-scale language model pre-training (efficient scaling, prompt-based task switching, transferability) while retaining precise spatial capabilities that earlier vision-language models lacked.

Florence-2-base (232M parameters) and Florence-2-large (771M parameters) both demonstrate strong zero-shot performance across tasks, and after fine-tuning on task-specific data they are competitive with much larger specialist models. The design has since influenced subsequent unified vision models that adopt similar prompt-and-token-sequence formulations.

  • SAM — a complementary foundation model for segmentation that uses spatial prompts (points, boxes) rather than text tokens, showing an alternative design for promptable spatial understanding
  • DETR — the transformer-based detection architecture that first brought encoder-decoder seq2seq thinking to object detection, paving the way for Florence-2’s unified formulation
  • CLIP — the contrastive vision-language pretraining approach that established joint image-text embedding spaces, which Florence-2 extends to generative seq2seq modeling across spatial tasks

If you found this paper review helpful, consider sharing it with others.

Mastodon