An educational project that teaches how to build a machine learning web application using Python, FastAPI, and scikit-learn. Learn to train a model on the MNIST dataset, create a REST API, and deploy to the cloud.
-
Handwritten Digit Recognition: Recognizes digits 0-9 from uploaded images.
-
Confidence Scores: Shows prediction confidence percentage.
-
Top 5 Predictions: Displays the top 5 most likely digits with probabilities.
-
Image Preview: Shows uploaded image before classification.
-
Modern UI: Clean, responsive design that works on mobile and desktop.
-
REST API: Full REST API for programmatic access.
-
Cloud Deployment: Deployable to Render for free tier public access.
-
Input Validation: Validates file type and size.
-
Error Handling: User-friendly error messages.
Try it out locally or deploy to the cloud. The application provides:
-
A web interface for uploading digit images.
-
Real-time preview of uploaded images.
-
Classification results with confidence scores.
-
A REST API that can be called from any system.
Get up and running quickly:
# 1. Clone the repository
git clone https://github.com/HelixCipher/digit-classifier-api.git
cd digit-classifier-api
# 2. Install dependencies
pip install -r requirements.txt
# 3. Run the server (pre-trained model included)
uvicorn main:app --reload --port 3000
# 4. Open your browser
# Navigate to http://localhost:3000To enable the auto-ping workflow that keeps Render from spinning down:
- Go to your GitHub repo → Settings → Secrets and variables → Actions
- Add a new secret:
- Name:
RENDER_HEALTHCHECK_URL - Value:
https://your-app.onrender.com/health
- Name:
This triggers the workflow in .github/workflows/ping-render.yml to ping your Render app every 10 minutes.
Before you begin, ensure you have the following:
-
Python 3.8+ - Download from python.org
-
Git - For version control
-
GitHub Account - For deployment (optional)
-
Render Account - For cloud deployment (optional, free tier)
git clone https://github.com/HelixCipher/digit-classifier-api.git
cd digit-classifier-api# On Windows
python -m venv venv
venv\Scripts\activate
# On macOS/Linux
python3 -m venv venv
source venv/bin/activatepip install -r requirements.txtThe project already includes a pre-trained model (model.zlib), but here's how to train your own:
python train_model.pyThis will:
-
Load the MNIST dataset (60,000 training images, 10,000 test images).
-
Train a Random Forest classifier.
-
Evaluate accuracy on the test set.
-
Save the model as
model.pkl(original) andmodel.zlib(compressed).
Open the notebook for a complete step-by-step guide:
jupyter notebook digit_classifier.ipynbThe notebook includes:
-
Detailed explanations of each step
-
Data visualization
-
Model training and evaluation
-
Model compression techniques
uvicorn main:app --reload --port 3000Parameters explained:
-
main:app- The FastAPI application object in main.py -
--reload- Auto-restart on code changes (useful for development) -
--port 3000- Run on port 3000
Open your browser and navigate to:
-
http://localhost:3000 - Main web interface
-
http://localhost:3000/docs - Auto-generated API documentation
-
http://localhost:3000/predict-image/ - Prediction endpoint
-
Open the web interface at http://localhost:3000
-
Click "Choose Image" to select a handwritten digit image
-
Preview your selected image
-
Click "Classify" to get predictions
-
View results including:
-
The predicted digit
-
Confidence percentage
-
Top 5 predictions with probabilities
-
For best results, your image should:
-
Be a clear image of a handwritten digit (0-9)
-
Be in JPEG, PNG, GIF, or WebP format
-
Be under 5MB in size
-
Show the digit clearly on a contrasting background
GET /
Returns the HTML interface.
POST /predict-image/
Upload an image file to get digit prediction.
Parameters:
file(required): Image file (JPEG, PNG, GIF, or WebP)
Response:
{
"prediction": 5,
"confidence": 94.32,
"top_predictions": [
{"digit": 5, "confidence": 94.32},
{"digit": 3, "confidence": 3.21},
{"digit": 8, "confidence": 1.45},
{"digit": 9, "confidence": 0.67},
{"digit": 0, "confidence": 0.35}
]
}Error Responses:
-
400: Invalid file type or file too large
-
500: Server error during processing
Render offers free tier web hosting. Follow these steps:
Ensure you have:
-
main.py- FastAPI application -
index.html- Frontend interface -
model.zlib- Compressed model file -
requirements.txt- Python dependencies -
.gitignore- Git ignore patterns
git add .
git commit -m "Initial commit: Digit Classifier API"
git remote add origin https://github.com/yourusername/digit-classifier-api.git
git push -u origin main-
Create a Render account at render.com (sign up with GitHub)
-
Create a new Web Service:
-
Click "New +" → "Web Service"
-
Connect your GitHub repository
-
Select the branch to deploy (usually
main)
-
-
Configure the service:
-
Name:
digit-classifier(or your preferred name) -
Environment:
Python -
Build Command:
pip install -r requirements.txt -
Start Command:
uvicorn main:app --host 0.0.0.0 --port $PORT
-
-
Click "Create Web Service"
-
Wait for deployment (may take several minutes)
-
Your API is live You'll get a URL like:
https://your-app-name.onrender.com
# Using curl
curl -X POST -F "file=@digit.png" https://your-app-name.onrender.com/predict-image/
import requests
url = "https://your-app-name.onrender.com/predict-image/"
# Upload an image
with open('digit.png', 'rb') as f:
response = requests.post(url, files={'file': f})
# Get the prediction
result = response.json()
print(f"Prediction: {result['prediction']}")
print(f"Confidence: {result['confidence']}%")const formData = new FormData();
formData.append('file', fileInput.files[0]);
fetch('https://your-app-name.onrender.com/predict-image/', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log(`Prediction: ${data.prediction}`);
console.log(`Confidence: ${data.confidence}%`);
});# Single prediction
curl -X POST -F "file=@my_digit.png" https://your-app-name.onrender.com/predict-image/
# Get JSON response
curl -X POST -F "file=@my_digit.png" https://your-app-name.onrender.com/predict-image/ | jq .You can call this API from:
-
iOS (Swift): Use URLSession
-
Android (Kotlin): Use Retrofit or OkHttp
-
React Native: Use fetch or axios
-
Flutter: Use http package
This project uses joblib with zlib compression to reduce the model file size, making it small enough to deploy to GitHub and cloud platforms.
-
Original pickle file: ~115 MB (exceeds GitHub's 100MB limit)
-
Compressed with joblib+zlib: ~18 MB
-
Compressed with Gzip: ~16 MB
-
~84-86% reduction in file size
| Method | Size | Reduction | Load Time |
|---|---|---|---|
| Original (pickle) | 115 MB | - | Fast |
| Joblib + zlib | 18 MB | 84% | ~0.3s |
| Gzip | 16 MB | 86% | ~0.3s |
| BZ2 | 11 MB | 91% | ~1.4s |
Note: The notebook dynamically calculates the best compression method based on actual size and load time measurements, using a weighted score (60% size importance, 40% load speed). This means the recommendation adapts to your specific results.
Loading a compressed model:
import joblib
# Load compressed model
model = joblib.load('model.zlib')
# Use for prediction
prediction = model.predict(image_array)Creating a compressed model:
import joblib
# Save with compression (zlib at level 3)
joblib.dump(model, 'model.zlib', compress=('zlib', 3))digit-classifier-api/
├── main.py # FastAPI backend application
├── index.html # Frontend web interface
├── train_model.py # Script to train the model
├── model.zlib # Compressed trained model (~18 MB)
├── requirements.txt # Python dependencies
├── digit_classifier.ipynb # Educational Jupyter notebook
├── README.md # This file
├── .gitignore # Git ignore patterns
├── LICENSE # License file (CC BY 4.0)
├── ATTRIBUTION.md # Attribution requirements
└── DISCLAIMER.md # Disclaimer and limitation of liability
-
Python 3.11+ - Programming language
-
FastAPI - Modern web framework
-
scikit-learn - Machine learning library
-
NumPy - Numerical computing
-
Pillow - Image processing
-
joblib - Model compression
-
HTML5 - Markup language
-
CSS3 - Styling (responsive, modern design)
-
JavaScript - Client-side logic
-
Fetch API - API calls
-
Render - Cloud hosting (free tier)
-
GitHub - Version control and repository hosting
-
MNIST Dataset: LeCun et al. - The classic handwritten digit dataset.
-
scikit-learn: For the Random Forest classifier.
-
FastAPI: For the web framework.
-
Render: For free tier cloud hosting.
-
Keras/TensorFlow: For easy MNIST dataset access.
1. Model file not found
FileNotFoundError: [Errno 2] No such file or directory: 'model.zlib'
Solution: Run python train_model.py to train and save the model.
2. Port already in use
ERROR: [Errno 98] Address already in use
Solution: Kill the process using the port or use a different port:
uvicorn main:app --port 30013. Out of memory during training
Solution: Reduce the number of trees in RandomForest:
clf = RandomForestClassifier(n_estimators=50, n_jobs=-1)4. Low prediction accuracy
Solution: Ensure uploaded images are:
-
Clear handwritten digits
-
Properly formatted (not too dark or too light)
-
Show the digit clearly against the background
This project is licensed under the Creative Commons Attribution 4.0 International (CC BY 4.0) license.
You are free to use, share, copy, modify, and redistribute this material for any purpose (including commercial use), provided that proper attribution is given.
Any reuse, redistribution, or derivative work must include:
-
The creator's name:
HelixCipher -
A link to the original repository:
-
An indication of whether changes were made
-
A reference to the license (CC BY 4.0)
This work is based on Digit Classifier API by
HelixCipher.
Original source: https://github.com/HelixCipher/digit-classifier-api
Licensed under the Creative Commons Attribution 4.0 International (CC BY 4.0).
You may place this attribution in a README, documentation, credits section, or other visible location appropriate to the medium.
Full license text: https://creativecommons.org/licenses/by/4.0/
This project is provided "as—is". The author accepts no responsibility for how this material is used. There is no warranty or guarantee that the notebooks are safe, secure, or appropriate for any particular purpose. Use at your own risk.
See DISCLAIMER.md for full terms. Use at your own risk.

