Views
No views yet
edge-fall-vlm-2.2b (SmolVLM2-2.2B),
chosen for capability over edge-device footprint — see the honest trade-off below.{"posture":"horizontal-on-floor","status":"down","confidence":0.9,"person_down":true,"n_people":1}
where status ∈ {down, distress, normal} (3-class down3 scheme).edge-fall-vlm-2.2b (SmolVLM2-2.2B) has strong recall on our in-the-wild benchmark, but a
real user-submitted clip (an elderly person falling from a bent/reaching position near a
chair, in a cluttered, low-light room) was missed by every variant of it we tried —
including three separate rounds of synthetic-data improvement (more fall-type coverage,
procedural occlusion, and real photo-textured occlusion + indoor HDRI lighting). None of
those closed the gap. Testing a different base architecture on the exact same clip did:
Qwen3.5-2B, fine-tuned with the same recipe/data as the SmolVLM2 model (no extra
synthetic enrichment), correctly detected the fall — the only model in the investigation
to do so, verified across multiple frame windows spanning the actual fall event.| Model | Accuracy | Recall (danger) | This specific hard real clip |
|---|---|---|---|
| edge-fall-vlm-2.2b (SmolVLM2) | 0.800 | 0.897 | Missed |
| edge-fall-vlm-qwen3.5-2b (this model) | 0.847 | 0.784 | Detected |
edge-fall-vlm-2.2b may be the better choice; see that model's card.edge-fall-vlm-2.2b, for a clean architecture comparison:down3 label scheme, balanced oversampling.1from transformers import AutoModelForImageTextToText, AutoProcessor
2import torch
3proc = AutoProcessor.from_pretrained("Luigi/edge-fall-vlm-qwen3.5-2b", do_image_splitting=False,
4 size={"shortest_edge": 64*64, "longest_edge": 384*384})
5model = AutoModelForImageTextToText.from_pretrained("Luigi/edge-fall-vlm-qwen3.5-2b",
6 dtype=torch.bfloat16).to("cuda").eval()
7prompt = ("You are a safety monitor. These are consecutive video frames (oldest first), "
8 "possibly with more than one person. Report whether ANYONE has fallen, fainted, "
9 "is lying immobile, or is in distress; else normal. Answer with JSON only.")
10frames = [...] # list of PIL images, ~6 frames spanning ~3.5s
11msgs = [{"role":"user","content":[{"type":"image"} for _ in frames]+[{"type":"text","text":prompt}]}]
12text = proc.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True, enable_thinking=False)
13batch = proc(text=[text], images=[frames], return_tensors="pt").to("cuda")
14print(proc.batch_decode(model.generate(**batch, max_new_tokens=64, do_sample=False)[:, batch["input_ids"].shape[1]:], skip_special_tokens=True)[0])enable_thinking=False to apply_chat_template — Qwen's chat
template defaults to injecting a <think> block, which without this flag causes the model
to ramble into open-ended reasoning instead of emitting the trained JSON format directly.