You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

MakeModel-VLM-450M

A 450M-parameter vision-language model that identifies the make and model of vehicles on Indian roads from cropped CCTV / traffic-camera images.

Fine-tuned from LiquidAI/LFM2.5-VL-450M on ~40k labelled vehicle crops across 645 make/model classes.

The class vocabulary is built around the Indian vehicle fleet — auto rickshaws, Honda Activa and Hero Splendor two-wheelers, Tata and Ashok Leyland commercial vehicles, Maruti Suzuki hatchbacks, Mahindra SUVs — rather than the Western model mix that generic VLMs and Stanford-Cars-style datasets assume. Off-the-shelf models tend to answer "Motorcycle" or "Honda"; this one answers "Honda Activa".

At 450M parameters and ~0.9 GB in bf16, it is small enough for edge deployment alongside a detector in an ANPR/traffic pipeline.

Results

Evaluated on a held-out, stratified 4,535-image split (same distribution as training; never seen during training).

Metric Base LFM2.5-VL-450M This model Change
Top-1 exact match (645 classes) 0.0% 41.0%
Top-1 after snapping to label space 4.0% 41.0% 10.2×
Brand-level accuracy 26.5% 62.7% 2.4×
Macro-average per-class accuracy 3.5% 17.2% 4.9×
Outputs that are valid class labels 93.8% 99.8%

The base model scores 0.0% on raw exact match because it replies in sentences ("This appears to be a Honda motorcycle"); the 4.0% column maps those answers onto the label space so the comparison is fair. After fine-tuning, raw and mapped scores are identical — the model emits bare labels directly.

Usage

Requires transformers >= 5.1.

import torch
from transformers import AutoModelForImageTextToText, AutoProcessor
from transformers.image_utils import load_image

model_id = "sanskar003/MakeModel-VLM-450M"

processor = AutoProcessor.from_pretrained(model_id, max_image_tokens=256)
model = AutoModelForImageTextToText.from_pretrained(
    model_id, dtype=torch.bfloat16, device_map="auto"
)

image = load_image("vehicle_crop.jpg")

# Use this exact prompt -- the model was trained on it, and changing the
# wording measurably degrades accuracy.
conversation = [
    {"role": "system", "content": [{"type": "text", "text":
        "You are a vehicle recognition expert. You identify the make and model "
        "of vehicles in cropped CCTV images. Reply with only the make and model."}]},
    {"role": "user", "content": [
        {"type": "image", "image": image},
        {"type": "text", "text": "What is the make and model of this vehicle?"},
    ]},
]

inputs = processor.apply_chat_template(
    conversation, add_generation_prompt=True, tokenize=True,
    return_dict=True, return_tensors="pt",
).to(model.device)

out = model.generate(**inputs, max_new_tokens=16, do_sample=False)
print(processor.tokenizer.decode(
    out[0, inputs["input_ids"].shape[1]:], skip_special_tokens=True).strip())
# -> "Honda Activa"

Input expectations

Feed tight crops of a single vehicle, not full scene frames — that is what the model was trained on. Upscale very small crops so the short side is at least 64px; images are handled at native resolution up to 512px.

The 645 valid labels ship in label_space.json. Snapping generations to the nearest entry is optional here (99.8% are already valid) but recommended if you need a guaranteed closed vocabulary.

Serving with vLLM (Docker)

Verified against vllm/vllm-openai:latest (v0.25.1), which registers Lfm2VlForConditionalGeneration natively — no --trust-remote-code needed. Note that the vLLM docs' supported-models table does not yet list LFM2-VL, but the runtime does support it.

docker run --rm --gpus all -p 8000:8000 --ipc=host \
  vllm/vllm-openai:latest \
  --model sanskar003/MakeModel-VLM-450M \
  --served-model-name makemodel-vlm-450m \
  --max-model-len 8192

On a shared GPU, cap the memory vLLM reserves and pin a device:

docker run --rm --gpus '"device=1"' -p 8000:8000 --ipc=host \
  vllm/vllm-openai:latest \
  --model sanskar003/MakeModel-VLM-450M \
  --served-model-name makemodel-vlm-450m \
  --gpu-memory-utilization 0.35 \
  --max-model-len 8192

Query it with the OpenAI-compatible API:

IMG=$(base64 -w0 vehicle_crop.jpg)
curl -s http://localhost:8000/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d @- <<JSON | python3 -c "import sys,json;print(json.load(sys.stdin)['choices'][0]['message']['content'])"
{
  "model": "makemodel-vlm-450m",
  "max_tokens": 16,
  "temperature": 0,
  "messages": [
    {"role": "system", "content": "You are a vehicle recognition expert. You identify the make and model of vehicles in cropped CCTV images. Reply with only the make and model."},
    {"role": "user", "content": [
      {"type": "image_url", "image_url": {"url": "data:image/jpeg;base64,$IMG"}},
      {"type": "text", "text": "What is the make and model of this vehicle?"}
    ]}
  ]
}
JSON

Or with the openai Python client:

import base64
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="EMPTY")
b64 = base64.b64encode(open("vehicle_crop.jpg", "rb").read()).decode()

resp = client.chat.completions.create(
    model="makemodel-vlm-450m",
    max_tokens=16,
    temperature=0,
    messages=[
        {"role": "system", "content":
            "You are a vehicle recognition expert. You identify the make and model "
            "of vehicles in cropped CCTV images. Reply with only the make and model."},
        {"role": "user", "content": [
            {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{b64}"}},
            {"type": "text", "text": "What is the make and model of this vehicle?"},
        ]},
    ],
)
print(resp.choices[0].message.content.strip())
# -> "Tata Motors Prima"

Keep temperature=0 — the evaluation numbers above are from greedy decoding.

Limitations

Accuracy is strongly tied to how common a model is. Performance tracks training support almost linearly:

Training support Classes Accuracy
1–2 images 412 12.2%
6–10 images 49 26.6%
26–60 images 25 45.5%
151+ images 3 76.8%

443 of the 645 classes get zero correct predictions on the eval split, covering 19% of it. Common vehicles are reliable — Auto Rickshaw 83%, Suzuki Swift 74%, Honda Activa 73%, Mahindra Bolero 74% — but the rare tail is close to unusable. Treat the brand-level output (62.7%) as the more dependable signal when the exact model matters less.

The label vocabulary contains genuine ambiguity. The training data labels the same vehicle several ways (Hero Honda / Hero Splendor / Hero Honda Splendor; Auto / Auto Rickshaw), and includes some bare vehicle types (Scooter, SUV, Truck) alongside real make/models. About 6.8% of eval samples are scored wrong for a defensible answer, so true accuracy is closer to 47.9%. Those classes are also the weakest large ones (Scooter 17%, Hero Honda 19%).

Other constraints:

  • English output only; single vehicle per image.
  • Tuned for Indian road scenes. Expect degradation on other vehicle markets.
  • Trained on daytime-dominant CCTV crops; night, heavy rain, and motion blur are under-represented.
  • No confidence score is emitted. Do not use it as a sole identifier for enforcement, insurance, or legal decisions — pair it with ANPR and human review.

Training

Base LiquidAI/LFM2.5-VL-450M (LFM2.5-350M LM + SigLIP2 NaFlex 86M vision)
Method Full fine-tune, all 448.7M params including the vision tower
Data 40,212 train / 4,535 val crops, 645 classes, stratified split
Epochs 3 (1,887 steps)
Effective batch 64 (8 × 4 grad accum × 2 GPU)
LR 2e-5, cosine, 57 warmup steps
Precision bf16, gradient checkpointing
Hardware 2 × NVIDIA RTX A6000, 41 minutes
Final loss train 0.445 / eval 0.405

Eval loss decreased monotonically across all four checkpoints (0.470 → 0.413 → 0.406 → 0.405), so the final checkpoint is the best one.

The vision tower was deliberately not frozen: roughly 20% of these crops have a short side under 64px, far from the natural-image distribution the encoder was pretrained on. Loss was masked to the assistant answer only, so no gradient is spent on the fixed prompt or image placeholder tokens.

Classes with fewer than 5 examples were dropped, as were images with a short side under 24px and rows with no make/model label.

License

Inherits LFM Open License v1.0 (lfm1.0) from the base model. Review the base model's terms before commercial deployment.

Citation

@misc{makemodel_vlm_450m,
  title  = {MakeModel-VLM-450M: Vehicle Make and Model Recognition for Indian Roads},
  author = {sanskar003},
  year   = {2026},
  url    = {https://huggingface.co/sanskar003/MakeModel-VLM-450M},
  note   = {Fine-tuned from LiquidAI/LFM2.5-VL-450M}
}
Downloads last month
17
Safetensors
Model size
0.4B params
Tensor type
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for sanskar003/MakeModel-VLM-450M

Finetuned
(31)
this model

Evaluation results

  • Top-1 exact match (645 classes) on Indian Road CCTV Vehicle Crops (held-out split)
    self-reported
    0.410
  • Brand-level accuracy on Indian Road CCTV Vehicle Crops (held-out split)
    self-reported
    0.626
  • Macro-average per-class accuracy on Indian Road CCTV Vehicle Crops (held-out split)
    self-reported
    0.172