EfficientNet Food Classifier
A fine-tuned EfficientNet-B0 model for classifying food images into 8 categories. Trained using a two-stage transfer learning approach with ImageNet pre-trained weights.
Model Description
- Architecture: EfficientNet-B0 + custom classification head
- Task: Image Classification (8 food categories)
- Framework: TensorFlow / Keras
- Input: 224ร224 RGB images
- Pre-training: ImageNet
Classes
| ID |
Label |
| 0 |
Baked Potato |
| 1 |
Burger |
| 2 |
Crispy Chicken |
| 3 |
Donut |
| 4 |
Fries |
| 5 |
Hot Dog |
| 6 |
Pizza |
| 7 |
Sandwich |
Training
Two-Stage Transfer Learning
- Stage 1 โ Feature extraction: Backbone frozen, only classification head trained (LR: 1e-3)
- Stage 2 โ Fine-tuning: Backbone unfrozen (BatchNorm frozen), full model trained (LR: 2e-5)
Training Configuration
| Parameter |
Value |
| Base Model |
EfficientNet-B0 (ImageNet) |
| Image Size |
224 ร 224 |
| Batch Size |
8 |
| Stage 1 Epochs |
12 (EarlyStopping patience=3) |
| Stage 2 Epochs |
6 |
| Optimizer |
Adam |
| Loss |
Sparse Categorical Crossentropy |
| Dropout |
0.2 |
| Data Augmentation |
RandomFlip, RandomRotation(0.05), RandomZoom(0.1), RandomContrast(0.1) |
Dataset
| Split |
Images |
| Train |
3,797 |
| Validation |
813 |
| Test |
814 |
| Total |
5,424 |
- Stratified 70/15/15 split with leak-free guarantee (SHA256 dedup)
- Images sourced via DuckDuckGo image search, manually cleaned
Evaluation
Overall Metrics (Test Set)
| Metric |
Score |
| Accuracy |
0.9328 |
| Precision (weighted) |
0.9345 |
| Recall (weighted) |
0.9328 |
| F1 (weighted) |
0.9331 |
| F1 (macro) |
0.9301 |
Per-Class Performance
| Class |
Precision |
Recall |
F1-score |
Support |
| Baked Potato |
0.907 |
0.898 |
0.903 |
98 |
| Burger |
0.934 |
0.904 |
0.919 |
94 |
| Crispy Chicken |
0.860 |
0.968 |
0.911 |
95 |
| Donut |
0.986 |
0.958 |
0.971 |
142 |
| Fries |
0.926 |
0.917 |
0.921 |
96 |
| Hot Dog |
0.957 |
0.957 |
0.957 |
94 |
| Pizza |
0.971 |
0.918 |
0.944 |
73 |
| Sandwich |
0.906 |
0.923 |
0.914 |
52 |
Available Formats
| Format |
File |
Size |
Use Case |
| Keras |
BestModelEfficientNetLite.keras |
16 MB |
Python / TensorFlow |
| TFLite |
tflite/model.tflite |
4.4 MB |
Mobile / Edge (dynamic range quantized) |
| TFLite float16 |
tflite/model_float16.tflite |
7.8 MB |
Mobile / Edge (float16 quantized) |
| TFJS |
tfjs/model.json |
15 MB |
Browser / Node.js |
Usage
Python (Keras)
import tensorflow as tf
import numpy as np
from PIL import Image
model = tf.keras.models.load_model(
"BestModelEfficientNetLite.keras",
custom_objects={"preprocess_input": tf.keras.applications.efficientnet.preprocess_input},
)
img = Image.open("food.jpg").resize((224, 224))
x = np.expand_dims(np.array(img), axis=0).astype("float32")
probs = model.predict(x)[0]
classes = ["Baked Potato", "Burger", "Crispy Chicken", "Donut", "Fries", "Hot Dog", "Pizza", "Sandwich"]
print(f"Predicted: {classes[np.argmax(probs)]} ({probs.max():.1%})")
TFLite (Python)
import numpy as np
from PIL import Image
import tflite_runtime.interpreter as tflite
interpreter = tflite.Interpreter(model_path="tflite/model.tflite")
interpreter.allocate_tensors()
img = np.array(Image.open("food.jpg").resize((224, 224)), dtype=np.float32)
img = np.expand_dims(img, axis=0)
interpreter.set_tensor(interpreter.get_input_details()[0]['index'], img)
interpreter.invoke()
output = interpreter.get_tensor(interpreter.get_output_details()[0]['index'])
TFJS (JavaScript)
import * as tf from '@tensorflow/tfjs';
const model = await tf.loadGraphModel('tfjs/model.json');
const img = tf.browser.fromPixels(imageElement).resizeBilinear([224, 224]).expandDims(0).toFloat();
const predictions = model.predict(img);
const classIndex = predictions.argMax(-1).dataSync()[0];
Limitations
- Trained on web-scraped images; may not generalize well to all food photography styles
- Limited to 8 food categories
- Best performance on clearly visible, single-item food images
License
Apache 2.0