A professional, modular, and extensible implementation of a neural network for classifying handwritten digits from the MNIST dataset. This project demonstrates best practices in machine learning engineering, including proper code organization, testing, logging, and CI/CD integration.
- Features
- Project Structure
- Installation
- Quick Start
- Usage
- API Reference
- Development
- Testing
- Results
- Contributing
- License
- Modular Architecture: Clean separation of concerns with dedicated modules for data loading, model definition, training, evaluation, and visualization
- Configurable: Easily adjust hyperparameters, model architecture, and training settings
- Command-Line Interface: User-friendly CLI for training and prediction
- Model Persistence: Save and load trained models for inference
- Comprehensive Logging: Detailed logging throughout the pipeline for debugging and monitoring
- Multilayer Perceptron (MLP): Fully connected neural network with customizable hidden layers
- Dropout Regularization: Prevent overfitting with configurable dropout rates
- Advanced Callbacks: Model checkpointing, learning rate reduction, early stopping, and CSV logging
- Multiple Optimizers: Support for Adam, SGD, and other Keras optimizers
- Rich Metrics: Accuracy, loss, confusion matrix, per-class accuracy, top-k accuracy
- Comprehensive Visualizations: Training curves, confusion matrices, sample predictions, misclassified samples
- Classification Reports: Detailed per-class precision, recall, and F1-scores
- Automated Reporting: Generate summary reports with all metrics and configurations
- Type Hints: Full type annotations for better IDE support and code quality
- Docstrings: Comprehensive documentation for all classes and methods
- Unit Tests: Extensive test suite with pytest
- CI/CD: Automated testing and linting with GitHub Actions
- PEP 8 Compliant: Clean, readable code following Python standards
Handwritten-Digit-Classification/
├── .github/
│ └── workflows/
│ └── ci.yml # GitHub Actions CI/CD pipeline
├── src/
│ └── mnist_classifier/
│ ├── __init__.py # Package initialization
│ ├── config.py # Configuration management
│ ├── data_loader.py # Data loading and preprocessing
│ ├── model.py # Neural network model definition
│ ├── trainer.py # Training logic and callbacks
│ ├── evaluator.py # Model evaluation and metrics
│ ├── visualization.py # Plotting and visualization utilities
│ └── cli.py # Command-line interface
├── tests/
│ ├── __init__.py
│ ├── test_config.py # Configuration tests
│ ├── test_data_loader.py # Data loading tests
│ ├── test_model.py # Model tests
│ └── test_trainer.py # Training tests
├── models/ # Saved models (gitignored)
├── results/
│ ├── plots/ # Generated visualizations
│ └── logs/ # Training logs and history
├── data/ # Dataset cache (gitignored)
├── train.py # Main training script
├── predict.py # Prediction script
├── requirements.txt # Production dependencies
├── requirements-dev.txt # Development dependencies
├── setup.py # Package installation script
├── pytest.ini # Pytest configuration
├── .gitignore # Git ignore rules
├── LICENSE # MIT License
└── README.md # This file
- Python 3.8 or higher
- pip package manager
-
Clone the repository:
git clone https://github.com/pyenthusiasts/Handwritten-Digit-Classification.git cd Handwritten-Digit-Classification -
Install dependencies:
pip install -r requirements.txt
For development with testing and code quality tools:
pip install -r requirements-dev.txtInstall as a Python package:
pip install -e .This enables you to use the package from anywhere and provides command-line entry points.
python train.pyThis will:
- Load and preprocess the MNIST dataset
- Build a neural network with default architecture (128, 64 hidden units)
- Train for 10 epochs with batch size 128
- Save the trained model to
models/ - Generate visualizations in
results/plots/ - Save training logs to
results/logs/
python predict.py --model-path models/best_model.h5 --num-samples 10python train.pypython train.py \
--epochs 20 \
--batch-size 256 \
--learning-rate 0.001 \
--hidden-units 256 128 64 \
--dropout-rate 0.3python train.py --early-stoppingpython train.py --verbosepython predict.py --model-path models/best_model.h5 --num-samples 20python predict.py \
--model-path models/best_model.h5 \
--show-misclassified \
--num-samples 10python predict.py \
--model-path models/best_model.h5 \
--save-predictions \
--output-dir results/predictions| Option | Description | Default |
|---|---|---|
--epochs |
Number of training epochs | 10 |
--batch-size |
Batch size for training | 128 |
--learning-rate |
Learning rate for optimizer | 0.001 |
--validation-split |
Fraction of data for validation | 0.2 |
--hidden-units |
Hidden layer sizes (space-separated) | 128 64 |
--dropout-rate |
Dropout rate for regularization | 0.2 |
--early-stopping |
Enable early stopping | False |
--no-callbacks |
Disable training callbacks | False |
--no-plots |
Disable plot generation | False |
--model-dir |
Directory to save models | models |
--results-dir |
Directory to save results | results |
--seed |
Random seed for reproducibility | 42 |
--verbose |
Enable verbose output | False |
| Option | Description | Default |
|---|---|---|
--model-path |
Path to trained model (required) | - |
--num-samples |
Number of samples to predict | 10 |
--show-misclassified |
Show only misclassified samples | False |
--save-predictions |
Save prediction visualizations | False |
--output-dir |
Directory to save outputs | results/predictions |
--verbose |
Enable verbose output | False |
from mnist_classifier import (
Config,
MNISTDataLoader,
MNISTModel,
ModelTrainer,
ModelEvaluator,
Visualizer,
)
# Create configuration
config = Config(
epochs=15,
batch_size=256,
hidden_units=(256, 128, 64),
dropout_rate=0.3
)
# Load data
data_loader = MNISTDataLoader(normalize=True, categorical=True)
(X_train, y_train), (X_test, y_test) = data_loader.load_data()
# Build and compile model
model = MNISTModel(config)
model.build()
model.compile()
# Train model
trainer = ModelTrainer(model, config)
history = trainer.train(X_train, y_train)
# Evaluate model
evaluator = ModelEvaluator(model)
metrics = evaluator.evaluate(X_test, y_test)
# Visualize results
visualizer = Visualizer(config)
visualizer.plot_training_history(history)
# Save model
model.save("my_model.h5")Configuration dataclass for all hyperparameters and settings.
Handles loading and preprocessing the MNIST dataset.
Methods:
load_data(): Load and preprocess dataget_data_shapes(): Get information about data dimensions
Neural network model wrapper.
Methods:
build(): Build the neural network architecturecompile(): Compile the model with optimizer and losssave(filepath): Save model to diskload(filepath): Load model from disksummary(): Print model architecturecount_parameters(): Get parameter counts
Handles model training with callbacks and history tracking.
Methods:
train(X_train, y_train): Train the modelget_history(): Get training historyget_final_metrics(): Get final epoch metrics
Model evaluation and metrics computation.
Methods:
evaluate(X_test, y_test): Evaluate on test setpredict(X): Generate predictionspredict_classes(X): Predict class labelsget_confusion_matrix(X_test, y_test): Compute confusion matrixget_classification_report(X_test, y_test): Generate detailed reportget_misclassified_samples(X_test, y_test): Find misclassified examplesget_top_k_accuracy(X_test, y_test, k): Compute top-k accuracyget_per_class_accuracy(X_test, y_test): Compute per-class accuracy
Visualization utilities for plots and reports.
Methods:
plot_training_history(history): Plot training curvesplot_confusion_matrix(cm): Plot confusion matrix heatmapplot_sample_predictions(images, true, pred): Plot sample predictionsplot_per_class_accuracy(per_class_acc): Plot per-class accuracy barsplot_misclassified_samples(images, true, pred): Plot misclassified samplescreate_summary_report(metrics): Generate text summary report
# Clone the repository
git clone https://github.com/pyenthusiasts/Handwritten-Digit-Classification.git
cd Handwritten-Digit-Classification
# Install development dependencies
pip install -r requirements-dev.txt
# Install package in editable mode
pip install -e .black src/ tests/ train.py predict.pyisort src/ tests/ train.py predict.pyflake8 src/ tests/ train.py predict.py --max-line-length=120mypy src/pytestpytest --cov=src/mnist_classifier --cov-report=htmlpytest tests/test_model.py -vpytest -n autoWith default settings, the model typically achieves:
- Test Accuracy: ~98%
- Training Time: ~2-3 minutes on CPU
- Parameters: ~100K trainable parameters
After training, you'll find:
Models:
models/best_model.h5- Best model based on validation accuracymodels/final_model.h5- Model from final epoch
Visualizations:
results/plots/training_history.png- Training/validation curvesresults/plots/confusion_matrix.png- Confusion matrix heatmapresults/plots/per_class_accuracy.png- Per-class accuracy barsresults/plots/sample_predictions.png- Sample predictionsresults/plots/misclassified_samples.png- Misclassified examples
Logs:
results/logs/training_YYYYMMDD_HHMMSS.csv- Training metrics per epochresults/logs/history_YYYYMMDD_HHMMSS.json- Complete training historyresults/summary_report.txt- Summary report with all metrics
Contributions are welcome! Here's how you can help:
-
Fork the repository
# Click the 'Fork' button on GitHub -
Clone your fork
git clone https://github.com/your-username/Handwritten-Digit-Classification.git cd Handwritten-Digit-Classification -
Create a feature branch
git checkout -b feature/your-feature-name
-
Make your changes
- Write clean, documented code
- Add tests for new functionality
- Ensure all tests pass:
pytest - Format code:
black .andisort . - Lint code:
flake8 .
-
Commit your changes
git add . git commit -m "Add: description of your changes"
-
Push to your fork
git push origin feature/your-feature-name
-
Create a Pull Request
- Go to the original repository on GitHub
- Click "New Pull Request"
- Select your feature branch
- Describe your changes
- Follow PEP 8 style guidelines
- Add type hints to all functions
- Write docstrings for all public methods
- Maintain test coverage above 80%
- Update documentation for new features
This project is licensed under the MIT License - see the LICENSE file for details.
- MNIST Dataset: Yann LeCun, Corinna Cortes, and Christopher J.C. Burges
- TensorFlow/Keras: Google Brain Team
- Community: Thanks to all contributors and users
If you use this project in your research or work, please cite:
@software{mnist_digit_classification,
title = {MNIST Digit Classification with Neural Network},
author = {MNIST Classifier Team},
year = {2024},
url = {https://github.com/pyenthusiasts/Handwritten-Digit-Classification}
}- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with passion for clean code and machine learning