File size: 14,394 Bytes
50ba6d4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# 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).*