Spaces:
Running on Zero
A newer version of the Gradio SDK is available: 6.19.0
title: curbcheck
emoji: 🅿️
colorFrom: red
colorTo: gray
sdk: gradio
app_file: app.py
pinned: true
license: mit
tags:
- build-small-hackathon
- backyard-ai
- vision-language-model
- qlora
- qwen2.5-vl
- track:backyard
- sponsor:modal
- achievement:welltuned
- achievement:fieldnotes
short_description: Can a small VLM tell you if you can legally park in SF?
🅿️ curbcheck
Can a small vision-language model tell you if you can legally park in San Francisco?
Upload a photo of a parking-sign pole, pick a day and time, and a QLoRA-tuned Qwen2.5-VL-3B reads each sign into structured rules. A tiny deterministic resolver then applies them to that moment and returns the verdict, showing you both what the model read and why. Read-then-resolve: the VLM only perceives; the logic is exact.
I came to SF for a week in April 2026 and left with two parking tickets, both because I couldn't parse a pole holding four stacked signs. curbcheck is the model that gets it.
Demo video
Track & badges
- Track: Backyard AI (a real, local, personal problem solved with a small model)
- A 3B model, well under the 32B cap, runs on ZeroGPU.
How it works
photo ─▶ VLM (reads each sign to JSON) ─▶ deterministic resolver ─▶ verdict + reason
- The model only reads the pole into structured restrictions (kind, days, hours, limits, permits).
- A deterministic resolver (
rules.py, no model in the loop) applies them to the current time. - Both halves are shown, so misreads are visible, not buried in a confident sentence.
The result
A stock Qwen2.5-VL-3B scores 0.16 on "can I park here right now", below random. One QLoRA run takes it to 0.82 reasoning and 0.98 read accuracy on the synthetic benchmark; on real SF photos the read-then-resolve pipeline reasons at 0.89.
Full benchmark, training, and honest results: https://github.com/shubhamgoel27/curbcheck
Results in brief
| base Qwen2.5-VL-3B | tuned (QLoRA) | |
|---|---|---|
| Read F1 (synthetic) | 0.34 | 0.98 |
| Reasoning (synthetic) | 0.16 | 0.82 |
| Read F1 (real SF photos) | 0.04 | 0.34 |
| Pipeline reasoning (real) | 0.78 | 0.89 |
The honest part: more real data barely moved real-photo reading (0.33 to 0.34), so that gap is model capacity, not data volume. The deterministic resolver keeps pipeline reasoning at 0.89 even when reading is hard. Full, honest results in the repo.
Links
- Live demo (this Space): https://huggingface.co/spaces/build-small-hackathon/curbcheck
- Demo video: https://huggingface.co/spaces/build-small-hackathon/curbcheck/resolve/main/curbcheck_demo.mp4
- Code + benchmark (GitHub): https://github.com/shubhamgoel27/curbcheck
- Fine-tuned model (QLoRA adapter): https://huggingface.co/shubhamgoel27/curbcheck-qwen25vl3b-v4-lora
- Base model: https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct
- Writeup: https://shubham.gg/artifold-share/30da5524.html