- ground_qwen3_5
- GroundQwen35 β Instance Segmentation on Qwen3.5-0.8B
- Overview
- Typical configuration
- Architecture
- Sequence format
- Data pipeline
- Training loss (
loss.py) - Training script notes (
train.py) num_bridgesconstraints- Spatial grid assignment
- Dataset mixtures
- Key training arguments
- Sample validation
- Validation eval loop
- Known limitations (current code)
- Quick start
- Inference demo
- Integration test
- Verification / smoke testing
- LR schedule
- File structure
- Dependencies
- Overview
ground_qwen3_5
Research code tree for grounded segmentation / referring-expression work built on Qwen3-VL, including the video extension, ablation variants, baselines and analysis tooling.
This is a working research tree, published as-is rather than as a packaged library. It contains many parallel variant directories (each a self-contained copy of the model, training loop and eval scripts) that differ in a single design choice.
What's here
| path | contents |
|---|---|
ground_qwen35*/ |
model + training + eval variants (one directory per design choice) |
ground_qwen35_video*/ |
video extension: tube assignment, temporal action tokens |
baselines/ |
MM-GroundingDINO / mmdetection and other baseline checkouts |
analysis/ |
bridge-attention and multi-scale analysis outputs |
ConCor-1-release/ |
the ConCor-1 public release staging area |
data_scripts/, scripts/ |
dataset construction and job launch scripts |
docs/ |
design notes |
wandb/ |
training run logs |
Trained checkpoints for these runs are published separately at royguw/ground-ckpts.
Packaging note
HuggingFace caps a repository at 20,000 files; the raw tree has ~175,800. To fit, these directories are published as tar archives rather than loose files:
| archive | contents |
|---|---|
wandb.tar |
wandb/ training run logs |
analysis.tar |
analysis/ result dumps |
baselines/mm_grounding_dino/venv.tar |
that baseline's .venv |
ground_qwen35_video/baselines/*/venv.tar |
each video baseline's .venv |
Extract in place, e.g. tar -xf wandb.tar.
Not included: .git/ history, and __pycache__/ / *.pyc bytecode.
WANDB_API_KEY and --hf-token are read from the environment; set them yourself.
Notes
WANDB_API_KEYis read from the environment; set it yourself before training.- Git history is not included.
baselines/and the nestedground_qwen35_video/baselines/vendor third-party repositories (mmdetection and others), which carry their own licences.
Project README
GroundQwen35 β Instance Segmentation on Qwen3.5-0.8B
Instance-level image segmentation using configurable bridge (query) tokens on top of Qwen3.5-0.8B's hybrid-attention multimodal backbone.
Ported from ground_olmo.
Overview
GroundQwen35 is a grounding-only model (not autoregressive). It appends learnable bridge tokens after text in the sequence, whose hidden states are fed to grounding heads that predict:
- Segmentation masks: Per-bridge, per-visual-token predictions (
seg_logits) - Presence: Which bridges are "occupied" by an instance (
presence_logits) - Text grounding (optional via config): Which category text tokens align with each instance (
text_logits), using a second bilinear head whenquery_text_groundingis enabled
Typical configuration
Many training runs use together:
| Setting | Role |
|---|---|
query_text_grounding=True (CLI default; see below) |
Instantiates text_head and builds text labels; forward requires non-empty text_grounding_mask. |
--spatial_grid_assignment |
Hungarian assignment of instances to bridge slots on an NΓN grid (num_bridges must be NΒ²). |
--presence_mlp |
SwiGLU-style MLP presence head instead of a single linear layer. |
Optional / less central for that path: --reset_position, --mask_incomplete_presence, --mask_out_incomplete_presence.
Architecture
Qwen3.5-0.8B Backbone
βββββββββββββββββββ
image pixels βββΊ β Vision Encoder β βββΊ visual tokens (N_vis)
β (12-layer ViT) β
ββββββββββ¬βββββββββ
βΌ
input_ids βββββββΊ βββββββββββββββββββ
(vis + text + β Text Model β βββΊ hidden_states (B, S, 1024)
bridge tokens) β (24 hybrid β
β layers) β
ββββββββββ¬βββββββββ
βΌ
βββββββββββββββββββ
β extract_tokens β
β _by_mask β
ββββ¬βββββββββββ¬ββββ¬β
βΌ βΌ βΌ
query_hidden visual_hidden text_hidden *
(B,Q,D) (B,N_vis,D) (B,N_txt,D)
β β β
ββββββ¬ββββββ β
βΌ β
βββββββββββββββ β
β seg_head β β
β (Visual β β
β SingleSlot β β
β Gate) β β
ββββββββ¬βββββββ β
βΌ β
seg_logits β
(B,Q,N_vis) β
βΌ
βββββββββββββββββββββββββββ΄βββββββββββββ
β text_head (VisualSingleSlotGate) * β
β inputs: text_hidden + query_hidden β
ββββββββββββββββββββ¬ββββββββββββββββββββββ
βΌ
text_logits *
(B,Q,N_txt)
query_hidden
β
βΌ
βββββββββββββββ
β presence_ β
β head β
β (MLP or β
β Linear) β
ββββββββ¬βββββββ
βΌ
presence_logits
(B,Q)
* text_hidden, text_head, and text_logits exist only when query_text_grounding is True (ground_qwen35/model.py). N_txt is the number of tokenizer outputs for the category string segment. seg_head uses visual + query hidden states; text_head uses category text + query; presence_head uses query only.
Sequence format
<|vision_start|> <|image_pad|>ΓN_vis <|vision_end|> category_text bridge_tokenΓQ
Here Q = num_bridges (default 225 in train.py / GroundQwen35Config). Bridge token IDs are consecutive unused embedding rows starting at 248077; if Q > 243, the embedding table is resized (init_bridge_embeddings).
No system/assistant wrapping. Causal attention is used as in the backbone.
Data pipeline
flowchart LR
A[GroundingSourceDataset] --> B[DeterministicGroundingDataset]
B --> C[QwenExamplePreprocessor]
C --> D[QwenGroundingCollator]
D --> E[IterableGroundingMixture]
- Sources (
data/dataset.py):ground_olmodataset classes (COCONut / LVIS / ADE20K / RefCOCO / baselines, etc.); empty-segment rows removed. - Deterministic wrapper: deterministic RNG +
QwenExamplePreprocessor;_validate_sample(image, instances, text labels,text_grounding_mask.any()); up to 50 retries with rerouted indices. - Preprocessor (
data/preprocessor.py): HF image processor β merged patch labels (data/segmentation.py); optionalcompute_spatial_grid_assignment(perfect-squarenum_bridges);format_grounding_sequence(data/formatter.py); optionalbuild_instance_text_labels(first token subsequence match for category + char spans). - Collator (
data/collator.py): pad sequences and labels;presence_loss_mask; optionalslot_assignment. - Iterable mixture (
data/iterable_mixture.py): mixture probabilities, per-dataset epoch shuffle, global batch sharding across workers and ranks.
Training loss (loss.py)
All three terms are computed every step; train.py adds a zero multiple of head outputs so DDP always touches all parameters (DDP-safe empty batches).
- Presence: BCE with optional weighting (
presence_loss_type:raw/weight/neg_down_sample) andpresence_loss_mask. - Segmentation: Dice + BCE (or BCE only) on active bridge predictions vs instance masks; with
slot_assignment, maps slots back to the correct instance row (--grounding_loss_func:dice_bceorbce). - Text grounding: BCE on active slots vs padded text labels (-100 ignored).
Training script notes (train.py)
- Monkey patch
Qwen3_5TextModel.forward(mRoPE /position_idshandling) β applied before model construction. - Optimizer: AdamW;
seg_headandtext_headuse--head_lr;presence_headand backbone use--backbone_lr. - Schedule: Linear warmup then cosine decay to
alpha_f Γpeak LR.global_stepcounts optimizer steps (aftergrad_accum_stepsmicro-batches). - Checkpoints:
model.pt,optimizer.pt, per-ranktrain/rank*.pt(RNG,global_train_examples_seen, etc.). - Weights & Biases:
_init_wandbcurrently assignsWANDB_API_KEYinside the script β remove hardcoded credentials for any shared or public repo; usewandb loginor environment variables instead.
num_bridges constraints
| Situation | Requirement |
|---|---|
--spatial_grid_assignment |
num_bridges must be NΒ² (asserted in data/segmentation.py::compute_spatial_grid_assignment). Examples: 64, 100, 225. Not 32. Training startup does not re-assert this (unlike --reset_position). |
--reset_position |
Also requires a perfect square; asserted in train.py. |
| Neither | Any positive Q works for sequential slot filling (instance i β bridge i, capped by loss logic when n_inst > Q). |
| Reserved tokenizer IDs | 243 slots without tokenizer entries; larger Q triggers resize_token_embeddings. |
Spatial grid assignment
Hungarian matching assigns each instance (after reading-order sort) to the nearest cell on an NΓN grid in normalized coordinates. If more instances than slots, some rows stay -1 in slot_assignment and are skipped where the loss checks slot indices.
Dataset mixtures
Configured in ground_qwen35/train.py:get_training_data(), not CLI.
| Mode | Content (summary) |
|---|---|
debug |
Single group single_ref (weight 0.2): refcoco_clean / refcoco_plus_clean / refclef with sampling_rate 0.4 / 0.4 / 0.2. (Commented blocks show older vocab / pos_neg debug.) |
full |
allvocab 0.1 (coconut_vocab_all_cat, lvis_vocab_all_cat; ade20k_vocab_all_cat commented), pos_neg 0.2, panoptic_caption 0.4 (coconut_pancap), reference 0.3 (refcoco_clean, refcoco_plus_clean, refclef with 0.45 / 0.45 / 0.1). |
debug_single |
coconut_vocab_all_cat only. |
debug_mixed |
0.5 coconut_pos_neg + 0.5 Ref clean splits (for DDP / complete vs incomplete testing). |
Per-dataset sampling uses sqrt(n) or sampling_rate * sqrt(n) within a group, then group weights; probabilities are normalized globally.
Registry also includes baselines and cleaned Ref variants β see DATASET_REGISTRY in data/dataset.py.
Key training arguments
| Arg | Default | Description |
|---|---|---|
--mode |
debug |
Mixture selector (debug, full, debug_single, debug_mixed). |
--num_bridges |
225 |
Bridge / query count. |
--grounding_loss_func |
dice_bce |
dice_bce or bce. |
--presence_loss_type |
weight |
raw, weight, neg_down_sample. |
--freeze_vision |
True |
Freeze vision tower. |
--freeze_backbone |
False |
Freeze full backbone. |
--query_text_grounding |
effectively on | Implemented as action="store_true", default=True β the flag does not toggle off from CLI; to disable, change train.py / argparse. |
--spatial_grid_assignment |
False |
NΓN Hungarian slot assignment (needs num_bridges square). |
--presence_mlp |
False |
SwiGLU MLP presence head. |
--reset_position |
False |
Override bridge MRoPE to interpolated grid (perfect-square num_bridges). |
--head_lr / --backbone_lr |
1e-4 / 1e-5 |
LR groups. |
--warmup_steps |
200 |
Warmup length (optimizer steps). |
--alpha_f |
0.1 |
Cosine floor factor. |
--max_grad_norm |
1.0 |
Clipping (0 = off). |
--grad_accum_steps |
1 |
Micro-steps per optimizer step. |
--eval_interval |
0 |
Eval every N optimizer steps (0 = off). |
--eval_max_examples |
512 |
Cap per eval dataset. |
--detail_debug |
False |
Verbose debug dumps. |
Sample validation
Every sample passes _validate_sample() (data/dataset.py):
pixel_valuespresent- At least one instance and
N_vis > 0 instance_text_labelsnot Nonetext_grounding_maskhas at least one True
Implication: text-only preprocessor paths are not accepted by the current training loop validation.
Validation eval loop
With --eval_interval N, eval runs on: EVAL_DATASETS (coconut_pancap, coconut_pos_neg, ade20k_pos_neg, lvis_pos_neg), plus REFCOCO_EVAL_DATASETS (refcoco, refcoco_plus), plus CAPTION_BASELINE_EVAL_DATASETS (refcocog_baseline, grefcoco_baseline, coconut_pancap_baseline), each capped by --eval_max_examples.
Known limitations (current code)
_build_presence_labels(loss.py): whenslot_assignmentis set, the loop usesn = min(num_instances, Q, max_instances), so ifnum_instances > Qsome instance indices are never scanned for presence targets (Hungarian may still assign higher indices to slots). Be aware when using dense scenes with smallQ.- Collator assumes the same
Qfor every sample in a batch (bridge_token_mask.sum()from the first example). --query_text_grounding: cannot be turned off via CLI with the current argparse pattern (see table above).
Quick start
cd /weka-mm/royg/ground_qwen3_5
# 1. Run debug test (single sample, full intermediate logging)
python -m ground_qwen35.debug_test
# 2. Full training (8 GPUs, example: global batch 64 = 8 Γ 4 Γ 2 accum)
/opt/conda/bin/torchrun --nproc-per-node 8 -m ground_qwen35.train \
--mode full --batch_size 4 --grad_accum_steps 2 --max_steps 10000 \
--spatial_grid_assignment --presence_mlp \
--num_bridges 225 \
--save_dir /path/to/checkpoints --eval_interval 500 --wandb
# 3. Debug training (single GPU)
python -m ground_qwen35.train --mode debug
# 4. Inference demo (Gradio web UI)
python demo/segmentation_demo.py /weka-mm/royg/ground_ckpts/qwen3_5/mar_10_g8/step3000
# 5. Integration test (2 GPUs, damaged samples, checkpoint resume)
bash ground_qwen35/run_test.sh
Inference demo
python demo/segmentation_demo.py /path/to/checkpoint [--port 7860] [--share]
Three modes: Image + Text (masks + text highlights), Panoptic (empty text), Text-only (no image).
Features: Threshold sliders, per-bridge overlays, bridge checkboxes, presence / coverage / text grounding readouts.
Integration test
bash ground_qwen35/run_test.sh
2-GPU end-to-end: short training with damaged-sample injection, checkpoint save/resume. Verifies DDP, validation, eval, auto-resume.
Verification / smoke testing
# With spatial grid (use perfect-square num_bridges, e.g. 64 or 100)
/opt/conda/bin/torchrun --nproc-per-node 2 -m ground_qwen35.train \
--mode debug --batch_size 2 --max_steps 3 --num_bridges 64 \
--spatial_grid_assignment \
--eval_interval 1 --eval_max_examples 8 --detail_debug --log_interval 1
# Without spatial grid
/opt/conda/bin/torchrun --nproc-per-node 2 -m ground_qwen35.train \
--mode debug --batch_size 2 --max_steps 3 \
--eval_interval 1 --eval_max_examples 8 --detail_debug --log_interval 1
LR schedule
Cosine with warmup (same spirit as ground_olmo CosWithWarmup): linear 0 β peak over warmup_steps, then cosine to alpha_f Γ peak by max_steps.
Param groups: seg_head, text_head (head_lr); presence_head, backbone (backbone_lr).
File structure
ground_qwen3_5/
Qwen3.5-0.8B/ # Pretrained weights
docs/ # Architecture tutorial
demo/
segmentation_demo.py # Gradio inference demo
ground_qwen35/
__init__.py
config.py # GroundQwen35Config
model.py # GroundQwen35 model
grounding_heads.py # VisualSingleSlotGate
token_utils.py # extract_tokens_by_mask
loss.py # Dice+BCE, presence BCE, text BCE (slot_assignment)
train.py # Training, mixtures, monkey patch, CLI
test_training.py # Integration test helpers
run_test.sh # 2-GPU integration test runner
debug_test.py # Single-sample debug test
debug_logger.py # Detailed debug logging
data/
__init__.py
segmentation.py # Mask β merged-token labels, spatial Hungarian
formatter.py # Sequence construction
preprocessor.py # Example preprocessing
collator.py # Batch collation
dataset.py # Source + validation + deterministic wrapper
iterable_mixture.py # Iterable mixture + sharding
README.md # This file
Dependencies
- Python 3.11+
- PyTorch 2.x
- transformers (Qwen3.5 support); model uses Flash Attention 2 where configured
- scipy (Hungarian matching)
- pycocotools (via ground_olmo / COCO)
- gradio (demo)
- ground_olmo at
/weka-mm/royg/ground_olmo(datasets,coco_instances_to_bool_masks, etc.)