Quick start
import torch
from PIL import Image
from transformers import AutoModelForImageTextToText, AutoProcessor
MODEL = "ReconAI/Qwen3.5-0.8B-Detection"
processor = AutoProcessor.from_pretrained(MODEL)
model = AutoModelForImageTextToText.from_pretrained(MODEL, torch_dtype=torch.bfloat16, device_map="auto")
image = Image.open("demo.jpg").convert("RGB")
categories = ["person", "accessory/umbrella", "accessory/handbag"]
prompt = (
"\n Locate every instance that belongs to the following categories: \n"
+ "".join(f"\t{c}\n" for c in categories)
+ " \nReport bbox coordinates in JSON format."
)
messages = [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": prompt}]}]
text = processor.apply_chat_template(
messages, add_generation_prompt=True, tokenize=False, enable_thinking=False
)
inputs = processor(images=[image], text=[text], add_special_tokens=False, return_tensors="pt").to(model.device)
with torch.inference_mode():
generated = model.generate(**inputs, max_new_tokens=1024, do_sample=False)
response = processor.tokenizer.decode(generated[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
print(response)
Output:
```json
[
{"bbox_2d": [332, 88, 697, 227], "label": "accessory/umbrella"},
{"bbox_2d": [231, 227, 482, 989], "label": "person"},
{"bbox_2d": [376, 202, 602, 989], "label": "person"},
{"bbox_2d": [506, 234, 762, 989], "label": "person"},
{"bbox_2d": [190, 366, 366, 697], "label": "accessory/handbag"},
{"bbox_2d": [666, 380, 839, 676], "label": "accessory/handbag"}
]
```
Coordinates
bbox_2d is [x0, y0, x1, y1], normalized to 0–1000, with x normalized by image width and
y by image height independently. To convert back to pixels:
x_px = x / 1000 * image.width
y_px = y / 1000 * image.height
Categories
Category names must include the prefix — use "animal/dog", not "dog". List only the
categories you care about; the shorter the list, the more accurate the output.
COCO_CATEGORIES = [
"person", "vehicle/bicycle", "vehicle/car", "vehicle/motorcycle", "vehicle/airplane",
"vehicle/bus", "vehicle/train", "vehicle/truck", "vehicle/boat", "outdoor/traffic light",
"outdoor/fire hydrant", "outdoor/stop sign", "outdoor/parking meter", "outdoor/bench",
"animal/bird", "animal/cat", "animal/dog", "animal/horse", "animal/sheep", "animal/cow",
"animal/elephant", "animal/bear", "animal/zebra", "animal/giraffe", "accessory/backpack",
"accessory/umbrella", "accessory/handbag", "accessory/tie", "accessory/suitcase",
"sports/frisbee", "sports/skis", "sports/snowboard", "sports/sports ball", "sports/kite",
"sports/baseball bat", "sports/baseball glove", "sports/skateboard", "sports/surfboard",
"sports/tennis racket", "kitchen/bottle", "kitchen/wine glass", "kitchen/cup", "kitchen/fork",
"kitchen/knife", "kitchen/spoon", "kitchen/bowl", "food/banana", "food/apple", "food/sandwich",
"food/orange", "food/broccoli", "food/carrot", "food/hot dog", "food/pizza", "food/donut",
"food/cake", "furniture/chair", "furniture/couch", "furniture/potted plant", "furniture/bed",
"furniture/dining table", "furniture/toilet", "electronic/tv", "electronic/laptop",
"electronic/mouse", "electronic/remote", "electronic/keyboard", "electronic/cell phone",
"appliance/microwave", "appliance/oven", "appliance/toaster", "appliance/sink",
"appliance/refrigerator", "indoor/book", "indoor/clock", "indoor/vase", "indoor/scissors",
"indoor/teddy bear", "indoor/hair drier", "indoor/toothbrush",
]
Training
Table with columns: Stage, Setup| Stage | Setup |
|---|
| SFT | Full COCO 2017 train detection annotations |
| RL | GSPO, 600 steps, reward = soft-count F1 (IoU as partial credit) |
On the validation set (first 200 images of COCO val2017), soft-F1 improved from 0.437 to 0.479
over RL. The gain comes mainly from precision: the average number of predicted boxes dropped
from 10.5 to 6.2 while the ground-truth count stayed around 6.5 — the model learned to stop
over-reporting.
sft training on coco2017
gspo training on coco2017
Limitations
- Vocabulary is limited to the COCO 80 categories; names outside it were never trained on
- A 0.8B autoregressive VLM — weaker than dedicated detectors (DETR / YOLO family) on dense
small objects and heavy occlusion
- Single-image input, output capped at 1024 tokens
Any questions or feedback? Feel free to reach out to me at yeats.hu@gmail.com.
Acknowledgements
Qwen3.5 · COCO · GSPO