Spaces:
Runtime error
Runtime error
| # Smart Plate β Model Development Documentation | |
| Technical documentation of the image-classification model that powers **Smart Plate** | |
| (*Indian Food Classification and Calorie Estimation*). It covers the complete model | |
| development workflow as implemented in | |
| [`notebook/indian-food-image-detection-vit.ipynb`](notebook/indian-food-image-detection-vit.ipynb). | |
| --- | |
| ## Project Overview | |
| **Objective.** Build an image-classification model that identifies an Indian dish from a | |
| single food photograph, so that the dish can be mapped to its calorie / nutritional | |
| profile in the Smart Plate application. | |
| **Problem being solved.** Manually identifying a dish and looking up its nutritional value | |
| is slow and error-prone. This project automates the first half of that task β *recognising | |
| the dish from a photo* β across 80 popular Indian food categories. The predicted label is | |
| later joined with a calorie database in the application layer to estimate nutrition. | |
| The model is built by **fine-tuning a Vision Transformer (ViT)** image classifier. | |
| --- | |
| ## Dataset Information | |
| | Property | Value | | |
| |---|---| | |
| | Source | Kaggle β *Indian Food Images Dataset* (`indian-food-images-dataset`) | | |
| | Path used | `Indian Food Images/Indian Food Images/*/*.jpg` | | |
| | Total samples | **4,000 images** | | |
| | Classes | **80** Indian food categories | | |
| | Samples per class | 50 (balanced) | | |
| | Image format | RGB JPEG, variable resolution | | |
| | Train / test split | **60% / 40%**, stratified by label, shuffled | | |
| | Train samples | 2,400 | | |
| | Test samples | 1,600 (20 per class) | | |
| **Classes (80):** adhirasam, aloo_gobi, aloo_matar, aloo_methi, aloo_shimla_mirch, | |
| aloo_tikki, anarsa, ariselu, bandar_laddu, basundi, bhatura, bhindi_masala, biryani, | |
| boondi, butter_chicken, chak_hao_kheer, cham_cham, chana_masala, chapati, chhena_kheeri, | |
| chicken_razala, chicken_tikka, chicken_tikka_masala, chikki, daal_baati_churma, daal_puri, | |
| dal_makhani, dal_tadka, dharwad_pedha, doodhpak, double_ka_meetha, dum_aloo, gajar_ka_halwa, | |
| gavvalu, ghevar, gulab_jamun, imarti, jalebi, kachori, kadai_paneer, kadhi_pakoda, kajjikaya, | |
| kakinada_khaja, kalakand, karela_bharta, kofta, kuzhi_paniyaram, lassi, ledikeni, litti_chokha, | |
| lyangcha, maach_jhol, makki_di_roti_sarson_da_saag, malapua, misi_roti, misti_doi, modak, | |
| mysore_pak, naan, navrattan_korma, palak_paneer, paneer_butter_masala, phirni, pithe, poha, | |
| poornalu, pootharekulu, qubani_ka_meetha, rabri, ras_malai, rasgulla, sandesh, shankarpali, | |
| sheer_korma, sheera, shrikhand, sohan_halwa, sohan_papdi, sutar_feni, unni_appam. | |
| **Preprocessing performed at load time:** | |
| - `ImageFile.LOAD_TRUNCATED_IMAGES = True` to tolerate truncated/corrupted JPEGs. | |
| - File paths and parent-folder names collected into a Pandas DataFrame (`image`, `label`). | |
| - **Random oversampling** (`imblearn.RandomOverSampler`, `random_state=83`) applied for class | |
| balancing. The dataset was already balanced (50/class), so the count remained 4,000. | |
| - Conversion to a HuggingFace `Dataset` with the `image` column cast to `Image` and the | |
| `label` column cast to a `ClassLabel` (string β integer id mapping). | |
| --- | |
| ## Tech Stack | |
| **Language:** Python 3 | |
| **Core libraries & frameworks:** | |
| - **PyTorch** + **torchvision** β deep-learning backend and image transforms | |
| - **HuggingFace Transformers** β `ViTForImageClassification`, `ViTImageProcessor`, `Trainer`, `TrainingArguments`, `pipeline` | |
| - **HuggingFace Datasets** β dataset construction, `ClassLabel`, stratified split, on-the-fly transforms | |
| - **HuggingFace Evaluate** β accuracy metric | |
| - **Accelerate** β training backend | |
| - **scikit-learn** β accuracy, F1, confusion matrix, classification report | |
| - **imbalanced-learn (imblearn)** β `RandomOverSampler` | |
| - **pandas / numpy** β data handling | |
| - **matplotlib** β confusion-matrix visualisation | |
| - **Pillow (PIL)** / **tqdm** β image I/O and progress bars | |
| - **huggingface_hub** β model publishing | |
| **Development environment:** Kaggle Notebook with a single CUDA GPU (`device=0`). | |
| --- | |
| ## Methodology | |
| The workflow, in the order performed in the notebook: | |
| ### 1. Data Loading | |
| All `*/*.jpg` files were globbed from the dataset directory; the parent folder name of each | |
| file was used as its class label. Paths and labels were assembled into a DataFrame | |
| (`4000 rows Γ 2 columns`). | |
| ### 2. Data Exploration & Visualization | |
| - Inspected the DataFrame head and the list of 80 unique labels. | |
| - Rendered a sample image to confirm correct loading. | |
| - Built and printed `id2label` / `label2id` mappings. | |
| - After training, a **confusion matrix** (80Γ80) and a per-class **classification report** | |
| were generated for analysis. | |
| ### 3. Data Cleaning & Preprocessing | |
| - Enabled truncated-image loading. | |
| - Oversampled to enforce class balance. | |
| - Cast columns to `Image` and `ClassLabel`; encoded string labels to integer ids. | |
| ### 4. Data Augmentation | |
| Augmentation was applied **on-the-fly to the training split only**, via | |
| `dataset.set_transform`: | |
| | Transform | Train | Validation/Test | | |
| |---|---|---| | |
| | `Resize((224, 224))` | β | β | | |
| | `RandomRotation(90)` | β | β | | |
| | `RandomAdjustSharpness(2)` | β | β | | |
| | `RandomHorizontalFlip(0.5)` | β | β | | |
| | `ToTensor()` | β | β | | |
| | `Normalize(mean, std)` | β | β | | |
| `mean`/`std` are taken from the pretrained `ViTImageProcessor`. | |
| ### 5. Feature Engineering | |
| Not applicable in the classical sense β the Vision Transformer learns visual feature | |
| representations directly from pixels. "Feature engineering" here is limited to the | |
| resize/normalise/augment pipeline above. | |
| ### 6. Model Selection | |
| A **Vision Transformer (ViT)** was selected for image classification. The underlying base | |
| architecture is `google/vit-base-patch16-224-in21k`. In the notebook, training starts from | |
| the task-specific checkpoint `aishrica/indian_food_prediction`, a ViT-Base classifier | |
| already aligned to this exact 80-class Indian-food task. | |
| ### 7. Model Architecture | |
| `ViTForImageClassification` β a ViT-Base encoder (patch size 16, 224Γ224 input) with a | |
| linear classification head re-initialised for `num_labels = 80`. | |
| ### 8. Training Procedure | |
| - A custom `collate_fn` stacks `pixel_values` and `labels` into batches. | |
| - The HuggingFace `Trainer` orchestrates training with `compute_metrics` (accuracy). | |
| - The model was **evaluated before training** (baseline) and **after every epoch**; the best | |
| checkpoint (by accuracy) was reloaded at the end. | |
| - Total optimisation: **760 steps over 20 epochs**, ~31 minutes runtime on GPU. | |
| ### 9. Hyperparameter Configuration | |
| | Hyperparameter | Value | | |
| |---|---| | |
| | Epochs | 20 | | |
| | Learning rate | 1e-6 | | |
| | Train batch size | 64 | | |
| | Eval batch size | 32 | | |
| | Weight decay | 0.02 | | |
| | Warmup steps | 50 | | |
| | Evaluation strategy | per epoch | | |
| | Save strategy | per epoch (`save_total_limit=1`) | | |
| | `load_best_model_at_end` | True (metric = accuracy) | | |
| | `remove_unused_columns` | False | | |
| ### 10. Validation Strategy | |
| A single **stratified 60/40 split** was used. The 40% split serves as the `eval_dataset` | |
| during training (per-epoch evaluation) and as the final test set. Best-model selection is | |
| driven by validation accuracy. *(No separate, fully held-out test set was reserved β see | |
| Limitations.)* | |
| ### 11. Evaluation Metrics | |
| Accuracy (HuggingFace `evaluate`) during training; **accuracy, macro-F1, confusion matrix, | |
| and a full per-class precision/recall/F1 report** (scikit-learn) at the end. | |
| ### 12. Testing & Inference | |
| `trainer.predict` produced final test metrics. A `transformers.pipeline` | |
| (`image-classification`) was then used to run single-image inference, validated against the | |
| ground-truth label. | |
| --- | |
| ## Model Details | |
| | Attribute | Value | | |
| |---|---| | |
| | Base model | `google/vit-base-patch16-224-in21k` | | |
| | Fine-tuning checkpoint | `indian_food_prediction` (published as `aishrica/indian_food_prediction`) | | |
| | Architecture | Vision Transformer β ViT-Base, patch 16, 224Γ224 (`ViTForImageClassification`) | | |
| | Trainable parameters | **β 85.86 M** | | |
| | Weights file | `model.safetensors` (~343 MB) | | |
| | Input | RGB image, resized to 224Γ224, normalised with ViT mean/std β tensor `[3, 224, 224]` | | |
| | Output | Logits over **80 classes** β softmax probabilities; argmax = predicted dish | | |
| --- | |
| ## Results | |
| **Baseline (before fine-tuning):** | |
| - eval_loss: 3.1995 Β· eval_accuracy: **0.7494** | |
| **After fine-tuning (20 epochs):** | |
| - train_loss: 3.1225 (final) | |
| - eval_loss: 3.1539 Β· eval_accuracy: **0.7519** | |
| **Final test evaluation:** | |
| | Metric | Value | | |
| |---|---| | |
| | Test accuracy | **0.7519** | | |
| | Macro F1 | **0.7352** | | |
| | Test loss | 3.1539 | | |
| **Observations & findings:** | |
| - The task-specific checkpoint was already fitted to this dataset, and the learning rate was very | |
| small (1e-6); consequently fine-tuning produced only a marginal gain (**74.94% β 75.19%**). | |
| - Performance is **highly class-dependent.** Strong classes (F1 β 0.90+) include | |
| bhindi_masala, boondi, chak_hao_kheer, jalebi, imarti, makki_di_roti_sarson_da_saag. | |
| Weak classes include `chhena_kheeri` (F1 = 0.00), doodhpak, karela_bharta, ledikeni β | |
| typically dishes that are visually similar to others (e.g. curries and milk-based sweets). | |
| - **Predicted probabilities are poorly calibrated / very flat.** In the inference example the | |
| correct top-1 prediction carried a confidence of only ~0.04, which is consistent with the | |
| high cross-entropy loss (~3.15) despite ~75% top-1 accuracy. Top-1 *ranking* is reliable; | |
| the absolute confidence scores should not be read as probabilities. | |
| --- | |
| ## Inference Pipeline | |
| **How predictions are generated.** A `transformers.pipeline('image-classification')` loads | |
| the saved model and label mappings. An input image is resized/normalised by the ViT | |
| processor, passed through the encoder + classification head, and softmax scores are returned | |
| ranked by probability. | |
| **Input format:** a single image (PIL `Image`, file path, or URL). | |
| **Output format:** a list of `{ "label": <class>, "score": <float> }` objects sorted by | |
| descending score. Example: | |
| ```json | |
| [ | |
| { "label": "butter_chicken", "score": 0.0434 }, | |
| { "label": "chicken_tikka_masala", "score": 0.0335 }, | |
| { "label": "chana_masala", "score": 0.0264 }, | |
| { "label": "chicken_tikka", "score": 0.0256 }, | |
| { "label": "paneer_butter_masala", "score": 0.0226 } | |
| ] | |
| ``` | |
| In the Smart Plate application the top label (`raw_label`) is joined with `calorie_db.json` | |
| to return the dish category and calorie estimate. | |
| --- | |
| ## Project Structure | |
| ``` | |
| indian-food-calories/ | |
| βββ notebook/ | |
| β βββ indian-food-image-detection-vit.ipynb β model development (this document) | |
| βββ models/ | |
| β βββ aishrica_food_predictor/ β bundled ViT weights served by the app | |
| βββ app.py β FastAPI inference backend (/predict, /foods) | |
| βββ calorie_db.json β calorie data for the 80 classes | |
| βββ static/ β Smart Plate web UI (pages/css/js) | |
| βββ requirements.txt | |
| βββ README.md | |
| βββ DOC.md β this file | |
| ``` | |
| > Note: the notebook trains/publishes `indian_food_prediction`; the application serves | |
| > a bundled equivalent ViT model from `models/aishrica_food_predictor/`. Both are ViT-Base | |
| > classifiers over the same 80 Indian-food classes. | |
| --- | |
| ## Reproducibility | |
| **Requirements.** Python 3, a CUDA GPU (recommended), and: | |
| ```bash | |
| pip install -U evaluate "transformers" "datasets>=2.14.5" "accelerate>=0.27" \ | |
| torch torchvision scikit-learn imbalanced-learn pandas numpy matplotlib pillow tqdm | |
| ``` | |
| **Steps to run the notebook:** | |
| 1. Download the *Indian Food Images Dataset* from Kaggle and update the dataset glob path in | |
| the data-loading cell if you are not running on Kaggle. | |
| 2. Open `notebook/indian-food-image-detection-vit.ipynb`. | |
| 3. Run the cells in order: install β imports β data loading β preprocessing/oversampling β | |
| split β transforms β model load β baseline eval β `trainer.train()` β evaluation/report β | |
| `trainer.save_model()`. | |
| 4. (Optional) Run the final cells to log in to the HuggingFace Hub and push the model. | |
| The trained model is saved to the `indian_food_prediction/` output directory and can be | |
| loaded directly for inference. | |
| --- | |
| ## Limitations | |
| - **Marginal fine-tuning effect.** With a near-converged task-specific checkpoint and lr = 1e-6, | |
| training added only ~0.25% accuracy β the run is closer to verification than substantive | |
| fine-tuning. | |
| - **Poorly calibrated confidence.** Softmax scores are very flat (top scores β 0.04), so they | |
| cannot be presented to users as reliable probabilities without temperature scaling. | |
| - **No held-out test set.** The same 40% split is used for per-epoch validation and final | |
| testing, which can optimistically bias the reported metrics. | |
| - **Class confusion.** Visually similar dishes (curries; milk-based sweets) are frequently | |
| confused; at least one class (`chhena_kheeri`) scores F1 = 0. | |
| - **Small dataset.** 50 images/class limits generalisation to real-world photos with varied | |
| lighting, plating, and backgrounds. | |
| - **Single-dish assumption.** The model classifies one dish per image; it does not segment or | |
| detect multiple items on a plate. | |
| --- | |
| ## Future Improvements | |
| - **Reserve a separate test set** (train/validation/test) for unbiased evaluation. | |
| - **Tune the learning rate / schedule** (e.g. 1e-5β5e-5 with discriminative LR) and add early | |
| stopping for meaningful fine-tuning. | |
| - **Calibrate outputs** (temperature scaling or label smoothing) so confidence scores are | |
| trustworthy in the UI. | |
| - **Expand and diversify the dataset** β more images per class and real "in-the-wild" photos; | |
| stronger augmentation (color jitter, random erasing, mixup/cutmix). | |
| - **Address weak classes** with targeted data collection and per-class error analysis. | |
| - **Move toward food segmentation / multi-item detection** (e.g. instance segmentation) | |
| to handle full plates beyond the current single-dish classification setup. | |
| - **Add portion/volume estimation** from images to improve the downstream calorie estimate. | |
| - **Export an optimised model** (ONNX / quantisation) to speed up and shrink the served model. | |
| --- | |
| *Document generated from the notebookβs executed code and outputs. Metrics reflect the recorded | |
| run (20 epochs, test accuracy 0.7519, macro-F1 0.7352).* | |