Deitke, Clark, Lee et al. (Allen Institute for AI / UW), 2024

Molmo & PixMo:
Open VLMs Without Distillation

A state-of-the-art vision-language model built without ever asking GPT-4 for a single label — by inventing data-collection tricks (talk, don't type) and a pointing dataset that lets the model answer by literally pointing at pixels.

Prerequisites: Transformers + Vision encoders (CLIP/ViT)
11
Chapters
6
Simulations

Chapter 0: The Problem

You want to build a vision-language model — a system that takes an image and a question ("where is this bus going?") and writes a fluent, correct answer. By 2024 the best ones — GPT-4o, Gemini 1.5, Claude 3.5 Sonnet — could do this beautifully. But they are proprietary: no weights, no training data, no recipe. The community could use them but not understand them.

So researchers tried to reproduce them in the open. And here is where a quiet trap snapped shut. To get good training data — millions of detailed image descriptions, millions of question-answer pairs — the easiest path is to ask an existing VLM to generate it. Feed GPT-4V an image, collect its caption, train on that caption. Datasets like ShareGPT4V did exactly this.

The distillation trap: If you train your "open" model on text generated by GPT-4V, your model is not an independent reproduction — it is a distillation of GPT-4V. It can never exceed its teacher, it inherits the teacher's biases and quirks, and worst of all, the field learns nothing about how to build a good VLM from scratch. The capability is borrowed, not understood.

Why is collecting real data so hard?

If distillation is forbidden, you need humans to write the data. But human annotation of images is notoriously bad in exactly the way that matters here. Ask a crowd worker to "describe this image in detail" and they will:

This is the real bottleneck. Not the architecture (the architecture is almost boring — a vision encoder bolted to an LLM). The bottleneck is data you can actually trust and that is actually detailed.

The paper's bet: Molmo's headline claim is that with the same standard architecture everyone else uses, the deciding factor is data. If you can collect high-quality, detailed, VLM-free data cheaply, you can match or beat the proprietary giants. The whole paper is really a paper about data collection engineering wearing a model's clothing.
Why does training an "open" VLM on captions generated by GPT-4V undermine the scientific goal?

Chapter 1: The Key Insight

If detailed captions are impossible to type, change the modality. Molmo's central data trick is almost embarrassingly simple: don't ask people to type — ask them to talk.

The insight (modality switching): Show an annotator an image and ask them to speak a description for 60–90 seconds. Speaking is effortless compared to typing, so people naturally produce far more detail in far less time. Transcribe the speech with an off-the-shelf speech-to-text system, then have a language-only LLM (which has never seen the image) clean up the transcript into a polished caption. No VLM ever touches the pipeline.

Two beautiful side effects fall out of this choice:

The second pillar: pointing

The other half of the insight attacks a different weakness. VLMs are bad at grounding — connecting a word to a specific place in the image — and at counting. Molmo's fix is to collect data where annotators don't describe, they point: click directly on the pixels that answer the question.

Why points beat boxes: Drawing a bounding box requires two corners and care about extent. Clicking a point is one fast action. So you can collect grounding data far faster — Molmo gathered 2.3 million point annotations. And points unlock a trick: the model can count by pointing, emitting one point per object as a chain-of-thought, which is far more reliable than predicting a number directly.

The whole paper in one breath

PixMo data (no VLM)
Speak-then-clean captions (712k), interactive Q&A, 2.3M points, plus synthetic clocks/docs/counting.
Standard architecture
ViT vision encoder + connector + pretrained LLM. Nothing exotic — overlapping crops & attention-pool connector are the only tweaks.
Simple 2-stage training
Pre-train all params on captions, then fine-tune on the data mix. Beats Claude 3.5 Sonnet & Gemini 1.5 Pro.
What is the "modality switching" trick at the heart of PixMo-Cap?

Chapter 2: Background — How a VLM Is Built

Before the tricks, let's nail down the standard VLM template Molmo uses, because everything downstream is a tweak to it. A modern VLM has four parts, and the magic is that three of them already exist as pretrained components — you are mostly learning how to glue them.

1. Pre-processor
Turns the input image into multiple square crops at the ViT's fixed resolution (multiscale, multi-crop).
2. ViT image encoder
A CLIP-pretrained Vision Transformer. Each crop → a grid of per-patch feature vectors. Crops encoded independently.
3. Connector
Pools & projects patch features into the LLM's embedding space, so the LLM "sees" image tokens as if they were word tokens.
4. Decoder LLM
A pretrained text model (OLMo, Qwen2). Reads image tokens + the question, autoregressively writes the answer.

The data flow, with shapes

This is the part people skip — and it's the part that makes the model concrete. Let's trace one image of size 336×336 through the encoder (ViT-L/14, so 14-pixel patches):

StageTensor shapeWhat it means
Input crop[3, 336, 336]RGB pixels, one square crop
Patchify (14×14 patches)[576, 1024]336/14 = 24, so 24×24 = 576 patches, each a 1024-dim feature
2×2 attention-pool (Ch 4)[144, D]576/4 = 144 vision tokens after pooling
MLP project to LLM space[144, dllm]now the same width as word embeddings — the LLM can read them

So the image becomes a sequence of "visual word" tokens that get prepended to the text tokens. The LLM doesn't know or care that some tokens came from pixels — it just attends over the whole sequence.

Why this matters for everything else: The LLM's context is finite and expensive. A high-resolution image could produce thousands of patches. Two of Molmo's tweaks — overlapping crops (Ch 3) and the attention-pooling connector (Ch 4) — exist precisely to control how many vision tokens you spend and how much context each one carries. Token budget is the hidden constraint behind the architecture.

What Molmo keeps standard (on purpose)

The authors deliberately resist exotic architecture. They use OpenAI's CLIP ViT-L/14 336px as the encoder and swap LLMs across scales: OLMoE-1B-7B (a mixture-of-experts, their most efficient), OLMo-7B, Qwen2-7B (→ Molmo-7B-D), and Qwen2-72B (their best). Same data and recipe for all — only learning rates differ. The message: hold the architecture fixed, vary the data, and watch the data do the work.

In the VLM template, what is the connector's job?

Chapter 3: Overlapping Multi-crop

Here is a real headache. Most ViTs only accept square images at a fixed, fairly low resolution — say 336×336. But the interesting tasks (reading a street sign, parsing a chart) need high resolution. If you shrink a 1500×1000 photo down to 336×336, the text becomes an unreadable blur. So how do you feed a high-res image into a low-res encoder?

The standard answer (used by many VLMs): tile the image into multiple square crops. Each crop is encoded independently by the ViT at full 336px, and you also include the whole image resized to 336 as a low-resolution "overview." More crops = more effective resolution.

The border problem: When you slice an image into tiles, every patch on the edge of a crop loses its neighboring context — the rest of the object lives in the adjacent tile and the ViT never sees them together. Imagine a bike's brand name straddling a crop boundary: one tile shows "HIGH", the next shows "LAND", and neither crop ever encodes the whole word "HIGHLAND" as a unit. OCR breaks at exactly the seams.

Molmo's fix: let crops overlap

Allow adjacent crops to share a margin of pixels. Now a patch near a seam appears inside a crop where its neighbors are also visible, so the ViT can encode it with context. The bike's brand name is now fully inside at least one crop. This is the difference shown in the paper's Figure 3, and it is the simulation below.

The subtle bookkeeping (this is the clever bit): If you simply pass every patch from every overlapping crop to the LLM, the overlap region gets encoded twice and the LLM sees duplicate tokens — wasteful and confusing. Molmo's fix: the ViT uses the overlap for context while encoding, but only the non-overlapping patch features are passed downstream. So the features that reach the connector/LLM still exactly tile the image once — no duplicates — yet each was computed with neighbor context. Overlap costs a little resolution (you tile a slightly larger area), which you offset by adding more crops.
Crop the image: with vs. without overlapInteractive

Drag the overlap slider. Watch the seams: with zero overlap, the patches straddling a boundary (highlighted) are split across crops and lose context. Add overlap and each boundary patch lands fully inside a crop. The "word" spanning the center boundary is your OCR test case.

overlap = 0%
After overlapping crops are encoded, which patch features are passed to the LLM?

Chapter 4: The Connector (SHOWCASE)

The connector is the bridge between the visual world and the language world, and Molmo makes three specific choices that the ablations show genuinely matter. Let's build it piece by piece, then drive it interactively.

Choice 1: which ViT layers?

You might assume you take the ViT's final layer output as the image features. Molmo instead concatenates features from two layers — the third-to-last and the tenth-from-last. Why? Different depths capture different things: later layers hold abstract semantics, earlier layers hold finer spatial/texture detail. Concatenating gives the LLM both. It's a small, free win (the ablation calls it "slightly better").

Choice 2: pool 2×2 windows by attention, not concatenation

576 patches per crop × many crops would blow the token budget. So we pool. The naive option: take each 2×2 block of 4 patches and just stack them into one fat vector (concatenation). Molmo instead pools each 2×2 window with a small multi-headed attention layer, where the query is the mean of the four patches and the keys/values are the four patches themselves.

Why mean-query attention beats stacking: Stacking treats all four patches as equally important and just glues them — position is baked in, importance is not learned. Attention pooling lets the window decide which of its four patches matter for this content (a corner with text gets weighted up; flat background gets weighted down). The mean serves as a neutral "summarize yourself" query. The ablation: attention pooling 76.9 vs stacking 76.1 on the 11-benchmark average — small but real, and it's the difference between a dumb and a smart downsample.

Choice 3: project with an MLP

Finally a small MLP maps the pooled vectors into the LLM's embedding dimension. Now each pooled window is a single "visual token" the LLM reads alongside words.

Arranging the tokens

Order matters because the LLM is a sequence model. Molmo lays out: low-res overview tokens first, then high-res crop tokens in row-major (left-to-right, top-to-bottom) order. Special tokens mark the start/end of the low- and high-res blocks, and row-end tokens mark row transitions — so the LLM can recover 2D structure from a 1D sequence.

Connector data flow: patches → tokensInteractive

This is the connector as a pipeline. Change the crop count and toggle the pooling mode to watch the vision-token budget change — and see how attention pooling re-weights the 2×2 window vs. stacking glomming all four together. The number that flows into the LLM is your token budget.

Token-budget worked example. One 336px crop = 24×24 = 576 patches. After 2×2 pooling: 576/4 = 144 vision tokens. With the full-image overview + 12 high-res crops that's 13 × 144 = 1872 vision tokens in the LLM context before a single word of the question. That is why pooling is not optional — without it you'd be at 7488 tokens, and the LLM context (and attention cost, which is quadratic) would explode.
Why does Molmo pool each 2×2 patch window with mean-query attention instead of just stacking the four patches?

Chapter 5: PixMo-Cap — Talking Captions

Now the data engine in detail, because this is the paper's true contribution. PixMo-Cap is the pre-training dataset: 712k images, each turned into a 200-word caption — with no VLM in the loop.

The collection pipeline, step by step

1. Source diverse images
~70 topics: street signs, memes, food, drawings, websites, even blurry photos. Diversity prevents the model from overfitting to "stock photo" framing.
2. Annotator SPEAKS 60–90s
Prompted with 7 questions to answer about the image. Speech, not typing → far more detail per minute. The audio recording is kept as anti-cheat proof.
3. Speech-to-text
Off-the-shelf ASR transcribes the audio → a raw, messy transcript with "um"s and false starts.
4. Language-only LLM cleans
A text LLM (never sees the image!) merges multiple transcripts per image or polishes a single one — removing spoken artifacts, normalizing style. Output: a clean caption.
Why a language-only LLM is allowed but a VLM is not. This is the crucial distinction. The cleaning LLM only sees text the human produced — it adds no visual knowledge, it just edits grammar and style. The visual content all came from a human looking at the image. A vision-language model, by contrast, would inject its own (proprietary, possibly wrong) perception of the image — that's distillation. Molmo's rule is precise: machines may process text, but only humans may look at pixels.

The numbers that prove it worked

Caption sourceAvg lengthNote
COCO captions11 words"A man riding a bike." Salient-only.
Localized Narratives37 wordsBetter, but still thin.
PixMo-Cap196 wordsDense: foreground, background, text, colors, mood.

And the killer ablation (Table 3b): a Molmo trained on PixMo-Cap's human transcripts (cap-F1 54.1) matches one trained on GPT-4o captions of the same images (cap-F1 52.9). Human speech, cleaned by a text LLM, is as good as distilling GPT-4o — without the distillation.

Type vs. Speak: words collected per minuteInteractive

Set how long an annotator works. Typing produces a thin caption that plateaus (fatigue, salience bias). Speaking keeps producing detail. Watch the gap — this gap is the entire reason PixMo-Cap exists.

Why is using a LANGUAGE-ONLY LLM to clean transcripts NOT considered distillation, while using a VLM would be?

Chapter 6: Pointing & Counting (SHOWCASE)

PixMo-Points is Molmo's most novel idea. Instead of describing where something is in words ("the bus is on the left"), the model emits 2D coordinates — it points at the pixels. This single capability unlocks grounding, counting, and visual explanation.

The output format

Molmo predicts points as plain-text coordinates normalized to [0, 100] — not pixel values, not special tokens. A point to Mt. Rainier looks literally like text the LLM generates:

<point x="63.5" y="44.5" alt="Mt Rainier">Mt Rainier</point>

Normalizing to 0–100 means the coordinates are resolution-independent — the same "63.5" means "63.5% across" whether the image is 400px or 4000px wide. The ablation (Table 4d) found plain-text coordinates beat dedicated special tokens (89.4 vs 85.8): the LLM is already great at generating numbers as text, so don't fight it.

Counting by pointing — a chain-of-thought for vision. Ask "how many tables?" A normal VLM blurts a single number and is often wrong, especially for 5+ objects. Molmo instead points to each table in turn, ordered top-down then left-to-right, numbering each point — then reports the count as the length of that list. This is exactly chain-of-thought reasoning, but the "thoughts" are spatial. Each point is a verifiable intermediate step, so errors are local and the final count is grounded.
Why order matters (Table 4b). Training on points in a fixed order (top-down, left-right) scores 89.4 vs 85.4 for unordered. A consistent traversal lets the model learn a scanning policy — like a human sweeping their eyes across the scene — instead of guessing random locations. It also makes "did I already count this one?" answerable from sequence position. The ablation also shows "point then count" (89.4) beats "count then point" (81.5): you must do the spatial work before committing to a number, never after.
Count by pointing: blurt vs. chain-of-pointsInteractive

Set the number of objects in the scene. "Blurt" guesses a number directly — accuracy decays as objects multiply. "Point-then-count" sweeps top-down/left-right placing one numbered point per object, then reports the list length. Press Run to watch the two strategies race.

The forward-looking bet. The authors note pointing could let VLMs act: a robot points to the object to grasp, a web agent points to the button to click, a navigation system points to the next waypoint. Pointing is grounding made actionable — a coordinate is something the world can be commanded with. (The "boxer" lifting paper and VLA work in this Veanors collection pick up exactly this thread.)
How does Molmo answer a counting question more reliably than a normal VLM?

Chapter 7: The Training Recipe

Molmo's training is deliberately simple — and a couple of details quietly save a lot of compute. There are just two stages.

Stage 1: Pre-training on captions (all params)

Train all parameters (ViT, connector, LLM) on PixMo-Cap to generate either the clean caption or one of the raw transcripts. The prompt specifies the style, and 90% of the time includes a length hint ("write ~200 words") — which the ablation (Table 2e) shows is a better pre-training task than plain captioning, because it teaches the model to control output length.

The dropped stage. Most VLMs use a separate connector-only warmup stage first (freeze ViT and LLM, train only the bridge on noisy web data) before touching the big models. Molmo finds this unnecessary when pre-training on PixMo-Cap. Instead it just gives the connector a higher learning rate with a shorter warmup so it catches up fast, then trains everything together. This deletes an entire training phase and the noisy web-scale data it needed. Concretely: LR = 2e-4 (connector), 6e-6 (ViT — tiny, it's already good), 2e-5 (LLM); warmup 200 steps for connector vs 2000 for ViT/LLM. The asymmetric LR is the warmup stage, folded in.

Stage 2: Fine-tuning on the mix

Fine-tune on a blend of all PixMo datasets plus standard academic datasets (VQA v2.0, TextVQA, ChartQA, DocVQA, AI2D, …). Datasets are sampled proportional to the square root of their size (so giant synthetic sets don't drown small ones), with pointing data up-weighted because "pointing tasks learn more slowly than QA tasks."

Style tags: keeping benchmark quirks out of chat. Academic datasets have weird answer styles — DocVQA wants verbatim text, ChartQA wants digits without commas. If you train on these naively, your friendly chatbot starts answering everything in terse benchmark-ese. Molmo's fix: prefix each such dataset's questions with a style tag like "vqa2:". The model learns to use that clipped style only when the tag is present, so normal user questions get normal fluent answers. The tag is a switch the model learns to obey.

The multi-annotation trick (a real efficiency win)

Many images have several annotations (VQA v2.0 has multiple Q&A pairs per image). Encoding the image once per annotation wastes the expensive vision forward pass. Molmo packs all annotations for an image into one long sequence and uses an attention mask so each annotation's tokens attend to the image tokens and to themselves — but not to other annotations.

Why this is mathematically free. Because annotations can't see each other, training on the packed sequence is equivalent to training on each image-text pair separately — same gradients, same loss. But you encode each image once instead of N times. The payoff: images processed drop by two-thirds, training time more than halves, for only a 25% longer sequence. This is the same idea as "document packing" in LLM training, applied to the vision bottleneck.

They train for 4 epochs with AdamW, cosine LR decaying to 10% of peak, with gradient clipping applied separately to LM, ViT, and connector (because their LR scales differ wildly).

Why does packing multiple annotations per image with a block-diagonal attention mask give the same result as separate training, but faster?

Chapter 8: The Results

Two yardsticks: academic benchmarks (11 datasets) and a human-preference Elo (15k prompts, ~870 annotators, 325k pairwise ratings). The headline: the data-first recipe pays off.

ModelBackbone11-avgWhere it lands
MolmoE-1BOLMoE-1B-7B (MoE)68.6≈ GPT-4V — at a fraction of the size
Molmo-7B-OOLMo-7B74.6between GPT-4V and GPT-4o
Molmo-7B-DQwen2-7B77.3between GPT-4V and GPT-4o
Molmo-72BQwen2-72B81.2highest academic score; Elo rank #2, behind only GPT-4o
The result that mattered. Molmo-72B beats Gemini 1.5 Pro, Gemini 1.5 Flash, and Claude 3.5 Sonnet on the academic average, and ranks second by human preference — second only to GPT-4o. An open-weights, open-data model with no distillation reached the frontier. The smallest model, MolmoE-1B, nearly matches GPT-4V — showing the data quality, not just scale, is doing the work.

Where Molmo wins, and where it doesn't

Academic average across the model landscapeInteractive

Each bar is a model's 11-benchmark average. Toggle the openness filter to see who is proprietary, open-weights-only, or fully open (weights + data). The Molmo family is highlighted — note a fully-open model sitting at the top of the chart.

On which task family does Molmo lead ALL models, including proprietary ones?

Chapter 9: Ablations — What Actually Mattered

The ablations are the most scientifically valuable part of the paper, because they isolate which choices earned the performance. Two cheap proxy metrics drive them: cap-F1 (precision/recall of post-pretraining captions — a fast read on broad image understanding) and 11-avg (the benchmark suite average). Let's read the key findings as design lessons.

ChoiceLoserWinnerLesson
Croppingsingle 46.7 / multi-no-overlap 53.4multi-overlap 54.1Resolution & context both matter; overlap fixes seams
Poolingstacking 76.1attention 76.9Let the window learn what to keep
Length hintoff 76.2on 76.9Controllable length is a better pretrain task
Dropoutoff 74.6text-only 76.9Drop text → forces reliance on the image
Point orderunordered 85.4ordered 89.4A scanning policy beats random guessing
Point encodingspecial tokens 85.8plain text 89.4Don't fight the LLM's number fluency
The deepest ablation: vision encoder almost doesn't matter. CLIP, MetaCLIP, and SigLIP all score ~54.1 cap-F1 — within noise. Even DINOv2, trained on images only with no text and no labels, is competitive (53.2). This is a striking result: it says the encoder's job (good per-patch features) is largely solved, and the VLM's quality is decided downstream — by the connector, the LLM, and above all the data. It also means you can build a 100%-open model (MetaCLIP encoder + OLMo LLM) with every bit of data public.
Text-only dropout — a clever forcing function. During pre-training, dropout is applied to the caption text tokens only, never the image tokens. Why? Randomly hiding text tokens means the model can't lean on the easy crutch of "predict the next word from the previous words" (language priors). To fill the dropped slots it must actually look at the image. The ablation: 76.9 with text-only dropout vs 74.6 with none. You engineered the model into using its eyes.
Data-scaling is the punchline (Table 3a). Increasing PixMo-Cap from 0 → 712k images lifts cap-F1 from ~49.6 (at 12.5%) to 54.1 (at 100%) and the benchmark avg from 75.5 → 76.9 — monotonically. The architecture was fixed throughout. More high-quality, VLM-free data = strictly better. That single curve is the thesis of the entire paper, quantified.
What surprising thing did the vision-encoder ablation reveal?

Chapter 10: Connections & Code

Molmo's lasting lesson: in 2024, the architecture of a VLM is nearly a solved, commodity template — the differentiator is data you can trust. And the way to get trustworthy data is to engineer the annotation task so humans naturally produce quality (talk, don't type; click, don't box).

The core insight as runnable Python

Two ideas you can implement today. First, the attention-pooling connector (mean-query over each 2×2 window):

python
import torch, torch.nn as nn

class MolmoConnector(nn.Module):
    """Pool each 2x2 ViT patch window with mean-query attention, then project."""
    def __init__(self, d_vit=1024, d_llm=4096, heads=8):
        super().__init__()
        # concat 3rd-to-last + 10th-from-last ViT layers -> 2*d_vit
        self.attn = nn.MultiheadAttention(2*d_vit, heads, batch_first=True)
        self.mlp  = nn.Sequential(nn.Linear(2*d_vit, d_llm), nn.GELU(),
                                  nn.Linear(d_llm, d_llm))

    def forward(self, patches):           # patches: [B, 24, 24, 2*d_vit]
        B, H, W, C = patches.shape
        # group into non-overlapping 2x2 windows -> [B, 144, 4, C]
        win = patches.reshape(B, H//2, 2, W//2, 2, C)
        win = win.permute(0,1,3,2,4,5).reshape(B, (H//2)*(W//2), 4, C)
        q = win.mean(dim=2, keepdim=True)          # mean of 4 patches = query
        N = win.shape[1]
        out, _ = self.attn(q.reshape(B*N,1,C),     # 1 query per window
                           win.reshape(B*N,4,C),   # 4 keys/values
                           win.reshape(B*N,4,C))
        out = out.reshape(B, N, C)                  # [B, 144, C] vision tokens
        return self.mlp(out)                        # [B, 144, d_llm]

Second, the count-by-pointing decode — the model emits points, you count them:

python
import re
# Molmo emits points as plain text, coords normalized to [0,100]:
out = ('<points x1="12.0" y1="30.5" x2="48.2" y2="29.0" '
       'x3="80.1" y3="31.5" alt="table">tables</points>')

pts = re.findall(r'x\d+="([\d.]+)" y\d+="([\d.]+)"', out)
pts = [(float(x), float(y)) for x, y in pts]
count = len(pts)                  # count = length of the point list
# de-normalize to a 640x480 image:
pixels = [(x/100*640, y/100*480) for x, y in pts]
print(count, pixels)              # 3  [(76.8,146.4),(308.5,139.2),(512.6,151.2)]

The cheat sheet

IdeaWhat it isWhy it matters
Modality switchSpeak captions (60–90s) → ASR → text-LLM clean196-word captions, no VLM, audio = anti-cheat
2D pointingPlain-text coords in [0,100], <point> tagsFast to annotate (2.3M), enables count-by-pointing
Overlapping cropsTiles share margins; only non-overlap features passed onBorder patches keep context; no duplicate tokens
Attention poolingMean-query MHA over 2×2 windowsSmart 4× token reduction; 76.9 vs 76.1
Drop connector warmupHigher LR + shorter warmup for connector insteadDeletes a whole stage & its noisy web data
Multi-annotation packingBlock-diagonal attention mask per imageEncode image once; −2/3 images, >½ time saved
Text-only dropoutDropout caption tokens, not image tokens (pretrain)Forces reliance on vision; 76.9 vs 74.6
Style tags"vqa2:" prefix triggers benchmark answer styleKeeps terse benchmark-ese out of chat

Where to go next in this collection

The takeaway to carry forward. When a field's models converge on the same architecture, the moat moves to data. Molmo's genius wasn't a new layer — it was realizing that the limiting reagent was trustworthy detailed annotations, and that you get those by redesigning the human task (speak, point) rather than by building a cleverer net. "What I cannot create, I do not understand" — and here, to create the model, they first had to create the data.
What is Molmo's single biggest lesson for building VLMs?