FoodVision Mini
A small, fast image classifier that tells whether a food photo shows pizza, steak or sushi. It uses transfer learning: torchvision's EfficientNet-B2, pretrained on ImageNet, is kept frozen as a feature extractor and only a new 3-class head is trained, on 450 Food-101 photos.
Model
| Architecture | torchvision efficientnet_b2 (backbone initialised from EfficientNet_B2_Weights.IMAGENET1K_V1), classifier replaced by Dropout(0.3) -> Linear(1408, 3) |
| Parameters | 7,705,221 total, of which 4,227 (the head) were trained |
| Input | an RGB image of any size |
| Preprocessing | model.get_transform(): resize to 288 (bicubic), center-crop 288x288, ImageNet mean/std |
| Output | softmax probabilities for all 3 classes: {"pizza": p, "steak": p, "sushi": p} |
| Files | model.safetensors (weights), config.json (class_names, dropout, pretrained), model.py (architecture + load() + Predictor), handler.py + requirements.txt (Inference Endpoint), metrics.json, assets/ (experiment graphs) |
config.json records pretrained: true because the backbone was initialised from ImageNet
weights before training. model.load() always rebuilds the network with pretrained=False
and then loads model.safetensors, so inference never downloads the torchvision weights.
Usage
Python
from huggingface_hub import hf_hub_download, snapshot_download
import sys
path = snapshot_download("shalev396/foodvision-mini")
sys.path.insert(0, path)
import model
predictor = model.load(path, device="cpu") # or "cuda"
image = hf_hub_download("shalev396/foodvision-mini", "examples/2582289.jpg", repo_type="space")
print(predictor.predict(image)) # a PIL image works too
# {'pizza': 0.964, 'steak': 0.017, 'sushi': 0.019}
Requirements (requirements.txt): torch, torchvision, pillow, huggingface_hub, safetensors.
Space API (free): the Space exposes
/predict (image in, [label, seconds, device] out). curl and @gradio/client examples are in its README.
Inference Endpoint: deploy this repo from its page (Deploy -> Inference Endpoints, CPU or GPU).
handler.py loads the model once (on cuda when the endpoint has a GPU) and accepts an image sent as
raw bytes (Content-Type: image/jpeg) or as JSON {"inputs": "<base64 image>"}. It returns
{"pizza": p, "steak": p, "sushi": p}.
curl $ENDPOINT_URL -H "Authorization: Bearer $HF_TOKEN" -H "Content-Type: image/jpeg" --data-binary @pizza.jpg
Training
- Data: the
pizza_steak_sushi_20_percentsubset of Food-101, prepared for the PyTorch Deep Learning course (zip). It holds 450 train images (pizza 154, steak 146, sushi 150) and 150 test images (46 / 58 / 46). - Recipe: EfficientNet-B2 feature extractor. All backbone weights are frozen and only the new head trains.
Loss is plain cross-entropy, optimizer is
Adam(lr=1e-3), batch size 32, 10 epochs, seed 42. Train and test images get the same preprocessing (no data augmentation). BatchNorm layers stay in train mode, so their running statistics adapt to the food photos. The last epoch is kept (no early stopping). - This checkpoint: converted from the original training run
(PyTorch Deep Learning bootcamp, notebook 09, trained on Apple MPS with torch 2.11 and published on 2026-05-08).
The original
.pthstate_dict was mapped 1:1 intoFoodVisionNetand saved as safetensors. On the 3 example images, the converted model's probabilities match the original's exactly (max |diff| 0.0). Training time was not recorded. - Correction: an earlier version of the Space README said the model was trained on 225/75 images with label smoothing and TrivialAugmentWide. That was wrong: the notebook that produced these weights used the 20% split (450/150), plain cross-entropy and no augmentation.
Full code: training/ · Colab. The notebook retrains the recipe, evaluates the new run next to this checkpoint, and exports whichever is better (the deployed one wins ties).
Experiments
Every variant evaluated on the same 150 test images (from metrics.json -> comparison). The deployed
one is in bold. accuracy_<class> is the share of that class's photos classified correctly.
| variant | accuracy | f1_macro | accuracy_pizza | accuracy_steak | accuracy_sushi |
|---|---|---|---|---|---|
| original bootcamp checkpoint (deployed) | 0.960 | 0.961 | 0.957 | 0.948 | 0.978 |
| retrain with training/ on cpu, 2026-09-25 | 0.940 | 0.941 | 0.978 | 0.983 | 0.848 |
- original bootcamp checkpoint: the weights in this repo (see Training).
- retrain with training/: the same recipe run by the notebook on a desktop CPU on 2026-09-25 (857 s of training, on a CPU shared with other jobs). An earlier retrain on 2026-09-24 (520 s) gave exactly the same 0.940, so the CPU run is reproducible. It lands 3 images lower (141/150), which is within the noise of a 150-image test set, so the original weights stay deployed. The two make different mistakes: the retrain misses more sushi (7 of 46), the original misses more steak (3 of 58).
Training curves of the retrain (the original run's curves were not recorded). The loss is still falling at epoch 10. Test accuracy touches 0.960 at epoch 5 and ends at 0.940. With 150 test images, one epoch's swing is 1-3 images, and the recipe does not pick epochs on the test split.
Evaluation
| metric (test) | value |
|---|---|
| accuracy | 0.9600 |
| f1_macro | 0.9606 |
| accuracy_pizza | 0.9565 |
| accuracy_steak | 0.9483 |
| accuracy_sushi | 0.9783 |
The deployed checkpoint on the 150 test images, evaluated locally on CPU with this repo's training/src code.
144 of 150 test images are correct. 3 of the 6 errors are steak photos predicted as sushi. The bootcamp notebook printed 96.25% for this model. That figure is a mean of per-batch accuracies (the last batch holds only 22 images). The per-image accuracy on the same 150 images is 96.0%, and that is what is reported here.
Limitations
- Three classes only. Every image gets a pizza/steak/sushi label, even a photo of a dog. There is no "other" class or rejection threshold.
- Small data. 450 training images and 150 test images. The accuracy estimate has roughly a +/-1.6 percentage point standard error.
- No separate validation split. The recipe monitors the test split every epoch but keeps the last epoch. The choice between variants is also made on the test split (two candidates, so the optimism is small but not zero).
- Domain. Food-101 photos are user-uploaded restaurant/home photos with some label noise. Performance on other styles (packaged food, drawings, unusual cuisines) is unknown.
- Downloads last month
- -
Dataset used to train shalev396/foodvision-mini
Space using shalev396/foodvision-mini 1
Evaluation results
- accuracy on Food-101 pizza/steak/sushi 20% subsettest set self-reported0.960
- f1_macro on Food-101 pizza/steak/sushi 20% subsettest set self-reported0.961
- accuracy_pizza on Food-101 pizza/steak/sushi 20% subsettest set self-reported0.957
- accuracy_steak on Food-101 pizza/steak/sushi 20% subsettest set self-reported0.948
- accuracy_sushi on Food-101 pizza/steak/sushi 20% subsettest set self-reported0.978


