Skip to content
ModelListedExternal
Browse files

Vit Flower Classifier

Fine-tuned torchvision.models.vitb16 (ImageNet-1K pretrained) for 102-class flower classification on the Oxford-102 Flowers dataset, with the full backbone unfrozen during fine-tuning. Achieves 0.9837 accuracy /...

KindOtherVersionRevision 3bb7156Cgrade

Model source

Kind
Other
File size
328 MB
Version
vRevision 3bb7156
Updated
Oct 10, 2026
Downloads
0
Source
Hugging Face

Source description

ViT-B/16 Flower Classifier

Fine-tuned torchvision.models.vit_b_16 (ImageNet-1K pretrained) for 102-class flower classification on the Oxford-102 Flowers dataset, with the full backbone unfrozen during fine-tuning. Achieves 0.9837 accuracy / 0.9722 macro-F1 on a held-out test split — the best of every architecture evaluated for this project.

Note: this card describes a new checkpoint, retrained after a data leak in the training pipeline's split logic was fixed. The weights previously published under this repo were leak-era and were selected on a corrupted validation signal — see Training history. If you pinned an earlier revision, re-download.

Recommended when macro-F1 is the priority and the size budget is not binding — offline batch labeling, research baselines, or any deployment where 343.5 MB of weights is acceptable. For most serving, ConvNeXt-Tiny is the better trade: it scores 0.9694 macro-F1 against this model's 0.9722, at 111.6 MB instead of 343.5 MB.

Usage

import torch
from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
from torchvision import models

weights_path = hf_hub_download(repo_id="bengid/vit-flower-classifier", filename="vit-flower-classifier.safetensors")

model = models.vit_b_16(weights=None)
model.heads[-1] = torch.nn.Linear(model.heads[-1].in_features, 102)
model.load_state_dict(load_file(weights_path, device="cpu"))
model.eval()

# preprocessing: Resize((256, 256)) -> CenterCrop(224) -> ToTensor -> Normalize(dataset mean/std)
# note: the resize is to a fixed 256x256 square, NOT shortest-side-256 -- aspect ratio is not preserved
# see src/utils.py:get_transforms() in the training repo for the exact pipeline
# (https://github.com/ben-gid/flowers/blob/main/src/utils.py)

Training Data

Oxford-102 Flowers — 8,189 images across 102 flower species, downloaded via torchvision.datasets.Flowers102. Class-weighted CrossEntropyLoss was used to correct for the dataset's uneven per-class image counts.

Training Procedure

Single-stage fine-tune with a two-phase backbone unfreeze callback (BackboneFinetuning): the ViT backbone starts frozen (only the classification head trains), then unfreezes at a fixed epoch with its own, lower learning rate and a separate parameter group — unlike this project's original (v1) EfficientNet-B0 model, which only ever unfroze its last 3 backbone blocks.

Trained under the corrected-split-retrain MLflow experiment, i.e. after the split bug described in Training history was fixed. Checkpoint selection and early stopping were therefore driven by a genuine held-out validation signal.

Hyperparameters

ParameterValue
OptimizerAdamW
LR schedulerCosine annealing (T_max=50, eta_min=1e-06)
Head LR (before unfreeze)0.001
Head LR (after unfreeze)0.0003
Backbone LR (after unfreeze)2e-05
Unfreeze epoch5
Max epochs50
Batch size64
Effective batch size256
Gradient accumulation4
Precisionbf16-mixed
Weight decay0.01
Early stopping patience8

Evaluation

Oxford-102 split 70/15/15 by random_split(seed=42) → 5,733 train / 1,228 val / 1,228 test. Val and test are disjoint from train and from each other.

MetricValidationTest
Accuracy0.98370.9837
Macro F10.97370.9722
Loss0.07970.0762
PropertyValue
Parameters85,877,094
Model size343.5 MB
Checkpoint size1030.7 MB
Mean latency5.39 ms
p95 latency5.68 ms

Latency measured on an NVIDIA RTX 5070 (cuda:0) at batch size 1, with torch.cuda.synchronize() after every forward pass. These figures do not transfer to CPU. An earlier CPU benchmark of the same architectures put ViT-B/16 at roughly 3x the latency of the other two, while ConvNeXt-Tiny and EfficientNetV2-S landed within a few milliseconds of each other — close enough that two runs of that benchmark disagreed on which was faster. Benchmark on your own serving hardware before treating any of this as a ranking.

Macro F1 sits below accuracy on both splits, which is the signature of uneven per-class performance on a dataset whose class counts range from 40 to 258 images — the rare classes are where the misses are. This model has the narrowest accuracy-to-F1 spread of the three, though it leads ConvNeXt-Tiny only marginally on that measure. Per-class F1 is computed but not currently exported; see test_per_class_f1 in src/classifier.py.

Training history

An earlier version of this card claimed 1.0 accuracy / 1.0 F1. That was a measurement bug, not a real result. FlowerDataModule.setup() in src/data.py built all three splits from the same subset:

train_subset, val_subset, test_subset = random_split(full_ds, (0.7, 0.15, 0.15), generator=generator)

self.train_set = SubsetWithTransform(train_subset, self.transform_train)
self.val_set   = SubsetWithTransform(train_subset, self.transform_test)  # bug — should be val_subset
self.test_set  = SubsetWithTransform(train_subset, self.transform_test)  # bug — should be test_subset

val_subset and test_subset were built and then discarded. Validation and test scored the model against the exact images it had trained on — total leakage, not partial. A ~1.0 score was the expected outcome of memorization and carried no information about generalization.

The weights were never contaminated. Training only ever read train_subset, so no held-out image ever reached a gradient. What the bug corrupted was measurement — and, through it, model selection: ModelCheckpoint and EarlyStopping were both driven by val_acc, which was really train accuracy.

This checkpoint replaces the leak-era one. It comes from the corrected-split-retrain experiment, trained after the fix, so every metric above was measured on genuinely held-out data and the checkpoint was selected on a real validation signal.

One finding worth recording: the retrain recovered less than expected. The leak-era ViT checkpoint, re-scored honestly on the same held-out data, reached 0.9637 test macro-F1 against this model's 0.9722. The bug inflated the reported numbers substantially; it cost the weights comparatively little.

Strengths & Weaknesses

Strengths:

  • Best accuracy and macro-F1 of every architecture evaluated for this project — attention-based global context handles flowers that differ mainly in overall shape/arrangement rather than local texture.
  • Narrowest accuracy-to-macro-F1 spread of the three published models, so its errors are the least concentrated in rare classes — though it is only marginally ahead of ConvNeXt-Tiny here.
  • Full-backbone fine-tuning lets every ViT layer adapt to the flower domain, avoiding the ceiling that partial-unfreeze approaches hit.

Weaknesses:

  • Largest model in the lineup by a wide margin — 85,877,094 parameters and 343.5 MB of weights, roughly three times ConvNeXt-Tiny, for a macro-F1 margin measured in fractions of a point. For most serving that trade is not worth paying.
  • The accuracy lead over ConvNeXt-Tiny is small enough (0.9722 vs 0.9694) that it is within the range a different seed or a longer schedule could move. Treat it as a lead, not a law.
  • ViTs are comparatively data-hungry and were historically harder to fine-tune from limited data before full-backbone unfreezing plus a long enough schedule — this model only reaches its ceiling because both were used.
  • Not a good fit for edge/mobile deployment given its size.

Limitations

  • Closed-set, single-label: trained on exactly 102 Oxford flower species; will confidently misclassify any other flower species, non-flower image, or multi-flower image into one of the 102 known classes — there is no out-of-distribution rejection.
  • Fixed input pipeline: expects a 224×224 input produced by resizing to a fixed 256×256 square and center-cropping. The resize does not preserve aspect ratio, so non-square images are distorted before the crop, and off-center subjects can be cropped out of frame.
  • No adversarial robustness or calibration guarantees — confidence scores are not calibrated probabilities.
  • Reported metrics are on held-out Oxford-102 val/test splits; real-world images (different lighting, backgrounds, camera quality) may perform worse.
  • Macro F1 (0.9722) is the number to trust, not accuracy (0.9837) — Oxford-102 is class-imbalanced (40–258 images per class), and the gap between the two means errors concentrate in rare classes. If your use case cares about the long tail, budget for the F1 figure.

Intended Use

Intended uses:

  • Flower species identification within the 102 Oxford-102 classes (gardening/botany apps, educational tools, dataset labeling).
  • Backend model for this project's v2 /classify API endpoint when macro-F1 is prioritized over model size.

Out-of-scope uses:

  • General-purpose plant, object, or scene classification outside the 102 trained species.
  • Medical, toxicity, or safety-related plant identification.
  • Any use where a wrong classification has safety or financial consequences without human review.

Model Comparison

All figures are test-split metrics on held-out data. The three current models were trained on the corrected splits; the two v1 baselines are listed for historical context.

ModelTest AccTest F1ParamsSize (MB)Best For
SimpleCNN (scratch)~0.63---historical baseline only
EfficientNet-B0 (v1, partial unfreeze)>0.93---historical baseline only
ViT-B/16 (this model)0.98370.972285,877,094343.5maximum macro-F1
ConvNeXt-Tiny0.98210.969427,898,566111.6default serving choice
EfficientNetV2-S0.96910.943320,308,15081.8smallest weights

The two v1 models were trained by the older, pre-Lightning pipeline (api/app/v1/flowers/train_scratch.py), which split train/val/test correctly — their numbers were never affected by the leak and are directly comparable to the corrected figures above.

Why the earlier models underperformed

  • SimpleCNN (scratch) was trained from randomly initialized weights with no ImageNet pretraining, on a 6-block custom CNN — too little capacity and too little prior visual knowledge to learn 102 fine-grained flower classes from ~8k images alone.
  • EfficientNet-B0 (v1) started from ImageNet-pretrained weights but only ever unfroze its last 3 backbone blocks during fine-tuning (see this project's root README.md for the...

Sources

1 source
Hugging FacePrimaryVersion 3bb71561cbe4a96848a72f4f58bf2fb41a6e46d9
huggingface.co/bengid/vit-flower-classifier/tree/3bb71561cbe4a96848a72f4f58bf2fb41a6e46d9

Verified Oct 10

Download files

Model artifacts

2 artifacts

vit-flower-classifier.pth

pth · 328 MB · SHA-256 d798e70a3ca0…bc74 · Hugging Face

Download file

vit-flower-classifier.safetensors

safetensors · 328 MB · SHA-256 6b7571db13f2…b518 · Hugging Face

Download file

Source excerpts

3 excerpts

--- license: apache-2.0 library_name: pytorch pipeline_tag: image-classification tags: - image-classification - flowers - oxford-102 - torchvision - vit - transfer-learning metrics: - accuracy - f1 model-index: - name: ViT-B/16 Flower Classifier results: - task: type: image-classification dataset: name: Oxford-102 Flowers type: oxford-102-flowers split: validation metrics: - type: accuracy value: 0.9837 - type: f1 value: 0.9737 - task: type: image-classification dataset: name: Oxford-102 Flowers type: oxford-102-flowers split: test metrics: - type: accuracy value: 0.9837 - type: f1 value: 0.9722 --- # ViT-B/16 Flower Classifier Fine-tuned [`torchvision.models.vit_b_16`](https://docs.pytorch.org/vision/main/models/vision_transformer.html) (ImageNet-1K pretrained) for 102-class flower classification on the Oxford-102 Flowers dataset, with the full backbone unfrozen during fine-tuning. Achieves **0.9837 accuracy / 0.9722 macro-F1** on a held-out test split — the best of every architecture evaluated for this project. > **Note:** this card describes a **new checkpoint**, retrained after a data leak in the training pipeline's split logic was fixed. The weights previously published under this repo were leak-era and were selected on a corrupted validation signal — see [Training history](#training-history). If you pinned an earlier revision, re-download. **Recommended when** macro-F1 is the priority and the size budget is not binding — offline batch labeling, research baselines, or any deployment where 343.5 MB of weights is acceptable. For most serving, [ConvNeXt-Tiny](https://huggingface.co/bengid/convnext-tiny-flower-classifier) is the better trade: it scores 0.9694 macro-F1 against this model's 0.9722, at 111.6 MB instead of 343.5 MB. ## Usage ```python import torch from huggingface_hub import hf_hub_download from safetensors.torch import load_file from torchvision import models weights_path = hf_hub_download(repo_id="bengid/vit-flower-classifier", filename="vit-flower-classifier.safetensors") model = models.vit_b_16(weights=None) model.heads[-1] = torch.nn.Linear(model.heads[-1].in_features, 102) model.load_state_dict(load_file(weights_path, device="cpu")) model.eval() # preprocessing: Resize((256, 256)) -> CenterCrop(224) -> ToTensor -> Normalize(dataset mean/std) # note: the resize is to a fixed 256x256 square, NOT shortest-s...

--- license: apache-2.0 library_name: pytorch pipeline_tag: image-classification tags: - image-classification - flowers - oxford-102 - torchvision - vit - transfer-learning metrics: - accuracy - f1 model-index: - name: ViT-B/16 Flower Classifier results: - task: type: image-classification dataset: name: Oxford-102 Flowers type: oxford-102-flowers split: validation metrics: - type: accuracy value: 0.9796 - type: f1 value: 0.9658 - task: type: image-classification dataset: name: Oxford-102 Flowers type: oxford-102-flowers split: test metrics: - type: accuracy value: 0.9796 - type: f1 value: 0.9637 --- # ViT-B/16 Flower Classifier Fine-tuned [`torchvision.models.vit_b_16`](https://docs.pytorch.org/vision/main/models/vision_transformer.html) (ImageNet-1K pretrained) for 102-class flower classification on the Oxford-102 Flowers dataset, with the full backbone unfrozen during fine-tuning. Achieves **0.9796 accuracy / 0.9637 macro-F1** on a held-out test split. > **Note:** earlier versions of this card reported 1.0 accuracy / 1.0 F1. Those numbers were produced by a data leak in the training pipeline's split logic and have been corrected — see [Metrics correction](#metrics-correction--earlier-scores-were-invalid) below. The weights are unaffected; only the measurement was. **Recommended when** accuracy is the priority and the extra size/latency budget is acceptable — e.g. offline batch labeling, research baselines, or any deployment where a ~344MB model and ~87ms mean inference isn't a constraint. For latency- or memory-constrained serving, see [EfficientNetV2-S Flower Classifier](efficientnetv2-s-readme.md), which gives up ~2.7pp of test F1 for ~4x less size and ~3x less latency. ## Usage ```python import torch from huggingface_hub import hf_hub_download from safetensors.torch import load_file from torchvision import models weights_path = hf_hub_download(repo_id="bengid/vit-flower-classifier", filename="vit-flower-classifier.safetensors") model = models.vit_b_16(weights=None) model.heads[-1] = torch.nn.Linear(model.heads[-1].in_features, 102) model.load_state_dict(load_file(weights_path, device="cpu")) model.eval() # preprocessing: resize(256) -> center-crop(224) -> normalize with dataset mean/std # see src/utils.py:get_transforms() in the training repo for the exact pipeline #(https://github.com/ben-gid/flowers/blob/main/src/uti...

Source context: 0 downloads · 0 likes · Pipeline image-classification · Library pytorch · Repo bengid/vit-flower-classifier