Paper · 2021-12-20 · cs.CV
ScanQA: 3D Question Answering for Spatial Scene Understanding
ScanQA answers free-form questions about a 3D indoor scan and points to the objects the question refers to. A VoteNet detector and a GloVe/LSTM question encoder feed transformer fusion layers that drive localization, object-class and answer heads. Author-reported: EM@1 23.45 with objects and 20.90 without, above every listed 2D or pipeline baseline.
Visual groundingMultimodal fusionVisual question-answering evaluation3d-scene-understandingquestion-answeringpoint-cloudmultimodaldataset
arXivPDFCodeMarkdown13 min read
AI-compiled by claude-haiku-5-5 on 2026-10-24, not yet reviewed by a human. Every claim cites its source; check it when a detail matters.
In plain words
Most question-answering models for images look at a flat picture. This paper asks a harder question: can a model answer questions about a whole room, given a 3D scan of it? A question like "what color is the chair next to the table?" needs the model to know where things are, not only what they look like.
The authors build a dataset of such questions for indoor rooms from a public scan collection. Each answer is written by people, and each question is tied to the objects in the room it talks about, so the answer can be checked against the right place in the scan.
The model, called ScanQA, works in two parts at once. It finds candidate objects in the 3D scan and reads the question. It then mixes the two so that it can both name the answer and point to the object that the question is about. Training it to do both tasks together is the main idea: the answer and the location help each other.
TL;DR
- Task: given an RGB-D indoor scan and a free-form question, output an answer and the 3D boxes of the objects the question refers to §3.1.
- Model: VoteNet object proposals plus a question encoder, fused by transformer layers, with three heads (localization, object class, answer) trained jointly §4.
- Data: 41,363 questions over 800 ScanNet scenes, with a test split that has object annotations and one that does not tab. 2 §3.3.
- Results are author-reported: EM@1 23.45 on the test split with objects, against 22.31 for the best 2D baseline tab. 3.
Problem
Image question answering models see one or several 2D frames. They struggle with direction, distance, occlusion, and with telling whether two views show the same object §1. Existing 3D datasets for language are small or template-based §1. The paper frames a task where a scan of the room already exists, for example from prior navigation, and the model answers from that scan §1.
Method
The full pipeline is built from these parts; the forward pass and loss are in the pseudocode below.
- Question encoder. Each question word is embedded with GloVe (300-dimensional). A one-layer bidirectional LSTM with hidden size 256 models the sequence, and a GELU projection gives contextual word vectors of size d = 256 §4. The paper describes the LSTM as bidirectional; the code uses a unidirectional LSTM by default (see Paper vs code).
- Scene encoder. Point features r have 135 dimensions: height, colour, normals and multiview image features §4. VoteNet with a PointNet++ backbone detects object proposals, n_v = 256 of them §4. A GELU projection maps each proposal to size d §4.
- Fusion. Two transformer encoder layers process the question words and two decoder layers let the proposals attend to them §4. The paper cites the MCAN architecture as the source of these blocks §4. The two streams are then merged into one fused vector of size d by an attention MLP §4.
- Localization head. A two-layer MLP scores each proposal against the question. Scores are normalised over proposals and trained with cross-entropy §4.
- Object-class head. A two-layer MLP predicts one of the 18 ScanNet benchmark classes from the fused vector §4.
- Answer head. A projection to one score per training answer is trained with binary cross-entropy, so several answers can be correct §4.
- Loss. The paper uses L = L_ans + L_obj + L_loc + L_det, a plain sum of answer, object-class, localization and VoteNet detection losses §4.
Dataset collection, for context: questions were generated from ScanRefer captions with a T5 question-generation model, filtered by rules and by workers, rewritten by workers, and answered in free form with object IDs attached §3.2.
# ScanQA forward pass and training loss [§4]
# Input: cloud (N, 3 + f); question tokens; answer targets
# Output: scalar loss
def scanqa_loss(cloud, question, targets):
# Embed each question word with GloVe, then run a one-layer LSTM
# over the sequence [§4]
word_vectors = glove_embed(question) # (n_q, 300)
words = lstm(word_vectors) # (n_q, d), d = 256
words = gelu(linear(words)) # (n_q, d)
# Detect object proposals with VoteNet on a PointNet++ backbone,
# keeping n_v = 256 proposals [§4]
proposals = votenet(cloud) # (n_v, 128)
proposals = gelu(linear(proposals)) # (n_v, d)
# Let words and proposals attend to each other through
# L = 2 transformer encoder and decoder layers [§4]
words, proposals = fusion_layers(words, proposals)
# Merge both streams into one question-aware vector with an
# attention MLP [§4]
fused = attention_fuse(words, proposals) # (d,)
# Localization: score each proposal against the question [§4]
# Target is the proposal that best matches the annotated box
# [code: lib/dataset.py#L346-L350]
box_scores = two_layer_mlp(proposals) # (n_v,)
loc_loss = cross_entropy(box_scores, gt_index)
# Object class: one of the 18 ScanNet classes [§4]
class_logits = two_layer_mlp(fused) # (18,)
obj_loss = cross_entropy(class_logits, object_class)
# Answer: one score per training answer, multi-label [§4]
answer_logits = two_layer_mlp(fused) # (n_a,)
ans_loss = bce_with_logits(answer_logits, targets)
# Detection: VoteNet losses on the same scene [§4]
det_loss = votenet_loss(cloud, boxes)
# Unit-weighted sum of the four terms in the paper [§4]
return ans_loss + obj_loss + loc_loss + det_loss# Training schedule [§5.1]
# Input: training scenes with questions, answers and object boxes
# Output: trained weights
def train_scanqa(scenes):
# Adam with initial learning rate 5e-4 and weight decay 1e-5 [§5.1]
optimizer = adam(lr=5e-4, weight_decay=1e-5)
# Train for 30 epochs, with the learning rate multiplied by 0.2
# after epoch 15 [§5.1]
for epoch in range(30):
for batch in batches(scenes, size=16): # batch size 16 [§5.1]
# Rotate each axis by up to 5 degrees and shift each
# axis by up to 0.5 m [§5.1]
cloud, boxes = augment(batch)
loss = scanqa_loss(cloud, batch.questions, batch.targets)
optimizer.step(loss)Key equation (the loss is written inline in the paper; no equation numbers are used in the body): \mathcal{L} = \mathcal{L}_{ans} + \mathcal{L}_{obj} + \mathcal{L}_{loc} + \mathcal{L}_{det} §4
Hyperparameters
Values in the Where column are the paper's unless the row says code. "not stated" means the paper does not give the value.
| Name | Value | Where | Notes |
|---|---|---|---|
| Point features r | 135 dims: height, colour, normals, multiview | §4 | Feature sets compared in tab. 5; the code's 135-dim count is height + colour + normals + 128 multiview train.py |
| Points per scene | 40,000 | train.py | not stated in paper |
| Word embedding | GloVe, 300 | §4 | |
| Max question tokens | 36 | config.py | not stated in paper |
| Question LSTM | 1 layer, hidden 256 | §4 | paper says bidirectional; code default is unidirectional, see Paper vs code |
| Hidden size d | 256 | §4 | sweep in app. B |
| Object proposals n_v | 256 | §4 | same default in code train.py |
| Proposal feature size | 128 | train.py | not stated in paper |
| Transformer layers L | 2 | §4 | sweep in app. B |
| Attention heads | 8 | train.py | not stated in paper |
| Fused vector size | 512 (code default) | train.py | paper says the fused feature has size d |
| Object classes | 18 ScanNet classes | §4 | |
| VoteNet vote radius / neighbours | 0.3 / 16 | train.py | not stated in paper |
| Dropout, transformer | 0.1 | train.py | not stated in paper |
| Dropout, answer head | 0.3 | train.py | not stated in paper |
| Dropout, question encoder | 0.3 | train.py | not stated in paper |
| Optimizer | Adam | §5.1 | betas 0.9 / 0.999, epsilon 1e-8 train.py |
| Batch size | 16 | §5.1 | |
| Initial learning rate | 5e-4 | §5.1 | |
| Epochs | 30 | §5.1 | code default is 50 train.py |
| Learning-rate decay | x0.2 after 15 epochs | §5.1 | code default decays at epochs 100 and 200 train.py |
| Weight decay | 1e-5 | §5.1 | |
| Gradient clipping | max norm 1.0 | train.py | not stated in paper |
| Random seed | 42 | train.py | not stated in paper |
| Rotation augmentation | up to 5 degrees per axis, all three axes | §5.1 | dataset.py |
| Translation augmentation | up to 0.5 m per axis | §5.1 | dataset.py |
| Flip augmentation | probability 0.5 on x and y | dataset.py | not in paper |
| Loss weights | 1 for all four terms | §4 | code weights in Paper vs code |
| Answer candidates | all training answers | §4 train.py | min frequency 1, no cap |
| Answer targets | 0 / 1 per answer | §4 | code uses soft targets, see Paper vs code |
| Evaluation | EM@1, EM@10 | §5.1 | EM@K: top K predictions contain a ground-truth answer |
| Evaluation | BLEU-1..4, ROUGE-L, METEOR, CIDEr, SPICE | §5.1 | captioning metrics for free-form answers |
| Localization | Acc@0.25, Acc@0.5 (IoU) | app. B §5.3 | threshold 0.25 follows ScanRefer |
| Hardware and training time | not stated | §5 |
Results
Author-reported. Setup: the ScanQA dataset, 800 ScanNet scenes, with two test splits, one with and one without object annotations §3.3. Baselines: 2D VQA with MCAN on three random or oracle frames, VoteNet+MCAN, and ScanRefer+MCAN in pipeline and end-to-end form §5.1. Metrics are as in §5.1. Source: tab. 3 unless noted.
Test split with objects
| Model | EM@1 | EM@10 | BLEU-1 | BLEU-2 | BLEU-3 | BLEU-4 | ROUGE | METEOR | CIDEr | SPICE |
|---|---|---|---|---|---|---|---|---|---|---|
| RandomImage+MCAN | 22.31 | 53.11 | 26.66 | 18.49 | 16.16 | 14.26 | 31.27 | 12.13 | 60.37 | 9.05 |
| VoteNet+MCAN | 19.71 | 50.76 | 29.46 | 17.23 | 10.33 | 6.08 | 30.97 | 12.07 | 58.23 | 10.44 |
| ScanRefer+MCAN (pipeline) | 17.52 | 49.92 | 19.17 | 10.66 | 0.00 | 0.00 | 24.40 | 9.38 | 44.25 | 6.24 |
| ScanRefer+MCAN (e2e) | 20.56 | 52.35 | 27.85 | 17.27 | 11.88 | 7.46 | 30.68 | 11.97 | 57.36 | 10.58 |
| ScanQA | 23.45 | 56.51 | 31.56 | 21.39 | 15.87 | 12.04 | 34.34 | 13.55 | 67.29 | 11.99 |
| OracleImage+MCAN (upper bound, uses true object) | 25.34 | 55.93 | 28.70 | 20.11 | 16.78 | 12.89 | 34.59 | 13.42 | 67.24 | 11.93 |
Test split without objects
| Model | EM@1 | EM@10 | BLEU-1 | BLEU-2 | BLEU-3 | BLEU-4 | ROUGE | METEOR | CIDEr | SPICE |
|---|---|---|---|---|---|---|---|---|---|---|
| RandomImage+MCAN | 20.82 | 51.23 | 26.29 | 17.90 | 14.27 | 9.66 | 29.23 | 11.54 | 55.64 | 8.87 |
| VoteNet+MCAN | 18.15 | 48.56 | 29.63 | 17.80 | 11.57 | 7.10 | 29.12 | 11.68 | 53.34 | 10.36 |
| ScanRefer+MCAN (pipeline) | 16.47 | 49.05 | 18.71 | 10.98 | 16.53 | 0.76 | 22.45 | 8.76 | 40.81 | 6.41 |
| ScanRefer+MCAN (e2e) | 19.04 | 49.70 | 26.98 | 16.17 | 11.28 | 7.82 | 28.61 | 11.38 | 53.41 | 10.63 |
| ScanQA | 20.90 | 54.11 | 30.68 | 21.20 | 15.81 | 10.75 | 31.09 | 12.59 | 60.24 | 11.29 |
The paper reports the BLEU-3 value 16.53 for ScanRefer+MCAN (pipeline) on this split as printed, which is higher than its BLEU-2 and BLEU-4; the table here copies it as printed tab. 3.
Component ablation tab. 4. ANS is the answer head, OBJ the object-class head, LOC the localization head.
| ANS | OBJ | LOC | Split | EM@1 | EM@10 | BLEU-1 | BLEU-2 | BLEU-3 | BLEU-4 | ROUGE | METEOR | CIDEr | SPICE |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| yes | with objects | 12.16 | 42.77 | 12.86 | 5.45 | 0.12 | 0.02 | 17.55 | 6.71 | 29.17 | 4.05 | ||
| yes | yes | with objects | 18.31 | 49.18 | 22.32 | 14.53 | 11.15 | 7.92 | 26.37 | 9.94 | 49.10 | 6.36 | |
| yes | yes | with objects | 20.46 | 51.67 | 25.06 | 16.91 | 14.06 | 11.37 | 29.22 | 11.13 | 55.17 | 8.21 | |
| yes | yes | yes | with objects | 23.45 | 56.51 | 31.56 | 21.39 | 15.87 | 12.04 | 34.34 | 13.55 | 67.29 | 11.99 |
| yes | without objects | 10.78 | 39.44 | 11.94 | 5.02 | 0.12 | 0.02 | 15.34 | 5.91 | 25.51 | 3.51 | ||
| yes | yes | without objects | 16.23 | 46.30 | 21.37 | 13.49 | 10.71 | 7.64 | 23.63 | 9.10 | 43.21 | 6.13 | |
| yes | yes | without objects | 18.12 | 49.60 | 25.12 | 17.58 | 14.68 | 10.23 | 26.50 | 10.43 | 49.93 | 8.16 | |
| yes | yes | yes | without objects | 20.90 | 54.11 | 30.68 | 21.20 | 15.81 | 10.75 | 31.09 | 12.59 | 60.24 | 11.29 |
Input-feature ablation tab. 5. Localization and QA with different point features. Only Acc@0.25 and EM@10 are reported here.
| Model | Acc@0.25 | EM@10 |
|---|---|---|
| ScanQA (xyz) | 23.67 | 55.67 |
| ScanQA (xyz+rgb) | 23.45 | 55.43 |
| ScanQA (xyz+rgb+normal) | 23.35 | 54.50 |
| ScanQA (xyz+multiview) | 25.40 | 55.51 |
| ScanQA (xyz+multiview+normal) | 25.44 | 56.51 |
The paper's text says RGB did not help localization or QA, and multiview features helped localization but not QA on the single-object model §5.3. The main ScanQA row matches the xyz+multiview+normal setting on EM@10 tab. 5 tab. 3.
Other reported numbers. Human performance: EM@1 of 51.6 for the best MTurk worker on the sampled questions of the test split with objects §5.2. The appendix adds a multi-object variant, ScanQA (multiple), which uses sigmoid scores and BCE over boxes and labels app. A; its ablations are in app. B. In the appendix sweeps, hidden size d = 128 scores higher than d = 256 on EM@1 and EM@10 on the test split with objects (24.38 and 56.71 against 23.45 and 56.51), and L = 1 scores higher on EM@1 only app. B. The main text keeps d = 256 and L = 2 §4.
Paper vs code
Code: official repository ATR-DBI/ScanQA, linked from the paper's first page §1, commit 0a9f162112c80b439903062a4097d63015a70cad. Its LICENSE file is Creative Commons Attribution-NonCommercial-ShareAlike 3.0 (CC BY-NC-SA 3.0) LICENSE. No code is quoted here. The pseudocode is ours; citations are by path and line only.
Discrepancies, each with both sources:
- Training length and schedule. Paper: 30 epochs, learning rate multiplied by 0.2 after 15 epochs §5.1. Code defaults: 50 epochs and decay steps at 100 and 200 train.py train.py. A comment beside the decay default reads "# 15" train.py. The run actually used for the paper is not stated.
- Loss weights. Paper: unit weights on all four terms §4. Code: vote 1.0, objectness 0.5, box 1.0, semantic class 0.1, reference (localization) 0.1, language (object class) 0.1, answer 0.1 train.py loss_helper.py. Inside the box loss, heading-class and size-class terms are weighted 0.1 loss_helper.py.
- Question encoder direction. Paper: one-layer bidirectional LSTM §4. Code: LSTM, unidirectional unless
--lang_use_bidiris passed lang_module.py train.py. The README training command passes only--use_colorand--tagREADME.md, so it does not turn bidirectional on. - Fusion output. Paper: a fused feature of size d after a two-layer MLP with attention §4. Code: attention pooling of the question stream and of the proposal stream, summed, then LayerNorm, with a default size of 512 qa_module.py train.py.
- Answer targets. Paper: binary cross-entropy over answer candidates §4. Code: BCE with soft targets. A training answer scores 0, 0.3, 0.6, 0.9 or 1.0 by its training count of 0, 1, 2, 3 or at least 4 dataset.py dataset.py. The loss is summed and divided by batch size loss_helper.py. The paper does not describe these soft targets. A cross-entropy variant (
--answer_cls_loss ce) also exists in the code loss_helper.py. - Localization target. Paper: "which of the proposed object boxes corresponds to the question" with cross-entropy §4. Code: the target is the proposal whose box has the highest IoU with the annotated box of the first annotated object only dataset.py loss_helper.py. The score is trained with a softmax ranking loss against a one-hot target loss.py loss_helper.py.
- Object-class label. Paper: predict the object classes associated with the question, 18 classes §4. Code: the label is the class of the first annotated object; any name missing from the mapping becomes class 17 dataset.py.
- Augmentation. Paper: rotations of up to 5 degrees and translations of up to 0.5 m §5.1. Code: the same, plus a random flip of x and y with probability 0.5 each dataset.py dataset.py.
- Feature set in the default run. The paper's main configuration uses geometry, multiview features and normals §5.3. The README command adds colour and no multiview or normals README.md. Height is on unless
--no_heightis passed train.py. - Multiview features. The paper cites a multiview feature method for point features §4. The code expects precomputed image features in a separate pipeline with its own scripts config.py project_multiview_features.py.
Pitfalls
- Only the first annotated object is a localization target in the official code dataset.py. Answers that depend on several objects (for example, "what color are the chairs around the table?" §3.1) are only handled by the appendix variant app. A. An implementer who copies the single-target version will not match the multi-object setting.
- Soft answer targets are not in the paper. Using plain 0/1 BCE will change training behaviour, because frequent answers get lower target weight than rare ones in the code dataset.py.
- Answer vocabulary is closed. Candidates are the training answers, with no cap and minimum frequency 1 train.py train.py. Answers outside this set cannot be produced.
- Free-form answers. Exact match is strict. The paper adds captioning metrics because answers to "where is" questions vary in wording §3.3 §5.1. Report both families.
- Unit weights are not what the code uses. Copying the paper's loss will not reproduce the code's balance (see Paper vs code, item 2).
- Localization is not measurable on the split without objects. That split has no object annotations, so Acc@K does not apply to it §3.3.
- Model selection and test splits. The appendix sweeps over d and L show test-split scores next to validation scores, and the text calls d = 128 and L = 1 suitable for the test splits app. B. The main setting is d = 256 and L = 2 §4. A reader who wants the best reported test numbers should not assume the main setting is the sweep's optimum.
- Multiview features and the 135-dim vector depend on a separate image-feature pipeline that the paper only cites §4 config.py. Budget for it before reproducing the feature-ablation rows.
- Dataset provenance. Scenes come from ScanNet and questions are generated from ScanRefer captions §3.2. The terms of use of ScanNet and ScanRefer were not checked for this card; check them before reusing the data.
- Pretrained VoteNet is not stated. The paper does not say whether the detector starts from pretrained weights §4. The code trains the detection losses in the same run loss_helper.py.
- Hardware and training time are not stated §5, so compute cost cannot be estimated from the paper.
- Human baseline is one best worker on a sample of questions §5.2; treat it as a rough ceiling, not a benchmark.
Lineage
- Builds on: ScanRefer, for the localization loss and the scene data §4 §5.1; VoteNet, for object proposals §4; MCAN, for the transformer fusion blocks §4. None of these is a page in this wiki yet.
- Compared against: 2D VQA with MCAN on sampled or oracle frames, VoteNet+MCAN, and ScanRefer+MCAN, pipeline and end-to-end §5.1. The appendix adds Oscar as a 2D model app. B.
- Superseded by: not known from content already in this wiki.
Source and license
Adapted from “ScanQA: 3D Question Answering for Spatial Scene Understanding” by Daichi Azuma, Taiki Miyanishi, Shuhei Kurita, Motoaki Kawanabe (arXiv:2112.10482), used under CC BY 4.0. Changes: condensed and restructured for implementation, with tables, equations and algorithms taken from the paper and its code, each cited to its source. Not affiliated with or endorsed by the authors. Code citations refer to https://github.com/ATR-DBI/ScanQA at commit 0a9f162112c8 (CC BY-NC-SA 3.0 (non-commercial)). Any quoted code is shown under that license.