Image Classification
Assign a label to a whole image: defect/ok, species, product type, diagnosis.
Typical projects
Three ways to build it
Starter
No-code / AutoML vision (Teachable Machine, Roboflow, Vertex AutoML)Upload labeled images and get a model in minutes, no code required.
Best for: No or little data, or new to ML
Roboflow · Teachable Machine
Standard
Fine-tuned pretrained CNN/ViT (ConvNeXt, EfficientNet) via timmTransfer learning gets high accuracy from a few hundred images per class.
Best for: Some labeled data and Python experience
PyTorch · timm · torchvision
Advanced
Vision foundation model (DINOv2 / CLIP) fine-tuning + distillation to a small edge modelBest accuracy, then compress for fast/cheap inference.
Best for: Lots of data and an experienced team
PyTorch · timm · ONNX · TensorRT
How success is measured
Accuracy and per-class recall (for defect detection, missed defects matter most)
The data you'll need
- Collect images in the same conditions as production (lighting, angle, camera).
- Start with 100–300 images per class for transfer learning.
- Organise as one folder per class, e.g. data/train/cat/*.jpg and data/train/dog/*.jpg
Labeling
Folder-per-class is enough. For larger sets use Label Studio, CVAT or Roboflow.
Preparing the data
- Resize to model input size (e.g. 224×224)
- Data augmentation: flips, rotations, color jitter
- Remove near-duplicate images across train/test splits
Start with a baseline
A pretrained model used as a frozen feature extractor + logistic regression.
Evaluating the model
- Confusion matrix
- Look at misclassified images in a grid
- Test on images from a different day/camera
Monitoring in production
- Image brightness/size drift
- Prediction confidence distribution
- Sample images for periodic human audit
Common pitfalls
- Model learns the background, not the object
- Same item photographed twice ends up in train and test
Example code
CodeQuick start
# Zero-shot with CLIP: no training needed
# pip install transformers pillow torch
from transformers import pipeline
clf = pipeline("zero-shot-image-classification", model="openai/clip-vit-base-patch32")
print(clf("leaf.jpg", candidate_labels=["healthy leaf", "leaf with rust disease"]))CodeTrain your own model
# pip install torch torchvision timm
import timm, torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
tf = transforms.Compose([transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(),
transforms.ToTensor(), transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])])
train = datasets.ImageFolder("data/train", tf)
loader = DataLoader(train, batch_size=32, shuffle=True)
model = timm.create_model("convnext_tiny", pretrained=True, num_classes=len(train.classes))
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
opt = torch.optim.AdamW(model.parameters(), lr=1e-4)
for epoch in range(5):
for x, y in loader:
x, y = x.to(device), y.to(device)
loss = torch.nn.functional.cross_entropy(model(x), y)
opt.zero_grad(); loss.backward(); opt.step()
print(epoch, loss.item())
torch.save(model.state_dict(), "model.pt")