A minimal, production-leaning image classification template using TensorFlow/Keras with a MobileNet backbone and a standard softmax head.
Out of the box it supports folder-based datasets (e.g. cats vs dogs) and saves models in the native .keras format.
- MobileNet (ImageNet weights) + GAP + small MLP + softmax head
- Folder-based datasets via
image_dataset_from_directory - Preprocessing aligned with MobileNet (
preprocess_input) - Reproducible training (seeded, CSV logs)
- Callbacks:
ModelCheckpoint(best),EarlyStopping,CSVLogger - Clean separation:
data.py|model.py|train.py|evaluate.py - Native Keras saving/loading (
.keras) - Ready to extend (TensorBoard/plot_model/finetune)
ββ src/
β ββ data.py # dataset building + preprocessing
β ββ model.py # create_model(...) MobileNet + classifier head
β ββ train.py # training entrypoint (fit, callbacks, save)
β ββ evaluate.py # evaluation entrypoint (load .keras, evaluate)
ββ data/
β ββ train/
β β ββ class_a/ ... images ...
β β ββ class_b/ ...
β ββ val/
β ββ class_a/ ...
β ββ class_b/ ...
ββ models/ # saved final models (*.keras)
ββ checkpoints/ # best checkpoint from ModelCheckpoint
ββ logs/ # CSV logs (and optional TensorBoard)
ββ README.md
ββ requirements.txt
You can have more than two classes; the class list is inferred from the folder names inside
train/andval/.
## ποΈ Data Layout
Place your images like this:
data/
train/
cats/ img001.jpg ...
dogs/ img101.jpg ...
val/
cats/ img201.jpg ...
dogs/ img301.jpg ...- Class names are taken from subfolder names.
- Validation set should not overlap training set.
# Python 3.10+ recommended
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txtpython -m src.evaluate \
--model models/final.keras \
--val_dir data/val \
--batch 32Optional (for model diagram): To use plot_model, also install:
pip install pydot graphvizOn Linux/macOS you may need the Graphviz system package:
Ubuntu/Debian: sudo apt install graphviz
macOS (Homebrew): brew install graphviz
Windows: install Graphviz and add its bin to PATH.
π Quickstart
python -m src.train \
--train_dir data/train \
--val_dir data/val \
--epochs 15 \
--batch 32 \
--lr 1e-3 \
--freeze_base \
--out checkpoints/best.keras \
--save_final models/final.keras--freeze_base (optional) starts with the backbone frozen for stable warm-up. You can later fine-tune by unfreezing and lowering LR (see Tips below). Evaluate
python -m src.evaluate \
--model models/final.keras \
--val_dir data/val \
--batch 32This prints metrics (loss/accuracy) and a few sample predictions.
βοΈ Training Notes
Input size: 224Γ224Γ3 (changed in data.py if you need).
Labels: uses sparse integer labels (label_mode="int") by default.
Loss: sparse_categorical_crossentropy (switch to categorical if you prefer one-hot).
Preprocessing: MobileNet preprocess_input (scales to [-1, 1]).
Checkpoints: best model by val_accuracy β checkpoints/best.keras
Final model: always saved to models/final.keras
Logs: logs/train.csv for curves (you can plot later; add TensorBoard if you like).
π TensorBoard (optional)
Add a TensorBoard callback in src/train.py:
keras.callbacks.TensorBoard(log_dir="logs/tb", histogram_freq=1)Then
tensorboard --logdir logs/tb --port 6006π§ͺ Finetuning Tips
Warm-up: train with --freeze_base for a few epochs (faster convergence).
Unfreeze: modify model.py or add a CLI flag to set base.trainable=True.
Lower LR: e.g. 1e-4 for fine-tuning.
EarlyStopping: already configured; adjust patience as needed.
π§© Inference Snippet
import numpy as np
from tensorflow import keras
from tensorflow.keras.utils import load_img, img_to_array
from tensorflow.keras.applications.mobilenet import preprocess_input
model = keras.models.load_model("models/final.keras")
class_names = ["cats", "dogs"] # or load from training dataset metadata
img = load_img("path/to/image.jpg", target_size=(224, 224))
x = img_to_array(img)[None, ...].astype("float32")
x = preprocess_input(x)
pred = model.predict(x)
print(class_names[np.argmax(pred[0])], float(np.max(pred[0])))π§° Reproducibility
Seeds are set where appropriate; exact determinism may still vary by platform/BLAS/GPU.
For paper-grade reproducibility, pin package versions and record OS/driver details.