--- base_model: torchvision/efficientnet_v2_s (IMAGENET1K_V1) license: apache-2.0 library_name: pytorch pipeline_tag: image-classification tags: - image-classification - flowers - oxford-102 - torchvision - efficientnet - transfer-learning metrics: - accuracy - f1 model-index: - name: EfficientNetV2-S Flower Classifier results: - task: type: image-classification dataset: name: Oxford-102 Flowers type: oxford-102-flowers split: validation metrics: - type: accuracy value: 0.9996511340141296 - type: f1 value: 0.9994719624519348 --- # EfficientNetV2-S Flower Classifier Fine-tuned [`torchvision.models.efficientnet_v2_s`](https://docs.pytorch.org/vision/main/models/efficientnetv2.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.9997 accuracy / 0.9995 F1** on the validation split. **Recommended when** you need near-best accuracy at a fraction of the size and latency — the default choice for serving. At ~82MB and ~30ms mean latency it is ~4x smaller and ~3x faster than [ViT-B/16 Flower Classifier](vit-b16-readme.md) for a ~0.05 percentage-point accuracy difference. ## Usage ```python import torch from huggingface_hub import hf_hub_download from torchvision import models weights_path = hf_hub_download(repo_id="bengid/flower-classifier", filename="ft_EfficientNetV2-S.pth") model = models.efficientnet_v2_s(weights=None) model.classifier[1] = torch.nn.Linear(model.classifier[1].in_features, 102) model.load_state_dict(torch.load(weights_path, map_location="cpu", weights_only=True)) 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 ``` ## Training Data [Oxford-102 Flowers](https://www.robots.ox.ac.uk/~vgg/data/flowers/102/) — 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 EfficientNetV2 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*. ### Hyperparameters | Parameter | Value | |---|---| | Optimizer | AdamW | | LR scheduler | Cosine annealing (T_max=30, eta_min=1e-06) | | Head LR (before unfreeze) | 1e-3 | | Head LR (after unfreeze) | 1e-3 | | Backbone LR (after unfreeze) | 1e-5 | | Unfreeze epoch | 5 | | Max epochs | 30 | | Batch size | 32 | | Effective batch size | 32 | | Gradient accumulation | 1 | | Precision | 16-mixed | | Weight decay | 0.01 | | Early stopping patience | 5 | ## Evaluation | Metric | Value | |---|---| | **Accuracy** | **0.9997** | | **F1** | **0.9995** | | Parameters | 20,308,150 | | Model size | 81.8 MB | | Checkpoint size | 245.0 MB | | Mean latency | 29.6 ms | | p95 latency | 30.9 ms | Latency measured on {} at batch size 1. ## Strengths & Weaknesses **Strengths:** - Within 0.05pp of ViT-B/16's accuracy and F1 while being ~4x smaller (81.8MB vs 343.5MB) and ~3x faster (29.6ms vs 86.5ms mean latency) — the best accuracy-per-cost tradeoff of the four models trained. - Convolutional inductive bias generalizes well from a modest fine-tuning dataset (~8k images), needing less data than attention-based architectures to reach its ceiling. - Small enough to fit comfortably in the Docker image and serve cheaply (this is the architecture family the v2 API's original `helpers.py` defaulted to). **Weaknesses:** - Marginally lower accuracy/F1 than ViT-B/16, though the gap is negligible in practice (both are within noise of a perfect validation score). - EfficientNetV2's published training recipe (progressive resizing, adaptive regularization) is more sensitive to schedule/hyperparameter choices than a straightforward ViT fine-tune; deviating far from a tuned recipe can cost more accuracy than it would for ViT. - Like the ViT model, still requires unfreezing the *entire* backbone to reach these numbers — the smaller, cheaper EfficientNet-B0 v1 model that only partially unfroze its backbone topped out well below 93% for this reason. ## 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 center-cropped, normalized input (resize-then-crop). Unusual aspect ratios or off-center subjects can crop the flower out of frame. - **No adversarial robustness or calibration guarantees** — confidence scores are not calibrated probabilities. - Reported metrics are on the Oxford-102 validation split; real-world images (different lighting, backgrounds, camera quality) may perform worse. ## Intended Use **Intended uses:** - Flower species identification within the 102 Oxford-102 classes (gardening/botany apps, educational tools, dataset labeling). - Default backend model for this project's v2 `/classify` API endpoint — chosen for its accuracy/latency/size balance. **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 This project trained four models in total, in this order: | Model | Val Acc | Val F1 | Params | Size (MB) | Best For | |---|---|---|---|---|---| | [SimpleCNN (scratch)](https://huggingface.co/bengid/flower-classifier/blob/main/flower_model_weights.pth) | ~0.63 | {} | {} | {} | historical baseline only | | [EfficientNet-B0 (v1, partial unfreeze)](https://huggingface.co/bengid/flower-classifier/blob/main/ft_EfficientNet-B0.pth) | >0.93 | {} | {} | {} | historical baseline only | | **EfficientNetV2-S (this model)** | **0.9997** | **0.9995** | 20,308,150 | 81.8 | efficient production serving | | [ViT-B/16](https://huggingface.co/bengid/flower-classifier/blob/main/ft_ViT-B16.pth) | 1.0 | 1.0 | 85,877,094 | 343.5 | maximum accuracy | ### 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`, "Fine-Tuning EfficientNet-B0" section, for the original two-stage recipe) — the earlier backbone layers, tuned for general ImageNet features, never adapted to flower-specific low/mid-level features, capping accuracy well below the fully-unfrozen v2 models. - Both **EfficientNetV2-S** (this model) and **ViT-B/16** unfreeze the *entire* backbone during fine-tuning, which is the main driver of the jump from ~93% to ~99.97-100% accuracy. ## License Apache 2.0, consistent with this project's license. ## Citation **Base model (EfficientNetV2):** ```bibtex @inproceedings{tan2021efficientnetv2, title={EfficientNetV2: Smaller Models and Faster Training}, author={Tan, Mingxing and Le, Quoc V}, booktitle={International Conference on Machine Learning}, year={2021} } ``` **Training dataset:** ```bibtex @inproceedings{nilsback2008automated, title={Automated flower classification over a large number of classes}, author={Nilsback, Maria-Elena and Zisserman, Andrew}, booktitle={2008 Sixth Indian Conference on Computer Vision, Graphics \& Image Processing}, pages={722--729}, year={2008}, organization={IEEE} } ```