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 ~85k labelled vehicle crops across 369 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 9,437-image split, never seen during training.

Metric Base LFM2.5-VL-450M This model Change
Top-1 exact match (369 classes) 0.0% 46.0% —
Top-1 after snapping to label space 4.6% 46.0% 10.0×
Brand-level accuracy 23.6% 67.9% 2.9×
Macro-average per-class accuracy 3.5% 27.4% 7.9×
Outputs that are valid class labels 79.5% 99.97% —

The base model scores 0.0% on raw exact match because it replies in sentences ("This appears to be a Honda motorcycle"); the 4.6% 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.

Common vehicles are reliable:

Class Accuracy
Auto Rickshaw 87.8%
Honda CBR600RR 77.6%
Suzuki Swift 75.6%
Mahindra Bolero 71.5%
Hyundai Creta 71.3%

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 369 valid labels ship in label_space.json. Snapping generations to the nearest entry is optional here (99.97% 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:

Eval support Classes Accuracy
1–10 images 225 20.5%
11–25 images 68 31.8%
26–60 images 39 41.6%
61–150 images 23 45.2%
151+ images 14 62.1%

146 of the 369 classes get zero correct predictions on the eval split. Common vehicles are dependable; the rare tail is not. Treat the brand-level output (67.9%) as the more reliable 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, and a particularly tangled Tata family (Tata, Tata 407, Tata Motors, Tata Motors 407, Tata Motors Prima) — and includes bare vehicle types (Scooter, Motorcycle, Auto) alongside real make/models. About 7.5% of eval samples are scored wrong for a defensible answer, so true accuracy is closer to 53.5%.

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 84,995 train / 9,437 val crops, 369 classes, stratified split
Effective batch 64 (8 × 4 grad accum × 2 GPU)
LR 2e-5, cosine, 3% warmup
Precision bf16, gradient checkpointing
Final loss train 0.355 / eval 0.321

Eval loss plateaued at 0.321 and the best checkpoint was selected on eval loss rather than taking the last step.

The vision tower was deliberately not frozen: a substantial share 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 30 examples were dropped, as were images with a short side under 24px. Images appearing more than once with contradictory labels were removed rather than arbitrated.

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
36
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
(33)
this model

Evaluation results

  • Top-1 exact match (369 classes) on Indian Road CCTV Vehicle Crops (held-out split)
    self-reported
    0.460
  • Brand-level accuracy on Indian Road CCTV Vehicle Crops (held-out split)
    self-reported
    0.679
  • Macro-average per-class accuracy on Indian Road CCTV Vehicle Crops (held-out split)
    self-reported
    0.274