Mahad0007/digit-recognition
<div align="center">
✏️ Digit.AI — Handwritten Digit Recognition
A from-scratch machine learning pipeline that recognises handwritten digits in real time. Built with PCA dimensionality reduction + K-Nearest Neighbours — no deep learning, no black box.
    ![Accuracy]() 
</div>
🧠 What is this?
Digit.AI is a complete end-to-end machine learning system that:
- Trains on 70,000 MNIST handwritten digit images
- Compresses 784 pixel features down to 331 dimensions using PCA (keeping 95% of information)
- Classifies digits 0–9 using K-Nearest Neighbours with K=3
- Serves predictions through a FastAPI backend with a clean draw/upload UI
- Achieves 95.03% test accuracy — with zero neural networks
The goal of this project is to demonstrate that classical ML, done right, is powerful, interpretable, and fast.
📊 Model Performance
Per-class accuracy
🔬 How It Works
User draws/uploads digit
↓
Convert to grayscale
↓
Resize to 28×28
↓
Invert if needed
(white digit on black)
↓
Flatten → 784 values
↓
StandardScaler.transform()
↓
PCA.transform()
784 → 331 dims
↓
KNN.predict()
find 3 nearest neighbours
↓
Return digit + confidenceWhy PCA before KNN?
KNN suffers from the curse of dimensionality — in 784 dimensions, every point looks equally distant from every other point, making nearest-neighbour meaningless. PCA compresses the features while keeping 95% of the variance, making KNN fast and accurate.
Why StandardScaler before PCA?
Without scaling, pixels near the center of the image (which vary more) would dominate the PCA directions. Scaling gives every pixel equal weight so PCA finds truly meaningful components.
🗂️ Project Structure
digit-recognition/
│
├── step1_pca_pipeline.py ← Load MNIST · Scale · Apply PCA · Save artifacts
├── step2_knn_training.py ← Cross-validate K · Train KNN · Evaluate · Save model
├── app.py ← FastAPI server + embedded HTML/CSS/JS UI
│
├── mnist_model/
│ ├── scaler.pkl ← Fitted StandardScaler
│ ├── pca.pkl ← Fitted PCA (784 → 331 dims)
│ └── knn_model.pkl ← Trained KNN (K=3, Euclidean)
│
├── Dockerfile ← Container config for deployment
├── requirements.txt ← Python dependencies
└── README.md ← You are here🚀 Run Locally
Prerequisites
pip install fastapi uvicorn scikit-learn numpy pillow joblib matplotlibStep 1 — Train the model
# Downloads MNIST (~11MB), applies PCA, saves artifacts
python step1_pca_pipeline.py
# Cross-validates K values, trains final KNN, saves model
python step2_knn_training.pyStep 2 — Launch the app
uvicorn app:app --reloadOpen http://127.0.0.1:8000 in your browser.
🐳 Run with Docker
docker build -t digit-recognition .
docker run -p 7860:7860 digit-recognitionOpen http://localhost:7860
🌐 API Reference
GET /
Returns the full web UI (HTML/CSS/JS embedded in Python).
POST /predict
Request body:
{
"image": "data:image/png;base64,..."
}Response:
{
"digit": 7,
"confidence": 100.0,
"probabilities": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0, 0.0, 0.0]
}Interactive API docs available at /docs (Swagger UI).
🛠️ Tech Stack
📁 Training Pipeline Details
step1pcapipeline.py
- Loads MNIST via
fetch_openml - Splits into 60k train / 10k test
- Fits
StandardScaleron training data only (no data leakage) - Fits
PCA(n_components=0.95)— automatically selects 331 components - Saves
scaler.pklandpca.pkl - Generates variance analysis plots
step2knntraining.py
- Loads PCA-transformed data from
mnist_model/ - Runs 5-fold stratified cross-validation for K ∈ {1, 3, 5, 7, 9, 11, 15, 19, 25}
- Selects best K = 3 (CV accuracy = 94.99%)
- Trains final KNN on full training set
- Evaluates on held-out test set (95.03%)
- Saves
knn_model.pkl - Generates confusion matrix and per-class accuracy plots
👤 Author
Mahad Ahmad
- GitHub: @QaziMahadAhmad
- Hugging Face: @Mahad0007
📄 License
MIT License — feel free to use, modify, and distribute.
