Capability notes
Local image classification splits into two approaches in 2026: specialized vision models (CLIP, SigLIP, DINOv2) and multimodal LLMs prompted to classify. Specialized models are 10–100× faster and more accurate for defined label sets. Multimodal LLMs handle open-vocabulary classification — "is this photo showing signs of water damage?" — without retraining but at 3–20× higher compute cost.
CLIP ViT-L/14 (OpenAI, MIT-licensed) embeds images into a shared text–image space. Zero-shot classification: embed candidate labels, compute cosine similarity to image embedding, pick highest. Accuracy on ImageNet-1K zero-shot: 75.5% top-1. With prompt engineering ("a photo of a {label}") accuracy rises to 76.2%. OpenCLIP ViT-H/14 (LAION-2B trained) reaches 78.0% top-1 — the open-weight zero-shot ceiling. SigLIP (Google, more permissive license) trades some zero-shot flexibility for 2–3× faster inference with a sigmoid loss that produces better-calibrated probabilities.
DINOv2 (Meta, Apache 2.0) produces visual features that excel at fine-grained classification — distinguishing bird species, car models, plant varieties where CLIP's coarse alignment fails. DINOv2 ViT-g/14 reaches 86.4% linear-probe accuracy on iNaturalist 2021 vs CLIP's 72.1%. Requires a small classifier head trained on ~500–5,000 labeled examples per class.
Multimodal LLMs — [Llama 3.2 Vision](/models/llama-3-3-70b), Qwen2.5-VL, InternVL2 — can classify images from natural language descriptions. Accuracy on fine-grained tasks: 82–88% with 11B+ parameter VLMs. Tradeoff: 2–8 seconds per image on [RTX 4090](/hardware/rtx-4090) vs 2–20 ms for a specialized classifier. Use VLMs when your label set changes frequently or classification criteria are subjective. Use CLIP/DINOv2 when labels are stable and throughput matters.
If you just want to try this
Lowest-friction path to a working setup.
Start with [Ollama](/tools/ollama) running a VLM for zero-setup classification. Install Ollama, pull a vision model:
```bash
ollama pull llama3.2-vision:11b-instruct-q4_K_M
```
Then classify images via Ollama's API. Python example using requests:
```python
import requests, base64, json
with open("photo.jpg", "rb") as f:
img_b64 = base64.b64encode(f.read()).decode()
resp = requests.post("http://localhost:11434/api/generate", json={
"model": "llama3.2-vision:11b-instruct-q4_K_M",
"prompt": "Classify this image into one of: indoor, outdoor_nature, outdoor_urban, document.",
"images": [img_b64], "stream": False
})
print(resp.json()["response"])
```
This runs on [RTX 3060 12GB](/hardware/rtx-3060-12gb), [RTX 4060 Ti 16GB](/hardware/rtx-4060-ti-16gb), or any 8 GB+ GPU. Classification time: 2–5 seconds per image on consumer hardware.
For higher throughput (100+ images/second), switch to CLIP via [Hugging Face Transformers](/tools/transformers):
```python
from transformers import pipeline
classifier = pipeline("zero-shot-image-classification", model="openai/clip-vit-large-patch14")
result = classifier("photo.jpg", candidate_labels=["indoor", "outdoor_nature", "outdoor_urban", "document"])
print(result[0]["label"], result[0]["score"])
```
CLIP ViT-L uses 2.5 GB VRAM and classifies at 30–80 images/second on [RTX 4090](/hardware/rtx-4090). For fine-grained classification (species, products, defects), use DINOv2 with a linear probe — train on 500 labeled examples with scikit-learn's LogisticRegression on extracted features.
For production deployment
Operator-grade recommendation.
Production image classification pipelines prioritize throughput, accuracy, and latency based on use case.
**Batch inference pipeline.** For 10K–1M images/day at <100 ms latency, use [ONNX Runtime](/tools/llama-cpp) with CLIP ViT-B/32 quantized to FP16. Preprocess images to 224×224 on CPU, run encoder on GPU. Throughput on [L4](/hardware/nvidia-l4) (24 GB): 600–900 images/second at batch=64. On [L40S](/hardware/nvidia-l40s) (48 GB): 1,200–1,800 images/second.
**Fine-grained classification.** DINOv2 ViT-g + linear classifier head. Extract features once per image (cached to vector DB), classify via lightweight logistic regression. Retrieval: cosine similarity against labeled embeddings in [pgvector](/tools/pgvector) or [Qdrant](/tools/qdrant). 1M stored embeddings at 1536 dimensions = 6 GB storage. Retrieval + classification at 5,000–15,000 queries/second on CPU.
**Accuracy targets.** General multi-class (ImageNet-style, 1000 classes): 75–82% top-1 for zero-shot CLIP/SigLIP; 85–90% for fine-tuned. Fine-grained (species, product SKU): 80–92% with DINOv2 + linear probe. Subjective classification (image quality, aesthetic rating): 65–78% — multimodal LLMs outperform specialized models on subjective tasks but at 50–100× higher compute. Benchmark on your data — ImageNet zero-shot accuracy does not predict domain-specific accuracy.
**Multi-model ensemble.** Route easy samples (high CLIP confidence >0.9) through fast CLIP; route ambiguous samples to a VLM like [Llama 3.2 Vision](/models/llama-3-3-70b) via [vLLM](/tools/vllm). Typical split: 80% fast, 20% VLM. Reduces average latency from 3 seconds (all-VLM) to 150 ms while maintaining 92%+ accuracy on ambiguous 20%.
**Label drift monitoring.** Production label distributions shift. Implement drift detection: compare daily class distribution against 30-day rolling average using KS test or PSI. Trigger retraining when PSI > 0.1. Embedding drift: track mean cosine similarity between daily and reference embeddings — alert when similarity drops below 0.95.
What breaks
Failure modes operators see in the wild.
**Zero-shot label mismatch.** Symptom: CLIP assigns high confidence to wrong labels. "A photo of a jaguar" applied to a car image returns 0.89 confidence for "jaguar" (animal). Cause: CLIP encodes both visual and textual semantics — ambiguous labels produce dual-domain matches. Mitigation: add context to labels ("a wildlife photo of a jaguar" vs "a photo of a Jaguar automobile"), use SigLIP for better-calibrated probabilities, maintain negative label set for disambiguation.
**Fine-grained confusion.** Symptom: classifying bird species — CLIP assigns "American Robin" 0.67 vs "Varied Thrush" 0.64 (both correct-looking). Cause: CLIP's contrastive pretraining optimizes for coarse image–text alignment, not fine-grained visual differences. Mitigation: switch to DINOv2 with linear probe for fine-grained domains. DINOv2's self-supervised training learns per-patch features that capture species-level detail.
**Open-vocabulary hallucination with VLMs.** Symptom: VLM classifies image of an empty desk as containing "a laptop, coffee cup, and notebook" when none are present. Cause: VLMs are generative — they predict plausible descriptions, not constrained to observable elements. Mitigation: prompt-engineer with "List ONLY items VISIBLE in the image. Do not infer or assume." Use structured output (JSON schema) forcing a boolean per candidate label. For critical classification, use CLIP + VLM agreement — require both agree for confidence.
**Batch OOM on high-res inputs.** Symptom: 4K×3K photos at batch=32 exhaust 24 GB VRAM in preprocessing. Cause: image encoder processes full-resolution inputs before pooling — VRAM scales with (height × width × batch). Mitigation: resize to model's native resolution (224×224 for CLIP, 384×384 for ViT-L) before batching. Use a CPU-based preprocessing pipeline (Albumentations, torchvision) feeding GPU in streaming mode.
**Label space explosion.** Symptom: zero-shot classification with 10,000+ candidate labels — 90+ seconds per image. Cause: classification runs dot product against every candidate — O(labels × embedding_dim). Mitigation: hierarchical classification — first-stage CLIP into broad category (20 classes), second-stage specialized model within category. Reduces compute from O(10K) to O(√10K + 20). Precompute text embeddings once and reuse.
Hardware guidance
**Hobbyist tier ($300–600).** [RTX 3060 12GB](/hardware/rtx-3060-12gb): CLIP ViT-L at 15–30 images/second, DINOv2 ViT-B at 25–45 images/second. Fits VLM classification (llama3.2-vision:11b Q4) at 3–8 seconds/image. [Intel Arc B580](/hardware/intel-arc-b580) at 12 GB: CLIP at 20–35 images/second via OpenVINO backend. [RX 7600 XT](/hardware/rx-7600-xt) at 16 GB: CLIP at 15–25 images/second — AMD ROCm support for Transformers is spotty; fallback to ONNX Runtime with DirectML.
**Enthusiast tier ($1,500–2,500).** [RTX 4090](/hardware/rtx-4090) at 24 GB: CLIP ViT-L at 60–80 images/second batch=16, VLM classification at 2–3 seconds/image. Runs both CLIP pipeline + VLM fallback on same card. [RTX 5080](/hardware/rtx-5080) at 16 GB: CLIP at 50–70 images/second but VLM requires 16 GB — tight for 11B models. [RTX 5090](/hardware/rtx-5090) at 32 GB: same throughput as 4090 for classification but handles larger VLM batch sizes and 90B vision models.
**SMB tier ($6,000–15,000).** [RTX 6000 Ada](/hardware/rtx-6000-ada) at 48 GB: CLIP at 80–110 images/second batch=64. Fits DINOv2 ViT-g (1.1B params) for fine-grained classification at 30–50 images/second. [L40S](/hardware/nvidia-l40s) at 48 GB: identical profile, datacenter form factor.
**Enterprise tier ($25,000+).** [A100 80GB SXM](/hardware/nvidia-a100-80gb-sxm): DINOv2 ViT-g batch=128 at 80–120 images/second. Handles multi-model ensemble (CLIP + DINOv2 + VLM) in single GPU. [H100 PCIe](/hardware/nvidia-h100-pcie): provides no meaningful throughput gain for classification — encoders are memory-bandwidth-bound, not compute-bound. A100's 2.0 TB/s vs H100's 2.0 TB/s = identical.
**CPU-only classification.** 16-core x86: CLIP ViT-B at 2–5 images/second via ONNX Runtime with OpenVINO optimizations. Acceptable for <1,000 images/day. [Apple M4 Pro](/hardware/apple-m4-pro): CLIP at 15–25 images/second via CoreML — best CPU inference. DINOv2 ViT-B: 8–12 images/second.
Runtime guidance
**Transformers vs ONNX vs vLLM — runtime selection by throughput requirement.**
[Hugging Face Transformers](/tools/transformers) is the default: `pipeline("zero-shot-image-classification")` works out of the box. CLIP, SigLIP, DINOv2 all supported natively. Best for prototyping, research, and sub-1,000 images/day. Python API is consistent across model architectures. Tradeoff: ~20–40% lower throughput than optimized runtimes due to dynamic batching overhead and Python GIL.
For production throughput, export models to ONNX and use ONNX Runtime with CUDA execution provider. A CLIP ViT-B ONNX model processes 3–5× more images/second than the equivalent Transformers pipeline, primarily from eliminating Python overhead on per-token operations and using fused CUDA kernels. Export script: `optimum-cli export onnx --model openai/clip-vit-base-patch32 clip_onnx/`. For Intel GPUs, use OpenVINO execution provider; for AMD, use ROCm or DirectML.
[MLX-LM](/tools/mlx-lm) on Apple Silicon: CLIP ViT-L via MLX at 30–50 images/second on [M4 Max](/hardware/macbook-pro-16-m4-max). Native Metal acceleration without CUDA translation layer. MLX's unified memory model eliminates GPU→CPU transfers for preprocessing — load image via PIL, resize on CPU, MLX handles GPU transfer internally.
For VLM-based classification at scale, [vLLM](/tools/vllm) with its continuous batching serves llama3.2-vision and Qwen2.5-VL at 15–30 concurrent requests on single [L40S](/hardware/nvidia-l40s). Each classification request (image + label prompt) processes in 2–5 seconds. vLLM's prefix caching reuses vision encoder outputs when same image receives multiple label queries — useful for multi-label classification where one image is queried against 20+ label sets.
**Decision tree.** <1,000 images/day, changing labels: Transformers CLI + VLM. 1K–100K images/day, stable labels: ONNX Runtime + CLIP/SigLIP. >100K images/day: ONNX Runtime + custom C++ serving with gRPC. Fine-grained classification: DINOv2 feature extraction (Transformers) + [pgvector](/tools/pgvector) for similarity search. VLM fallback for ambiguous cases: [Ollama](/tools/ollama) for <100 ambiguous/day, [vLLM](/tools/vllm) for >100/day.