Think with 3D: Geometric Imagination Grounded Spatial Reasoning from Limited Views 用 3D 思考:基于几何想象力的有限视角空间推理
3DThinker is the first framework that enables VLMs to perform "3D mentaling" during reasoning — imagining 3D spatial structures from limited-view images without any 3D prior input. Training consists of two stages: Stage 1 aligns the VLM's 3D latent with a 3D foundation model (VGGT) via supervised training; Stage 2 refines the entire reasoning trajectory through GRPO reinforcement learning using outcome-based rewards.
3DThinker 是首个让 VLM 在推理过程中进行"3D 心象"的框架——无需任何 3D 先验输入,即可从有限视角图像中想象 3D 空间结构。训练分两阶段:阶段 1 通过监督训练将 VLM 的 3D 隐表示与 3D 基础模型(VGGT)对齐;阶段 2 通过 GRPO 强化学习,仅基于结果信号优化完整推理轨迹。
Three conda environments are required — one per training stage plus an SFT env for LoRA weight merging. Select a tab to view the setup commands.
需要三套 conda 环境——每个训练阶段一套,加一套用于 LoRA 权重合并的 SFT 环境。点击 tab 查看对应安装命令。
git clone https://github.com/zhangquanchen/3DThinker.git
cd 3DThinker
conda create -n 3DThinker-stage1 python=3.10 -y && conda activate 3DThinker-stage1
pip install -r envs/requirements_stage1.txtconda create -n 3DThinker-stage2 python=3.10 -y && conda activate 3DThinker-stage2
bash 3dthinker/stage2/setup.sh
# Replace transformers with the stage1 custom version
# Replace trl with the local copy if version conflicts
cp -rf 3dthinker/stage2/package/trl \
$(python -c "import site; print(site.getsitepackages()[0])")/trlconda create -n SFT python=3.10 -y && conda activate SFT
cd SFT/env
pip install -e ".[torch,metrics]" --no-build-isolation
Download VGGT-1B weights into models/. The script processes each scene's images through VGGT and saves aggregated 3D tokens as .npz files with shape [N, 1374, 2048].
下载 VGGT-1B 权重到 models/。脚本将每个场景的图像通过 VGGT 处理,保存聚合的 3D token 为 .npz 文件,shape 为 [N, 1374, 2048]。
from vggt.models.vggt import VGGT
# Load VGGT-1B model
model = VGGT.from_pretrained("models/VGGT-1B")
model.eval().cuda()
# For each training sample: load N views, extract features
images = load_and_preprocess(image_paths) # [N, 3, H, W]
with torch.no_grad():
# VGGT outputs aggregated 3D tokens per view
features = model.forward_features(images) # [N, P_3D=1374, C=2048]
# Save features for downstream training
np.savez(output_path, feature=features.cpu().numpy())
# Output: data/feature_vggt/{idx}/vggt.npz
# Also saves resized images: data/resized_images/
Chain-of-thought reasoning traces are generated using GPT-4.1. Each trace starts with <output_3D> — the placeholder for the model's mental 3D scene — followed by structured thinking and answer blocks.
使用 GPT-4.1 生成思维链推理轨迹。每条轨迹以 <output_3D> 开头——作为模型"3D 心象"的占位符——随后是结构化的思考和回答块。
SYSTEM_PROMPT = """You are a helpful assistant that can answer questions
about 3D spatial relationships from images.
The mental 3D scene you imagine will be represented by the special token
<output_3D>, which should be presented at the beginning of your response.
Your response should follow this format:
<output_3D><think> [step-by-step spatial reasoning] </think>
<answer> [final answer letter] </answer>"""
# Generate CoT with GPT-4.1 for each QA pair
response = client.chat.completions.create(
model="gpt-4.1",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": [
{"type": "image_url", "image_url": {"url": img_url}},
{"type": "text", "text": question}
]}
]
)Four scripts run sequentially to produce the final training JSONL:
四个脚本顺序执行,生成最终训练用的 JSONL 文件:
# 1. Generate chain-of-thought reasoning data
python preprocessing/produce_cot.py
# 2. Remove non-compliant entries (e.g., missing </output> tags)
python preprocessing/clean.py
# 3. Filter low-quality / useless data
python preprocessing/remove.py
# 4. Match VGGT feature indices to training samples
python preprocessing/jsonl_add_idx.py
# Output: data/data_output3d_begin_10k_resized.jsonlEach entry in the final JSONL contains:
最终 JSONL 的每条数据包含:
{
"text_input": "Question about spatial relationship...",
"image_input": ["img1.jpg", "img2.jpg", "img3.jpg", "img4.jpg"],
"text_output": "<output_3D><think>Looking at the images, I can see...</think>\n<answer>B</answer>",
"idx": 42
}
The tokenizer is extended with three new tokens: <|latent_start|>, <|latent_pad|> (repeated latent_size=12 times), and <|latent_end|>. During preprocessing, <output_3D> is replaced with these latent tokens in the assistant's response.
分词器新增三个特殊 token:<|latent_start|>、<|latent_pad|>(重复 latent_size=12 次)和 <|latent_end|>。预处理时,助手回复中的 <output_3D> 被替换为这些隐表示 token。
def replace_visual_spectial_tokens(texts):
"""Replace <output_3D> placeholder with latent token sequence."""
update_texts = []
for text in texts:
parts = text.split("<|im_start|>assistant")
prev, after = parts[0], parts[1]
# Key replacement: vision tokens → latent tokens
after = after.replace(
"<|vision_start|><|image_pad|><|vision_end|>",
"<|latent_start|><|image_pad|><|latent_end|>"
)
update_texts.append(prev + "<|im_start|>assistant" + after)
return update_texts
The projector bridges the VLM's latent token hidden states and the VGGT 3D feature space. It takes [T=12, 2048] latent hidden states + [N=4, P=391, 2048] image patch embeddings, and produces [N, 1374, 2048] features aligned with VGGT.
投影器连接 VLM 的隐 token 隐状态和 VGGT 3D 特征空间。输入为 [T=12, 2048] 的隐状态 + [N=4, P=391, 2048] 的图像 patch 嵌入,输出与 VGGT 对齐的 [N, 1374, 2048] 特征。
class projector(nn.Module):
def __init__(self, mlp_depth, mm_hidden_size, hidden_size,
latent_size, fusion_input, fusion_output):
super().__init__()
# Project latent hidden states: mm_hidden_size → hidden_size
self.proj_3d_model = build_mlp(mlp_depth, mm_hidden_size, hidden_size)
# Compress latent tokens: latent_size(12) → 64
self.prompte_model = build_mlp(mlp_depth, latent_size, 64)
# Fuse latent + image patches: (fusion_input + 64) → fusion_output(1374)
self.fusion_model = build_mlp(mlp_depth, fusion_input + 64, fusion_output)
def forward(self, hidden_states_feature, image_embeddings):
"""
hidden_states_feature: [1, T=12, C=2048] — latent token hidden states
image_embeddings: [1, N=4, P=391, C=2048] — input image patches
"""
# Step 1: Compress latent tokens → [C, 64]
latent = self.prompte_model(
hidden_states_feature.squeeze().permute(1, 0) # [C, T=12] → [C, 64]
)
feature_proj = []
for i in range(image_embeddings.shape[1]): # loop over N views
# Step 2: Concat latent (64) + image patches (391) → [C, 455]
x = torch.cat((latent, image_embeddings[:, i, :, :]), dim=1)
# Step 3: Fuse → [C, fusion_output=1374]
x_unify = self.fusion_model(x.squeeze().permute(1, 0))
# Step 4: Project → [1374, mm_hidden_size=2048]
x_output = self.proj_3d_model(x_unify)
feature_proj.append(x_output)
# Output: [1, N=4, P_3D=1374, C=2048] — aligned with VGGT feature space
return torch.stack(feature_proj).unsqueeze(0)Stage 1 uses a combined loss: standard cross-entropy for language modeling + MSE similarity loss that aligns the projector's output with pre-extracted VGGT features.
阶段 1 使用组合损失:标准交叉熵(语言建模)+ MSE 相似度损失(将投影器输出与预提取的 VGGT 特征对齐)。
class CustomTrainerStage1(SFTTrainer):
def compute_loss(self, model, inputs, **kwargs):
# Forward pass → get logits and hidden states
outputs = model(**inputs, output_hidden_states=True)
ce_loss = outputs.loss
# Extract hidden states at latent token positions
hidden_states = outputs.hidden_states[-1] # last layer
shift_predict_embeddings = extract_latent_positions(
hidden_states, inputs["input_ids"]
)
# Project latent embeddings through the 3D projector
image_embeddings = get_image_embeddings(model, inputs)
feature_proj = model.projector_model(
shift_predict_embeddings, image_embeddings
)
# Load pre-computed VGGT features
idx = inputs["idx"]
data = np.load(f'../../data/feature_vggt/{idx[0]}/vggt.npz')
feature_3d = torch.from_numpy(data['feature']).to(device)
# feature_3d shape: [1, N=4, P_3D=1374, C=2048]
# MSE similarity loss between projector output and VGGT
feature_sim = ((feature_proj - feature_3d.detach()) ** 2).sum(dim=-1)
sim_loss = feature_sim.mean() * 0.0005
# Combined loss: 0.1 * CE + alignment
loss = 0.1 * ce_loss + sim_loss
return loss# Stage 1: Supervised Alignment Training
conda activate 3DThinker-stage1
python src/main.py \
--model Qwen2.5-VL-3B-Instruct \
--epochs 10 \
--task mindcube \
--latent_size 12 \
--per_device_train_batch_size 1 \
--learning_rate 1e-4 \
--warmup_steps 10 \
--weight_decay 0.01 \
--logging_steps 20 \
--save_steps 2000 \
--stage stage1 \
--data_path ../../data/example.jsonl \
--save_model_path ../../models/3DThinker-S1-Qwen2.5-VL-3B_mlp6_lr1e-4_latent12 \
--wandb_name 3DThinker-S1-Qwen2.5-VL-3B_mlp6_lr1e-4_latent12Stage 2 optimizes the full reasoning trajectory using Group Relative Policy Optimization (GRPO). The model generates multiple candidate responses, scores them with three reward signals, and updates the policy. No labeled 3D data is used — only outcome signals.
阶段 2 使用 Group Relative Policy Optimization (GRPO) 优化完整推理轨迹。模型生成多个候选回复,通过三个奖励信号评分,更新策略。不使用标注的 3D 数据——仅基于结果信号。
Three reward functions score each generated trajectory. Each reward returns a scalar in [0, 1] per sample; GRPO then computes group-relative advantages over the num_generations=8 rollouts and updates the LoRA weights with a KL penalty (β=0.04) against the reference policy.
三个奖励函数为每条生成的轨迹评分。每个 reward 输出 [0, 1] 区间的标量;GRPO 随后在 num_generations=8 条采样上计算组内相对优势(以均值为基线),并带 KL 惩罚(β=0.04)更新 LoRA 权重。
<think>…</think> and then <answer>…</answer>. Returns 1.0 if the structure matches, 0.0 otherwise. This keeps the model from collapsing to plain text and forces it to actually emit the 12 latent tokens that the projector will read.
<|latent_start|>⟨z₁⟩⟨z₂⟩…⟨z₁₂⟩<|latent_end|> <think>The camera rotates 90° clockwise, so the chair that was on the left should now be in front…</think> <answer>B</answer>
The chair that was on the left moves to the front after a 90° rotation, so the answer is B.
<answer> is parsed for the option letter (A–E) via priority-based regex, then compared with the ground-truth letter. Exact match → 1.0, otherwise 0.0. This is the only outcome signal that ties the trajectory to the actual benchmark task.
B:0.0.[N, 1374, 2048] feature map. We compare it against the pre-computed VGGT ground-truth features for the same scene via mean cosine similarity, and remap from [-1, 1] to [0, 1]. This rewards trajectories whose internal 3D "mental image" stays geometrically faithful, even when the answer letter alone wouldn't reveal that.
mc_00471), two rollouts that both answer B correctly:<think>…</think>,再接 <answer>…</answer>。结构命中返回 1.0,否则 0.0。作用是防止模型倒退为纯文本、并强迫它真的吐出那 12 个供投影器读取的 latent token。
<|latent_start|>⟨z₁⟩⟨z₂⟩…⟨z₁₂⟩<|latent_end|> <think>相机顺时针旋转 90°,原本在左边的椅子 现在应该出现在正前方……</think> <answer>B</answer>
原本在左边的椅子在 90° 旋转之后会到正前方, 所以答案是 B。
<answer> 内部的内容用优先级正则抽出选项字母(A–E),与真值字母严格比对:完全一致 → 1.0,否则 0.0。这是唯一直接把轨迹绑定到下游任务的 outcome 信号。
B:0.0。[N, 1374, 2048] 特征,再与预计算好的 VGGT ground-truth 特征取平均余弦相似度,最后将 [-1, 1] 映射到 [0, 1]。它奖励那些内部「心象 3D」仍然几何上忠实的轨迹 — 这是单看选项字母看不出来的。
mc_00471),两条都答对 B 的采样:In the launch script, all three are switched on simultaneously via --reward_funcs format response latent_sim, and they are summed with equal weight per rollout (no per-reward coefficient). The format reward acts as a hard gate — a malformed trajectory loses 1 point regardless of accuracy — while the answer and latent rewards push the policy toward both correct and geometrically grounded reasoning.
启动脚本中三项同时打开(--reward_funcs format response latent_sim),等权相加(没有单独的 reward 系数)。Format 奖励充当硬门槛——格式出错不论答案对不对都会扣 1 分;Response 和 Latent 则共同推动策略同时走向「答案正确」与「几何上不脱靠」两个方向。
@staticmethod
def format_reward(completions, **kwargs):
"""Reward 1: Format compliance — full trajectory must match pattern."""
# Regex: latent tokens → <think>...</think> → <answer>...</answer>
pattern = r".*?<\|latent_start\|>.*?<think>.*?</think>\s*<answer>.*?</answer>"
completion_contents = [c[0]["content"] for c in completions]
matches = [
re.search(pattern, content, re.DOTALL) is not None
for content in completion_contents
]
rewards = [1.0 if match else 0.0 for match in matches]
return rewardsdef extract_answer(xml_text):
"""Extract content between <answer> and </answer> tags."""
match = re.search(r'<answer>\n?(.*?)\n?</answer>', xml_text, re.DOTALL)
return match.group(1).strip() if match else ""
@staticmethod
def response_text_reward(completions, answer, prompts, **kwargs):
"""Reward 2: Answer accuracy — option letter matching."""
contents = [c[0]["content"] for c in completions]
rewards = []
for content, gt_response in zip(contents, answer):
pred_response = extract_answer(content)
# extract_option: priority-based regex to find A-E letter
if extract_option(pred_response) and \
extract_option(gt_response) == extract_option(pred_response):
rewards.append(1.0)
else:
rewards.append(0.0)
return rewards@staticmethod
def sim_reward(index, prompts, image_embeddings, extracted_emb, **kwargs):
"""Reward 3: 3D latent similarity — cosine sim with VGGT features."""
rewards = []
for idx, prompt in zip(index, prompts):
target_dtype = torch.float32
# Run the projector on generated latent embeddings
decoder_feature = projector_model(
extracted_emb.to(target_dtype).to("cuda:0"),
image_embeddings.to(target_dtype).to("cuda:0")
)
decoder_feature_norm = decoder_feature / decoder_feature.norm(
dim=-1, p=2, keepdim=True
)
# Load pre-computed VGGT ground truth features
data = np.load(f'../../data/feature_vggt/{idx}/vggt.npz')
feature_3d = torch.tensor(data['feature']).to(
device=decoder_feature.device, dtype=decoder_feature.dtype
).squeeze()
feature_3d_norm = feature_3d / feature_3d.norm(
dim=-1, p=2, keepdim=True
)
# Cosine similarity → [0, 1] reward
cos_sim = F.cosine_similarity(
decoder_feature_norm, feature_3d_norm
).mean()
reward = (cos_sim + 1) / 2 # map [-1,1] → [0,1]
rewards.append(reward)
return rewards# Stage 2: GRPO Reinforcement Learning
conda activate 3DThinker-stage2
# Path to Stage 1 checkpoint
model_path="../../models/3DThinker-S1-Qwen2.5-VL-3B_mlp6_lr1e-4_latent12"
EXP_NAME="3DThinker-S1+S2-Qwen2.5-VL-3B_lr1e-5_gen8"
torchrun --nproc_per_node=8 \
--nnodes=1 --node_rank=0 \
--master_addr=127.0.0.1 --master_port=12346 \
src/open_r1/grpo_jsonl.py \
--use_vllm True \
--model_name_or_path $model_path \
--output_dir checkpoints/rl/${EXP_NAME} \
--reward_funcs format response latent_sim \
--beta 0.04 \
--num_generations 8 \
--max_completion_length 2048 \
--use_peft true \
--lora_r 64 \
--lora_alpha 128 \
--lora_dropout 0.05 \
--lora_task_type CAUSAL_LM \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 2 \
--learning_rate 1e-5 \
--num_train_epochs 1 \
--freeze_vision_modules true \
--deepspeed local_scripts/zero3.json \
--bf16# Merge LoRA adapters into the base model
conda activate SFT
llamafactory-cli export merge.yaml
# merge.yaml specifies:
# model_name_or_path: checkpoints/stage1 (base)
# adapter_name_or_path: checkpoints/stage2 (LoRA)
# export_dir: checkpoints/merged (output)Evaluation follows the official MindCube benchmark protocol. Additional benchmarks include Ego3D-Bench, VSI-Bench, SPBench, CV-Bench, SPAR-Bench, ViewSpatial-Bench, and MMSI-Bench.
评估遵循 MindCube 基准的官方协议。其他基准包括 Ego3D-Bench、VSI-Bench、SPBench、CV-Bench、SPAR-Bench、ViewSpatial-Bench 和 MMSI-Bench。
Mental rotation and cross‑view object/ego pose tracking from a small set of related views.
Ego‑centric 3D reasoning: distance estimation, motion direction, travel time, and relative position of nearby objects.
Visual‑spatial intelligence on real indoor videos: counting, size and distance, route planning, and appearance order.
Single‑ and multi‑image spatial perception: depth ordering, occupancy, layout, relative direction and distance.
Classic computer‑vision reasoning from a single image: object count, depth ordering, distance, and spatial relations (Cambrian‑1).
Spatial reasoning at multiple difficulty levels with ground‑truth 3D supervision — from basic perception to compositional inference.
move_<dir>:<m>,…,rotate_<dir>:<deg>,….”Spatial reasoning under changing viewpoints; tests whether a model maintains a consistent 3D world model as the camera moves.
Multi‑image spatial intelligence: cross‑image object grounding, attribute comparison, and scene‑level reasoning.
心理旋转与跨视角的物体/自身位姿追踪,输入为一组相关视角的图像。
以自我为中心的三维推理:距离估计、运动方向、行进时间、附近物体的相对位置。
针对真实室内视频的空间智能:计数、尺寸与距离、路径规划、出现顺序。
单图与多图场景下的空间感知:深度排序、空间占用、场景布局、相对方向与距离。
经典计算机视觉任务的推理评估:物体计数、深度排序、距离、空间关系(Cambrian‑1)。
多个难度等级的空间推理任务,有真实三维标注 — 从基础感知到组合推理。
move_<方向>:<米>,…,rotate_<方向>:<度>,…。”考察视角变化下的空间推理:相机移动时模型是否能保持一致的三维世界模型。
多图空间智能:跨图像的物体定位、属性比较以及场景级推理。
Accuracy on MindCube‑Tiny and Ego3D‑Bench, with training conducted on stage 1 (S1) and on both stages (S1 + S2). Best results within each VLM family are bolded; overall / average columns are in blue; the best results among all models are in red.
MindCube‑Tiny 与 Ego3D‑Bench 上的准确率,训练分别在第一阶段(S1)以及两阶段合训(S1 + S2)进行。同一 VLM 系列内的最佳结果以粗体标记;Overall / Avg. 列以蓝色标记;所有模型中的最佳结果以红色标记。
| Method | MindCube‑Tiny | Ego3D‑Bench | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Rotation | Among | Around | Overall↑ | EgoDist. | Obj.Dist. | Loc. | EgoMot. | Obj.Mot. | TravelTime | EgoRel. | Obj.Rel. | Avg.↑ | |
| Closed-source Models | |||||||||||||
| gpt-4o-2024-11-20 | 37.0 | 44.8 | 56.4 | 46.1 | 33.2 | 26.5 | 28.1 | 78.1 | 56.7 | 36.0 | 60.5 | 66.0 | 48.1 |
| gpt-4.1 | 45.5 | 44.2 | 47.2 | 45.1 | 51.7 | 36.2 | 41.8 | 82.7 | 62.6 | 44.3 | 65.7 | 70.2 | 56.9 |
| glm-4.5v | 28.0 | 43.0 | 33.2 | 37.8 | 49.9 | 39.6 | 48.4 | 88.8 | 73.4 | 40.4 | 57.1 | 81.9 | 59.9 |
| gemini-2.5-pro | 84.0 | 39.7 | 56.8 | 52.2 | 58.5 | 50.2 | 61.4 | 92.9 | 75.5 | 43.5 | 72.8 | 78.6 | 66.7 |
| claude-sonnet-4 | 49.5 | 42.2 | 12.8 | 36.6 | 48.9 | 36.5 | 51.6 | 81.9 | 55.1 | 33.6 | 53.9 | 69.5 | 53.9 |
| doubao-seed-1.6 | 87.0 | 35.8 | 38.0 | 46.1 | 55.2 | 50.8 | 60.5 | 89.0 | 67.3 | 49.8 | 71.4 | 86.0 | 66.3 |
| o3-2025-04-16 | 86.5 | 42.7 | 66.0 | 56.6 | 71.3 | 59.3 | 65.6 | 93.4 | 80.1 | 53.5 | 77.7 | 83.1 | 73.0 |
| Qwen2.5-VL Family [2] | |||||||||||||
| Qwen2.5-VL-3B | 37.4 | 33.3 | 30.3 | 33.2 | 21.5 | 29.4 | 28.8 | 50.3 | 41.9 | 30.9 | 54.1 | 56.1 | 39.1 |
| 3DThinker-S1Qwen2.5-3B | 44.0 | 64.8 | 72.4 | 62.7 | 36.1 | 39.4 | 32.5 | 54.8 | 46.2 | 30.8 | 64.0 | 69.7 | 46.7 |
| 3DThinker-S1+S2Qwen2.5-3B | 55.5 | 81.8 | 75.2 | 75.2 | 41.6 | 46.0 | 33.1 | 54.7 | 53.3 | 30.8 | 70.1 | 76.9 | 50.8 |
| Qwen2.5-VL-7B | 36.5 | 32.5 | 38.4 | 34.7 | 32.7 | 31.5 | 30.5 | 45.9 | 44.0 | 34.5 | 43.2 | 66.5 | 41.1 |
| 3DThinker-S1Qwen2.5-7B | 43.5 | 66.3 | 76.4 | 64.4 | 47.9 | 44.5 | 36.5 | 51.9 | 51.3 | 39.1 | 59.1 | 73.9 | 50.5 |
| 3DThinker-S1+S2Qwen2.5-7B | 55.0 | 83.0 | 76.0 | 76.0 | 54.0 | 52.3 | 36.5 | 52.7 | 56.6 | 38.2 | 66.0 | 83.1 | 54.9 |
| Qwen2.5-VL-32B | 39.5 | 34.5 | 43.6 | 37.6 | 45.4 | 40.7 | 49.6 | 75.6 | 74.1 | 40.1 | 54.0 | 79.0 | 57.3 |
| 3DThinker-S1Qwen2.5-32B | 45.0 | 66.8 | 77.2 | 65.1 | 52.0 | 51.9 | 54.8 | 80.1 | 79.4 | 44.3 | 62.0 | 83.1 | 63.5 |
| 3DThinker-S1+S2Qwen2.5-32B | 56.5 | 83.2 | 77.2 | 76.7 | 62.2 | 61.9 | 54.5 | 80.2 | 86.6 | 43.7 | 69.9 | 86.0 | 68.1 |
| Qwen2.5-VL-72B | 40.0 | 42.5 | 44.4 | 42.5 | 42.4 | 38.6 | 54.8 | 86.8 | 68.9 | 38.5 | 53.3 | 80.5 | 58.0 |
| 3DThinker-S1Qwen2.5-72B | 42.5 | 68.0 | 73.6 | 64.5 | 49.9 | 45.9 | 57.8 | 85.6 | 75.6 | 43.9 | 58.0 | 80.8 | 62.2 |
| 3DThinker-S1+S2Qwen2.5-72B | 57.0 | 83.7 | 77.6 | 77.1 | 61.1 | 59.9 | 59.7 | 93.1 | 84.9 | 43.7 | 69.8 | 87.8 | 70.0 |
| InternVL3 Family [98] | |||||||||||||
| InternVL3-8B | 37.0 | 40.3 | 63.2 | 45.1 | 25.8 | 28.7 | 29.8 | 54.1 | 54.8 | 36.1 | 49.9 | 65.2 | 43.1 |
| 3DThinker-S1InternVL3-8B | 43.0 | 66.8 | 79.2 | 65.2 | 43.8 | 44.4 | 32.9 | 60.6 | 61.2 | 46.9 | 64.1 | 72.1 | 53.3 |
| 3DThinker-S1+S2InternVL3-8B | 55.0 | 82.5 | 79.2 | 76.5 | 54.6 | 56.1 | 36.0 | 67.2 | 69.4 | 46.7 | 71.0 | 81.9 | 60.4 |
| InternVL3-14B | 36.0 | 48.0 | 55.6 | 47.5 | 46.0 | 35.6 | 35.9 | 63.2 | 65.9 | 41.6 | 55.5 | 70.1 | 51.7 |
| 3DThinker-S1InternVL3-14B | 42.0 | 68.3 | 77.2 | 65.4 | 56.2 | 49.1 | 37.3 | 70.0 | 71.8 | 51.1 | 68.0 | 77.7 | 60.2 |
| 3DThinker-S1+S2InternVL3-14B | 54.5 | 84.3 | 77.6 | 77.0 | 63.5 | 59.9 | 41.3 | 78.3 | 80.2 | 50.0 | 75.1 | 84.0 | 66.5 |
| InternVL3-38B | 32.5 | 48.5 | 56.0 | 47.2 | 35.4 | 31.0 | 39.4 | 66.6 | 64.9 | 38.0 | 61.0 | 77.3 | 51.7 |
| 3DThinker-S1InternVL3-38B | 39.0 | 68.0 | 76.8 | 64.6 | 44.8 | 47.0 | 43.6 | 73.1 | 68.6 | 48.5 | 71.2 | 79.1 | 59.5 |
| 3DThinker-S1+S2InternVL3-38B | 53.5 | 85.2 | 78.0 | 77.4 | 54.7 | 58.1 | 49.2 | 86.9 | 80.4 | 49.1 | 79.6 | 85.9 | 68.0 |
| InternVL3-78B | 38.5 | 50.5 | 57.4 | 49.9 | 54.6 | 48.4 | 50.3 | 77.7 | 70.0 | 44.8 | 57.0 | 76.6 | 59.9 |
| 3DThinker-S1InternVL3-78B | 43.5 | 69.0 | 77.2 | 66.1 | 59.8 | 53.1 | 52.2 | 80.1 | 72.5 | 53.9 | 65.1 | 78.0 | 64.3 |
| 3DThinker-S1+S2InternVL3-78B | 57.0 | 86.2 | 78.8 | 78.9 | 69.9 | 61.0 | 61.0 | 91.9 | 88.6 | 54.8 | 75.3 | 83.9 | 73.3 |
Accuracy across six spatial reasoning benchmarks: VSI‑Bench, SPBench, CV‑Bench, SPAR‑Bench, ViewSpatial‑Bench, and MMSI‑Bench. Modality tags: SI single image, MV multi‑view / video. Best within each backbone family is bolded; Avg. is in blue; our results are shaded.
六个空间推理基准上的准确率:VSI‑Bench、SPBench、CV‑Bench、SPAR‑Bench、ViewSpatial‑Bench、MMSI‑Bench。模态标记:SI 单图,MV 多视角 / 视频。同一 backbone 系列内最佳以粗体标记;Avg. 列以蓝色标记;本文结果以底色高亮。
| Method | VSI‑BenchMV | SPBenchSIMV | CV‑BenchSI | SPAR‑BenchSIMV | ViewSpatialSIMV | MMSI‑BenchMV | Avg.↑ |
|---|---|---|---|---|---|---|---|
| Qwen2.5‑VL‑3B Based Spatial Models | |||||||
| Qwen2.5‑VL‑3B | 29.4 | 38.5 | 70.6 | 24.6 | 35.6 | 26.5 | 37.5 |
| Spatial‑MLLM‑4B | 47.3 | 48.4 | 73.8 | 35.1 | 43.6 | 31.5 | 46.6 |
| SpatialLadder‑3B | 45.7 | 70.6 | 73.7 | 34.4 | 44.2 | 29.2 | 49.6 |
| 3DThinker‑S1Qwen2.5‑3B | 53.2 | 54.8 | 74.5 | 52.3 | 59.5 | 37.7 | 55.3 |
| 3DThinker‑S1+S2Qwen2.5‑3B | 59.1 | 60.2 | 78.4 | 58.2 | 64.7 | 41.9 | 60.4 |
| Qwen2.5‑VL‑7B Based Spatial Models | |||||||
| Qwen2.5‑VL‑7B | 35.8 | 42.9 | 73.0 | 30.2 | 37.9 | 26.9 | 41.1 |
| SpaceR‑7B | 44.5 | 54.0 | 75.3 | 37.1 | 45.5 | 28.8 | 47.5 |
| VILASR‑7B | 45.4 | 53.9 | 77.1 | 37.8 | 46.1 | 30.2 | 48.4 |
| Video‑R1 | 33.4 | 42.8 | 69.6 | 31.5 | 36.1 | 29.4 | 40.5 |
| 3DThinker‑S1Qwen2.5‑7B | 57.3 | 61.5 | 77.9 | 56.3 | 61.7 | 41.5 | 59.4 |
| 3DThinker‑S1+S2Qwen2.5‑7B | 63.7 | 68.3 | 81.1 | 63.3 | 68.6 | 43.3 | 64.7 |
cd eval
# Run inference on MindCube benchmark
python eval_qwen.py \
--model_path ../checkpoints/merged \
--data_path ../data/MindCube-Tiny \
--output_path results/mindcube_predictions.jsonl \
--max_new_tokens 1024
# Compute accuracy metrics
sh get_result.sh#!/bin/bash
# Extract answers and compute accuracy against ground truth
python compute_accuracy.py \
--predictions results/mindcube_predictions.jsonl \
--ground_truth ../data/MindCube-Tiny/test_answers.json
# Output:
# MindCube-Tiny Accuracy: XX.X%
# Per-category breakdown: spatial / counting / navigation / ...@inproceedings{chen2026think,
title = {Think with 3D: Geometric Imagination Grounded
Spatial Reasoning from Limited Views},
author = {Chen, Zhangquan and Zhang, Manyuan and Yu, Xinlei
and Luo, Xufang and Sun, Mingze and Pan, Zihao
and An, Xiang and Feng, Yan and Pei, Peng
and Cai, Xunliang and Huang, Ruqi},
booktitle = {Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)},
year = {2026}
}