From: allenai <huggingface.co/allenai>Newsgroups: alt.freeweights.olmoSubject:

Molmo2-4B

Date: 2026-11-14Commit: 042abfaSigned: 2DC28CC7B26A283B ✓

Image text to text model with 4.9B parameters. Safetensors weights, 19.4 GB in all. Published by allenai under Apache-2.0.

4.9B params19.4GBMolmo2-4Bsafetensorspermissive
FactsParams4.9BSize19.4 GBFormatssafetensorsTaskimage text to textLicenseApache-2.0From sourcepermissive
$ fw get allenai/Molmo2-4B
# manifest sha256 c99542749f5e… · 19.4 GB · 25 files
$ fw verify ./Molmo2-4B allenai/Molmo2-4B
From sourceHosted (signed)allenai/Molmo2-4BLast check: 2026-11-14 21:13 UTC

Get help: custom fine-tune or private deploy. We review your setup before we quote.

Fine-tune of Qwen3-4B-Instruct-2507.

Works with

Overview

Size
19.4 GB
Weights
19.4 GB (safetensors)
Parameters
4.9B
Formats
safetensors
Task
image text to text
License
Apache-2.0 (permissive)
Files
25, each with a sha256
Publisher
allenai
Added
2026-11-14
Section
alt.freeweights.olmo
Lineage

Original release from the publisher.

Base model: Qwen/Qwen3-4B-Instruct-2507

Where these bytes come from
Publisher at the pinned commit→You

Sources are tried in order. A file counts only when its size and sha256 match the manifest. Sources are checked when you ask for them, not on a timer.

Sources
BadgeHostLast checkResult
From sourcehuggingface.co2026-11-14 21:13 UTCpassed

Model card

From the publisher at commit 042abfa. Shown as plain text with links; images open at the publisher.

Image: Logo for the Molmo2 Project

Molmo2-4B#

Molmo2 is a family of open vision-language models developed by the Allen Institute for AI (Ai2) that support image, video and multi-image understanding and grounding. Molmo2 models are trained on publicly available third party datasets as referenced in our technical report and Molmo2 data, a collection of datasets with highly-curated image-text and video-text pairs. It has state-of-the-art performance among multimodal models with a similar size. You can find all models in the Molmo2 family here.

Learn more about the Molmo2 family in our announcement blog post.

Molmo2-4B is based on Qwen3-4B-Instruct and uses SigLIP 2 as vision backbone. It outperforms others in the class of open weight and data models on short videos, counting, and captioning, and is competitive on long-videos.

Ai2 is commited to open science. The Molmo2 datasets are available here. All other artifacts used in creating Molmo2 (training code, evaluations, intermediate checkpoints) will be made available at a later date, furthering our commitment to open-source AI development and reproducibility.

Quick links:

Quick Start#

Setup Conda Environment#
conda create --name transformers4571 python=3.11
conda activate transformers4571
pip install transformers==4.57.1
pip install torch pillow einops torchvision accelerate decord2 molmo_utils
General Video QA#
from transformers import AutoProcessor, AutoModelForImageTextToText
import torch

model_id="allenai/Molmo2-4B"

# load the processor
processor = AutoProcessor.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto"
)

# load the model
model = AutoModelForImageTextToText.from_pretrained(
     model_id,
     trust_remote_code=True,
     dtype="auto",
     device_map="auto"
)

# process the video and text
messages = [
    {
        "role": "user",
        "content": [
            dict(type="text", text="Which animal appears in the video?"),
            dict(type="video", video="https://storage.googleapis.com/oe-training-public/demo_videos/many_penguins.mp4"),
        ],
    }
]

inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_tensors="pt",
    return_dict=True,
)

inputs = {k: v.to(model.device) for k, v in inputs.items()}

# generate output
with torch.inference_mode():
    generated_ids = model.generate(**inputs, max_new_tokens=2048)

# only get generated tokens; decode them to text
generated_tokens = generated_ids[0, inputs['input_ids'].size(1):]
generated_text = processor.tokenizer.decode(generated_tokens, skip_special_tokens=True)

# print the generated text
print(generated_text)
Pointing Video QA#
from transformers import AutoProcessor, AutoModelForImageTextToText
import torch
from molmo_utils import process_vision_info
import re

model_id="allenai/Molmo2-4B"

# load the processor
processor = AutoProcessor.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto"
)

# load the model
model = AutoModelForImageTextToText.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto"
)

COORD_REGEX = re.compile(rf"<(?:points|tracks).*? coords=\"([0-9\t:;, .]+)\"/?>")
FRAME_REGEX = re.compile(rf"(?:^|\t|:|,|;)([0-9\.]+) ([0-9\. ]+)")
POINTS_REGEX = re.compile(r"([0-9]+) ([0-9]{3,4}) ([0-9]{3,4})")

def _points_from_num_str(text, image_w, image_h, extract_ids=False):
    all_points = []
    for points in POINTS_REGEX.finditer(text):
        ix, x, y = points.group(1), points.group(2), points.group(3)
        # our points format assume coordinates are scaled by 1000
        x, y = float(x)/1000*image_w, float(y)/1000*image_h
        if 0 <= x <= image_w and 0 <= y <= image_h:
            yield ix, x, y


def extract_video_points(text, image_w, image_h, extract_ids=False):
    """Extract video pointing coordinates as a flattened list of (t, x, y) triplets from model output text."""
    all_points = []
    for coord in COORD_REGEX.finditer(text):
        for point_grp in FRAME_REGEX.finditer(coord.group(1)):
            frame_id = float(point_grp.group(1))
            w, h = (image_w, image_h)
            for idx, x, y in _points_from_num_str(point_grp.group(2), w, h):
                if extract_ids:
                    all_points.append((frame_id, idx, x, y))
                else:
                    all_points.append((frame_id, x, y))
    return all_points

messages = [
    {
        "role": "user",
        "content": [
            dict(type="text", text="Point to the penguins."),
            dict(type="video", video="https://storage.googleapis.com/oe-training-public/demo_videos/many_penguins.mp4"),
        ],
    }
]

# process the video using `molmo_utils.process_vision_info`
_, videos, video_kwargs = process_vision_info(messages)
videos, video_metadatas = zip(*videos)
videos, video_metadatas = list(videos), list(video_metadatas)

# apply the chat template to the input messages
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)

# process the video and text
inputs = processor(
    videos=videos,
    video_metadata=video_metadatas,
    text=text,
    padding=True,
    return_tensors="pt",
    **video_kwargs,
)

inputs = {k: v.to(model.device) for k, v in inputs.items()}

# generate output
with torch.inference_mode():
    generated_ids = model.generate(**inputs, max_new_tokens=2048)

# only get generated tokens; decode them to text
generated_tokens = generated_ids[0, inputs['input_ids'].size(1):]
generated_text = processor.tokenizer.decode(generated_tokens, skip_special_tokens=True)

# decode video pointing outputs
points = extract_video_points(generated_text, image_w=video_metadatas[0]["width"], image_h=video_metadatas[0]["height"])
print(points)
Tracking Video QA#
from transformers import AutoProcessor, AutoModelForImageTextToText
import torch
from molmo_utils import process_vision_info
import re

model_id="allenai/Molmo2-4B"

# load the processor
processor = AutoProcessor.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto"
)

# load the model
model = AutoModelForImageTextToText.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto"
)

COORD_REGEX = re.compile(rf"<(?:points|tracks).*? coords=\"([0-9\t:;, .]+)\"/?>")
FRAME_REGEX = re.compile(rf"(?:^|\t|:|,|;)([0-9\.]+) ([0-9\. ]+)")
POINTS_REGEX = re.compile(r"([0-9]+) ([0-9]{3,4}) ([0-9]{3,4})")

def _points_from_num_str(text, image_w, image_h, extract_ids=False):
    all_points = []
    for points in POINTS_REGEX.finditer(text):
        ix, x, y = points.group(1), points.group(2), points.group(3)
        # our points format assume coordinates are scaled by 1000
        x, y = float(x)/1000*image_w, float(y)/1000*image_h
        if 0 <= x <= image_w and 0 <= y <= image_h:
            yield ix, x, y


def extract_video_points(text, image_w, image_h, extract_ids=False):
    """Extract video pointing coordinates as a flattened list of (t, x, y) triplets from model output text."""
    all_points = []
    for coord in COORD_REGEX.finditer(text):
        for point_grp in FRAME_REGEX.finditer(coord.group(1)):
            frame_id = float(point_grp.group(1))
            w, h = (image_w, image_h)
            for idx, x, y in _points_from_num_str(point_grp.group(2), w, h):
                if extract_ids:
                    all_points.append((frame_id, idx, x, y))
                else:
                    all_points.append((frame_id, x, y))
    return all_points

# use higher max fps for tracking
messages = [
    {
        "role": "user",
        "content": [
            dict(type="text", text="Track the player who is dunking"),
            dict(type="video", video="https://storage.googleapis.com/oe-training-public/demo_videos/arena_basketball.mp4", max_fps=8),
        ],
    }
]

# process the video using `molmo_utils.process_vision_info`
_, videos, video_kwargs = process_vision_info(messages)
videos, video_metadatas = zip(*videos)
videos, video_metadatas = list(videos), list(video_metadatas)

# apply the chat template to the input messages
text = processor.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)

# process the video and text
inputs = processor(
    videos=videos,
    video_metadata=video_metadatas,
    text=text,
    padding=True,
    return_tensors="pt",
    **video_kwargs,
)

inputs = {k: v.to(model.device) for k, v in inputs.items()}

# generate output
with torch.inference_mode():
    generated_ids = model.generate(**inputs, max_new_tokens=2048)

# only get generated tokens; decode them to text
generated_tokens = generated_ids[0, inputs['input_ids'].size(1):]
generated_text = processor.tokenizer.decode(generated_tokens, skip_special_tokens=True)

# decode video pointing outputs
points = extract_video_points(generated_text, image_w=video_metadatas[0]["width"], image_h=video_metadatas[0]["height"])
print(points)
Multi-image QA#
from transformers import AutoProcessor, AutoModelForImageTextToText
import torch
import requests
from PIL import Image

model_id="allenai/Molmo2-4B"

# load the processor
processor = AutoProcessor.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto",
)

# load the model
model = AutoModelForImageTextToText.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto",
)

# process the image and text
messages = [
    {
        "role": "user",
        "content": [
            dict(type="text", text="Compare these images."),
            dict(type="image", image=Image.open(requests.get("https://picsum.photos/id/237/536/354", stream=True).raw)),
            dict(type="image", image=Image.open(requests.get("https://vllm-public-assets.s3.us-west-2.amazonaws.com/vision_model_images/cherry_blossom.jpg", stream=True).raw))
        ],
    }
]

inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_tensors="pt",
    return_dict=True,
)

inputs = {k: v.to(model.device) for k, v in inputs.items()}

# generate output
with torch.inference_mode():
    generated_ids = model.generate(**inputs, max_new_tokens=448)

# only get generated tokens; decode them to text
generated_tokens = generated_ids[0, inputs['input_ids'].size(1):]
generated_text = processor.tokenizer.decode(generated_tokens, skip_special_tokens=True)

# print the generated text
print(generated_text)
Multi-Image Point QA#
from transformers import AutoProcessor, AutoModelForImageTextToText
import torch
import re
from PIL import Image
import requests

model_id="allenai/Molmo2-4B"

# load the processor
processor = AutoProcessor.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto",
    token=True
)

# load the model
model = AutoModelForImageTextToText.from_pretrained(
    model_id,
    trust_remote_code=True,
    dtype="auto",
    device_map="auto",
    token=True
)

COORD_REGEX = re.compile(rf"<(?:points|tracks).*? coords=\"([0-9\t:;, .]+)\"/?>")
FRAME_REGEX = re.compile(rf"(?:^|\t|:|,|;)([0-9\.]+) ([0-9\. ]+)")
POINTS_REGEX = re.compile(r"([0-9]+) ([0-9]{3,4}) ([0-9]{3,4})")

def _points_from_num_str(text, image_w, image_h, extract_ids=False):
    all_points = []
    for points in POINTS_REGEX.finditer(text):
        ix, x, y = points.group(1), points.group(2), points.group(3)
        # our points format assume coordinates are scaled by 1000
        x, y = float(x)/1000*image_w, float(y)/1000*image_h
        if 0 <= x <= image_w and 0 <= y <= image_h:
            yield ix, x, y


def extract_multi_image_points(text, image_w, image_h, extract_ids=False):
    """Extract pointing coordinates as a flattened list of (frame_id, x, y) triplets from model output text."""
    all_points = []
    if isinstance(image_w, (list, tuple)) and isinstance(image_h, (list, tuple)):
        assert len(image_w) == len(image_h)
        diff_res = True
    else:
        diff_res = False
    for coord in COORD_REGEX.finditer(text):
        for point_grp in FRAME_REGEX.finditer(coord.group(1)):
            frame_id = int(point_grp.group(1)) if diff_res else float(point_grp.group(1))
            w, h = (image_w[frame_id-1], image_h[frame_id-1]) if diff_res else (image_w, image_h)
            for idx, x, y in _points_from_num_str(point_grp.group(2), w, h):
                if extract_ids:
                    all_points.append((frame_id, idx, x, y))
                else:
                    all_points.append((frame_id, x, y))
    return all_points

# process the image and text
images = [
    Image.open(requests.get("https://storage.googleapis.com/oe-training-public/demo_images/boat1.jpeg", stream=True).raw),
    Image.open(requests.get("https://storage.googleapis.com/oe-training-public/demo_images/boat2.jpeg", stream=True).raw)
]

messages = [
    {
        "role": "user",
        "content": [
            dict(type="text", text="Point to the boats"),
            dict(type="image", image=images[0]),
            dict(type="image", image=images[1]),
        ],
    }
]

inputs = processor.apply_chat_template(
    messages,
    tokenize=True,
    add_generation_prompt=True,
    return_tensors="pt",
    return_dict=True,
)

inputs = {k: v.to(model.device) for k, v in inputs.items()}

# generate output
with torch.inference_mode():
    generated_ids = model.generate(**inputs, max_new_tokens=2048)

# only get generated tokens; decode them to text
generated_tokens = generated_ids[0, inputs['input_ids'].size(1):]
generated_text = processor.tokenizer.decode(generated_tokens, skip_special_tokens=True)

points = extract_multi_image_points(
    generated_text,
    [images[0].width, images[1].width],
    [images[0].height, images[1].height],
)
print(points)

Evaluations#

We report the Average Score on 15 Academic Benchmarks here. For details on the evals, refer to the main video results table in our technical report.

ModelAverage Score on 15 Academic Benchmarks
GPT-570.6
GPT-5 mini65.0
Gemini 3 Pro70.0
Gemini 2.5 Pro71.2
Gemini 2.5 Flash66.7
Claude Sonnet 4.559.6
InternVL3.5-4B53.4
InternVL3.5-8B54.1
Qwen3-VL-4B58.1
Qwen3-VL-8B59.5
Keye-VL-1.5-8B55.7
GLM-4.1V-9B56.9
MiniCPM-V-4.5-8B56.6
Eagle2.5-8B60.7
PLM-3B53.9
PLM-8B56.2
LLaVA-Video-7B52.7
VideoChat-Flash-7B56.1
Molmo2-4B (this model)62.8
Molmo2-8B63.1
Molmo2-7B59.7

License and Use#

This model is licensed under Apache 2.0. It is intended for research and educational use in accordance with Ai2’s Responsible Use Guidelines. This model is trained on third party datasets that are subject to academic and non-commercial research use only. Please review the sources to determine if this model is appropriate for your use case.

Files

25 files · 19.4 GBWeights 19.4 GB (safetensors). Sizes and sha256 from the signed manifest.
Show all 25 files with their sha256
FileSizesha256
.gitattributes1.57 KB34448b82c17d60fec9b65b1f093c115ddbaadc04beb1b0140b6bfed2e012a930
README.md16.3 KBcf666804da6505e06eab884ad45e57c23e1107347d0fedd64148798a5fbf844b
added_tokens.json9.92 KB8bc892fbf3933cf0c00794b0f0fa638ca22429db58d427f71ea953accabbf1e7
chat_template.jinja3.17 KBec66862fda57dc1aef90a21b2a0f64c18a280e2483206449bd251a73b8993101
config.json2.54 KB17e072e3c3b29d9be7a348c74b88658e67ccce094c31b21f87646f6cecd2a76f
configuration_molmo2.py13.9 KBe75a5ab18c298f91d81b2b1e68369812b655a573b4a6a52d59ab8535d791bf39
generation_config.json117 B103506d168927632c8f627d470611ec14e5bc38246470173713b5c28762026b0
image_processing_molmo2.py21.1 KB613bc2436936cebb1ebf0bfddda666b5c979573ba503fb904eed6eb56a706181
merges.txt1.67 MB8831e4f1a044471340f7c0a83d7bd71306a5b867e95fd870f74d0c5308a904d5
model-00001-of-00004.safetensors4.89 GB065ed844edb74cc3f3992415dc412cfae4c2f6e324de76dc84fffe3a652f6fd3
model-00002-of-00004.safetensors4.84 GBac1506897468e69d5af2f46127d30274eecb71c60745e7d27ea0319a27addbb4
model-00003-of-00004.safetensors4.84 GBda71a15c961f92361b64c3b45f5465d370c9992f032f0f4adf544d0b208ed573
model-00004-of-00004.safetensors4.82 GB519e694862a7d6d82e539424733f6f55fd8118c3c37e328c2dd942a6d4a16120
model.safetensors.index.json75.8 KBbc043f680e16ecd6f936ef55bf1979781772d37005192d2f3382385f79e42045
modeling_molmo2.py71.6 KB4a469aba3698dd1ef9b579439f363bda90ba3be2a5709e73bc10d8840f84e07a
molmo_2_logo_RGB.png26.7 KBeae441fb7cca5389e030cf2e39cdc16b725b70840cfc075e3982c64b81a11592
preprocessor_config.json555 B026ee03897ff86cb0d3f7c638145a04a253d4c0722fc5bcc3cf996bb0ebed211
processing_molmo2.py16.6 KB61ef1329d8a02caebaa5e5acd07a329fdbf9772e18ff32cd7256c09a3231360b
processor_config.json301 B1a976bcab088bcc4601906c345898558daa18f5e69ec4b1df8ab23a389db8bf2
special_tokens_map.json7.91 KBa4922e884f1f6d82a2ff339c39b2b4e15c437dc85b87816e7c1504d5645e6cd8
tokenizer.json11.5 MB95e80901c901584f416b8fd4349fd60022774b89ba4377626511f0562cc599f7
tokenizer_config.json64.4 KB090037776ede9d6522fbac368c1ce9141518a461828eefac1ab8660bd904fa1e
video_preprocessor_config.json984 Bab16df7a17cb94cf57daeab7c6760a8be8cfad1fa37258cb86ad48d09ffd1815
video_processing_molmo2.py37.0 KB91c4c6404f58dde3213b060874ce35c7f3695002f761fadf5af3f2515f770ba3
vocab.json2.78 MBca10d7e9fb3ed18575dd1e277a2579c16d108e32f27439684afa0e10b1440910
VersionsEach time the pinned upstream commit or file list changed in this repo. Newest first.
Upstream commitDateContents
042abfa7a3 pinned now manifest updated, same commit2026-11-14 21:13 UTC25 files · 19.4 GB
042abfa7a32026-11-14 21:13 UTC25 files · 19.4 GB

Use it

$ fw get allenai/Molmo2-4B
# manifest sha256 c99542749f5e… · 19.4 GB · 25 files
$ fw verify ./Molmo2-4B allenai/Molmo2-4B
fw.pyChecks the signature and every sha256, tries each source in order, resumes.
$ python3 fw.py get https://freeweights.allternit.com/manifests/allenai/Molmo2-4B.json --dir ./Molmo2-4B $ python3 fw.py verify ./Molmo2-4B ./allenai__Molmo2-4B.json
hfThe Hugging Face CLI with one setting. Same repo id.
$ HF_ENDPOINT=https://freeweights.allternit.com hf download allenai/Molmo2-4B --revision 042abfa7a38879a376cec03d949eff0aefaa0600 --local-dir ./Molmo2-4B
transformersLoad the weights in Python.
import os os.environ["HF_ENDPOINT"] = "https://freeweights.allternit.com" from transformers import AutoModelForImageTextToText, AutoProcessor processor = AutoProcessor.from_pretrained("allenai/Molmo2-4B", revision="042abfa7a38879a376cec03d949eff0aefaa0600") model = AutoModelForImageTextToText.from_pretrained("allenai/Molmo2-4B", revision="042abfa7a38879a376cec03d949eff0aefaa0600", device_map="auto")
vLLMServe an OpenAI-compatible API.
$ HF_ENDPOINT=https://freeweights.allternit.com vllm serve allenai/Molmo2-4B --revision 042abfa7a38879a376cec03d949eff0aefaa0600

License

Name
Apache License 2.0
Id
Apache-2.0
Class
Permissive
Redistribution
yes
Listing
Hosted (signed)
Decided
License as declared by the publisher. No person read it. 2026-11-14
Text in
README.md
Text sha256
cf666804da6505e06eab884ad45e57c23e1107347d0fedd64148798a5fbf844b
Terms gate
No extra terms beyond the license.

Read the license text

Licenses are checked against the license table. A custom license passes only after a person reads it.

Provenance

From: allenai <huggingface.co/allenai>Newsgroups: alt.freeweights.olmoSubject: Molmo2-4BDate: 2026-11-14Commit: 042abfaSigned: 2DC28CC7B26A283B ✓
Upstream
allenai/Molmo2-4B
Pinned commit
042abfa7a38879a376cec03d949eff0aefaa0600
Manifest
Molmo2-4B.json · .minisig
Manifest sha256
c99542749f5edccb5365aca6d1e4f5abb49dd262e2ef7488aef15305c0d50d4c
Signature
minisign, Ed25519, key 2DC28CC7B26A283B
Created
2026-11-14 21:13 UTC
Generator
freeweights-manifest/0.1.0
Takedowns
None.

How to verify

$ curl -O https://freeweights.allternit.com/manifests/allenai/Molmo2-4B.json $ curl -O https://freeweights.allternit.com/manifests/allenai/Molmo2-4B.json.minisig $ minisign -Vm Molmo2-4B.json -P RWQ7KGqyx4zCLXtUFOqvfv8L3g+8fpKJ1FsuxPyOYMF+4KufbUxKq1Oq $ shasum -a 256 <file> # compare with the Files tab

A manifest whose signature fails must be rejected. A file whose size or sha256 does not match must be discarded. Verify a download

Model tree

Parent

Qwen/Qwen3-4B-Instruct-2507 · this model is a finetune of it

1 model in the catalog name this one as their parent.

Fine-tunes
1
Fine-tunes 1
Get it
Download
$ HF_ENDPOINT=https://freeweights.allternit.com hf download allenai/Molmo2-4B
Open in Freeweights Studio

Pull, verify and run it in the Allternit app on Desktop, web or phone.

Signed manifest
minisign, key 2DC28CC7B26A283B. Molmo2-4B.json. Provenance. Takedown history: none.
From: bartowski <huggingface.co/bartowski>Subject: allenai_Olmo-3-7B-Instruct-GGUF
From: bartowski <huggingface.co/bartowski>Subject: allenai_Olmo-3-7B-Think-GGUF
From: bartowski <huggingface.co/bartowski>Subject: allenai_Olmo-3.1-32B-Instruct-GGUF
From: bartowski <huggingface.co/bartowski>Subject: allenai_Olmo-3.1-32B-Think-GGUF
From: bartowski <huggingface.co/bartowski>Subject: allenai_olmOCR-2-7B-1025-GGUF
From: bartowski <huggingface.co/bartowski>Subject: allenai_olmOCR-7B-0225-preview-GGUF