This project is an end-to-end image classification service built using transfer learning with Xception, trained on the Intel Image Classification dataset, and deployed as a containerized inference API on Fly.io. The service accepts an image file and returns the predicted class along with confidence scores.
- Model: Xception (ImageNet pretrained, frozen base)
- Framework: TensorFlow / Keras
- Deployment: Docker + Gunicorn + Fly.io
- Inference API: Flask
- Input: Image file (
.jpg,.png) - Output: Predicted class + confidence
Intel Image Classification Dataset
Classes:
- buildings
- forest
- glacier
- mountain
- sea
- street
Images are resized to 299 Γ 299, matching Xception's expected input.
The model uses transfer learning with the following architecture:
- Base: Xception (pretrained on ImageNet, frozen)
- Global Average Pooling: Reduces spatial dimensions
- Dense Layer: 100 units with ReLU activation
- Dropout: 0.2 rate for regularization
- Output Layer: 6 units (one per class)
Loss Function:
CategoricalCrossentropy(from_logits=True)
Optimizer:
- Adam with learning rate
1e-3
Training configuration from train.py:
- Input size: 299 Γ 299
- Batch size: 16
- Epochs: 20
- Learning rate: 0.001
- Dropout rate: 0.2
- Inner layer size: 100 units
- Data augmentation: Shear, zoom, horizontal flip
- Validation split: 20%
- Checkpoint: Best model saved based on
val_loss
Model saved as: models/model.keras
Training was done on GPU. Inference runs on CPU.
intel-image-classification/
βββ train.py # Training script
βββ predict.py # Flask inference API
βββ Dockerfile # Container definition
βββ fly.toml # Fly.io configuration
βββ requirements.txt # Python dependencies
βββ notebook.ipynb # Exploratory notebook
βββ .gitignore # Git ignore rules
βββ 30.jpg # Test image
βββ data/
β βββ intel-image-classification/
β βββ seg_train/seg_train/ # Training data
βββ models/
β βββ model.keras # Trained model
βββ README.md # This file
POST /predict
- Content-Type:
multipart/form-data - Field name:
file - Value: Image file (
.jpg,.png)
{
"class": "forest",
"confidence": 0.94
}pip install -r requirements.txtpython predict.pyThe server runs on port 9696.
curl -X POST \
-F "file=@30.jpg" \
http://localhost:9696/predictExpected output:
{
"class": "mountain",
"confidence": 0.97
}docker build -t intel-image-classifier .docker run -p 9696:9696 intel-image-classifiercurl -X POST \
-F "file=@30.jpg" \
http://localhost:9696/predictThe model is deployed on Fly.io and exposed as a REST API.
curl -X POST \
-F "file=@30.jpg" \
https://intel-image-classification.fly.dev/predictKey dependencies:
tensorflow==2.15.0
flask==3.0.0
gunicorn==21.2.0
pillow==10.1.0
numpy==1.26.2
All dependencies are listed in requirements.txt.
To retrain the model:
Download the Intel Image Classification dataset and place it in:
data/intel-image-classification/seg_train/seg_train/
python train.pyTraining features:
- Uses
ModelCheckpointto save best model - Data augmentation (shear, zoom, flip)
- Reproducible (fixed random seed: 42)
- GPU memory growth enabled
- 20% validation split
Output:
- Best model saved to
models/model.keras - Training history logged to console
Screenshot:
- Transfer learning with frozen Xception base dramatically reduces training time
- Correct image preprocessing (
preprocess_input) is critical for accuracy - Data augmentation helps prevent overfitting on small datasets
- Containerization with Docker simplifies deployment and dependency management
- GPU is only needed for training, not inference
- Gunicorn provides production-ready WSGI server
- Fly.io deployment issues are usually DevOps, not ML
- Ensure
models/model.kerasexists - Check Docker daemon is running
- Verify TensorFlow version matches training version
- Check model file path
- Change port in
predict.pyor use different port mapping
This project is for educational purposes as part of ML Zoomcamp.
- Intel Image Classification Dataset
- TensorFlow & Keras
- Fly.io
- ML Zoomcamp