MSG1999/vit-lora-cifar100
<div align="center">
ViT-Small + LoRA — CIFAR-100
Parameter-efficient fine-tuning of ViT-S/16 on CIFAR-100 · Val Acc: 90.46% · Test Acc: 90.44%
  ![Val Acc]() ![Test Acc]()   
</div>
Overview
This repository contains best_model.pt — the full merged state dict of a ViT-Small/16 model fine-tuned on CIFAR-100 using Low-Rank Adaptation (LoRA). Only the Q, K, V attention projections and classification head were updated during training. All other weights remain frozen.
Architecture
ViT-Small/16 (patch=16, dim=384, heads=6, layers=12)
├── Patch Embedding [frozen]
├── Transformer Encoder × 12
│ ├── Multi-Head Self-Attention
│ │ ├── Query ── LoRA(A·B, r=8) ✅ trained
│ │ ├── Key ── LoRA(A·B, r=8) ✅ trained
│ │ └── Value ── LoRA(A·B, r=8) ✅ trained
│ ├── LayerNorm [frozen]
│ └── MLP (FFN) [frozen]
└── Classification Head (384 → 100) ✅ trainedLoRA update rule: W' = W + (α/r) · B·A where W is frozen, A ∈ ℝ^{r×d} and B ∈ ℝ^{d×r} are learned. With r=8 and α=8, the scaling factor α/r = 1.0.
Hyperparameters
LoRA (best configuration)
Training
Data augmentation (train)
Experiment Results
Grid search — all 10 runs
Optuna hyperparameter search — 10 trials
Optuna searched over rank ∈ {2, 4, 8}, alpha ∈ {2, 4, 8}, and dropout ∈ [0.05, 0.30].
Key findings:
- rank=8, alpha=8 consistently tops the leaderboard across both search phases.
- Higher dropout (0.30 vs 0.10) with the best config yields nearly identical accuracy (90.39% vs 90.46%), confirming robustness.
- Increasing rank beyond 8 or alpha beyond 8 was not explored but is unlikely to yield significant gains given the plateau.
- LoRA provides +9.69 pp over the frozen-backbone baseline at just 1.18% parameter cost.
Quickstart
Install dependencies
pip install torch torchvision transformers peft huggingface_hub PillowLoad the model and run inference
import torch
from transformers import ViTForImageClassification, ViTImageProcessor
from peft import LoraConfig, get_peft_model
from huggingface_hub import hf_hub_download
from PIL import Image
REPO = "MSG1999/vit-lora-cifar100"
BASE = "WinKawaks/vit-small-patch16-224"
CIFAR100_CLASSES = [
"apple", "aquarium_fish", "baby", "bear", "beaver", "bed", "bee", "beetle",
"bicycle", "bottle", "bowl", "boy", "bridge", "bus", "butterfly", "camel",
"can", "castle", "caterpillar", "cattle", "chair", "chimpanzee", "clock",
"cloud", "cockroach", "couch", "crab", "crocodile", "cup", "dinosaur",
"dolphin", "elephant", "flatfish", "forest", "fox", "girl", "hamster",
"house", "kangaroo", "keyboard", "lamp", "lawn_mower", "leopard", "lion",
"lizard", "lobster", "man", "maple_tree", "motorcycle", "mountain", "mouse",
"mushroom", "oak_tree", "orange", "orchid", "otter", "palm_tree", "pear",
"pickup_truck", "pine_tree", "plain", "plate", "poppy", "porcupine",
"possum", "rabbit", "raccoon", "ray", "road", "rocket", "rose", "sea",
"seal", "shark", "shrew", "skunk", "skyscraper", "snail", "snake", "spider",
"squirrel", "streetcar", "sunflower", "sweet_pepper", "table", "tank",
"telephone", "television", "tiger", "tractor", "train", "trout", "tulip",
"turtle", "wardrobe", "whale", "willow_tree", "wolf", "woman", "worm",
]
id2label = {i: c for i, c in enumerate(CIFAR100_CLASSES)}
label2id = {c: i for i, c in id2label.items()}
# 1. Reconstruct model with the same LoRA config used during training
base_model = ViTForImageClassification.from_pretrained(
BASE,
num_labels=100,
id2label=id2label,
label2id=label2id,
ignore_mismatched_sizes=True,
)
lora_config = LoraConfig(
r=8,
lora_alpha=8,
lora_dropout=0.1,
target_modules=["query", "key", "value"],
bias="none",
)
model = get_peft_model(base_model, lora_config)
# 2. Download and load best_model.pt
ckpt_path = hf_hub_download(repo_id=REPO, filename="best_model.pt")
state_dict = torch.load(ckpt_path, map_location="cpu")
model.load_state_dict(state_dict, strict=False)
model.eval()
print("Model loaded successfully.")
# 3. Inference
processor = ViTImageProcessor.from_pretrained(BASE)
image = Image.open("your_image.jpg").convert("RGB")
inputs = processor(images=image, return_tensors="pt")
with torch.no_grad():
logits = model(**inputs).logits
pred_id = logits.argmax(-1).item()
confidence = logits.softmax(-1)[0, pred_id].item()
print(f"Predicted class : {id2label[pred_id]}")
print(f"Confidence : {confidence * 100:.1f}%")Batch inference
images = [Image.open(p).convert("RGB") for p in image_paths]
inputs = processor(images=images, return_tensors="pt")
with torch.no_grad():
logits = model(**inputs).logits
preds = logits.argmax(-1).tolist()
for path, pred in zip(image_paths, preds):
print(f"{path} → {id2label[pred]}")Repository files
Training code, logs, and all experiment weights are available in the GitHub repository.
Citation
@misc{gadiya2026vitlora,
title = {ViT-Small + LoRA Fine-tuning on CIFAR-100},
author = {Mahek Gadiya},
year = {2026},
note = {DLOps Assignment 5 — Q1, IIT Jodhpur},
url = {https://huggingface.co/MSG1999/vit-lora-cifar100},
}<div align="center"> DLOps Assignment 5 | IIT Jodhpur | <a href="https://huggingface.co/MSG1999">MSG1999</a> </div>
