Day 6 of My End-to-End ML Project
(Web Deployment and Production Serving)
Implementing Flask API for local testing and Streamlit application for cloud deployment
Today marks the completion of the end-to-end machine learning pipeline with the implementation of web interfaces for model serving. I developed two deployment configurations: a Flask application for local development and testing, and a Streamlit application for cloud deployment on Streamlit Cloud. The project now provides a production-ready interface for predicting student math scores based on demographic attributes and reading/writing performance.
flask_app.py: Local Development API
This Flask application implements a web server with two routes: a home page and a prediction endpoint. The application handles both GET and POST requests for the prediction workflow:
from flask import Flask, request, render_template
from src.pipeline.predict_pipeline import PredictPipeline, CustomData
app = Flask(__name__)
@app.route('/')
def index():
return render_template('index.html')
@app.route('/predictdata', methods=['GET', 'POST'])
def predict_datapoint():
if request.method == 'GET':
# Initialize empty form for GET requests
form_data = {
'gender': '',
'ethnicity': '',
'parental_level_of_education': '',
'lunch': '',
'test_preparation_course': '',
'writing_score': '',
'reading_score': ''
}
return render_template('home.html', results=None, form_data=form_data)
else:
# Capture form data for POST requests
form_data = {
'gender': request.form.get('gender', ''),
'ethnicity': request.form.get('ethnicity', ''),
'parental_level_of_education': request.form.get('parental_level_of_education', ''),
'lunch': request.form.get('lunch', ''),
'test_preparation_course': request.form.get('test_preparation_course', ''),
'writing_score': request.form.get('writing_score', ''),
'reading_score': request.form.get('reading_score', '')
}
try:
# Create CustomData object with form inputs
data = CustomData(
gender=form_data['gender'],
race_ethnicity=form_data['ethnicity'],
parental_level_of_education=form_data['parental_level_of_education'],
lunch=form_data['lunch'],
test_preparation_course=form_data['test_preparation_course'],
reading_score=float(form_data['reading_score']),
writing_score=float(form_data['writing_score'])
)
pred_df = data.get_data_as_data_frame()
predict_pipeline = PredictPipeline()
results = predict_pipeline.predict(pred_df)
return render_template(
'home.html',
results=results[0] if results else None,
form_data=form_data
)
except ValueError as e:
error_message = "Please enter valid numeric scores for Reading and Writing."
return render_template('home.html', results=None, form_data=form_data, error=error_message)
except Exception as e:
error_message = f"An error occurred: {str(e)}"
return render_template('home.html', results=None, form_data=form_data, error=error_message)
if __name__ == "__main__":
app.run(debug=True, port=8000)
Technical Implementation Details
Request Handling:
- GET requests to
/predictdatarender an empty form with predefined field structure - POST requests process form submissions, validate inputs, and execute predictions
- Form data persists across requests to repopulate fields after submission
Input Validation:
- Type conversion for reading/writing scores using
float()with exception handling - ValueError exceptions capture invalid numeric inputs
- Generic Exception handling captures pipeline failures with user-friendly messages
Prediction Workflow:
- Form data extracted from
request.formdictionary CustomDataobject instantiated with validated parameters- DataFrame created via
get_data_as_data_frame()method PredictPipeline.predict()executes inference using serialized artifacts- Results returned to template with original form data for display
Local Execution:
python flask_app.py
# Server runs at http://127.0.0.1:8000
streamlit_app.py: Cloud Deployment Application
Streamlit Cloud requires Streamlit-native applications; Flask applications cannot be deployed directly. I implemented a Streamlit application with equivalent functionality using Streamlitβs component library:
import streamlit as st
from src.pipeline.predict_pipeline import PredictPipeline, CustomData
# Page configuration
st.set_page_config(page_title="Student Performance Predictor", layout="centered")
# Session state initialization
if 'form_data' not in st.session_state:
st.session_state.form_data = {
'gender': '',
'ethnicity': '',
'parental_level_of_education': '',
'lunch': '',
'test_preparation_course': '',
'writing_score': 50,
'reading_score': 50
}
if 'prediction_result' not in st.session_state:
st.session_state.prediction_result = None
if 'error_message' not in st.session_state:
st.session_state.error_message = None
# Header
st.title("Student Performance Predictor")
st.markdown("Predict math scores based on student attributes (Reading/Writing scores must be 0-100)")
# Input form with two-column layout
with st.form("prediction_form"):
col1, col2 = st.columns(2)
with col1:
gender = st.selectbox("Gender", ["", "male", "female"],
index=["", "male", "female"].index(st.session_state.form_data['gender'])
if st.session_state.form_data['gender'] else 0)
ethnicity = st.selectbox("Ethnicity",
["", "group A", "group B", "group C", "group D", "group E"],
index=["", "group A", "group B", "group C", "group D", "group E"].index(st.session_state.form_data['ethnicity'])
if st.session_state.form_data['ethnicity'] else 0)
parental_edu = st.selectbox("Parental Education Level",
["", "some high school", "high school", "some college",
"associate's degree", "bachelor's degree", "master's degree"],
index=["", "some high school", "high school", "some college",
"associate's degree", "bachelor's degree", "master's degree"].index(st.session_state.form_data['parental_level_of_education'])
if st.session_state.form_data['parental_level_of_education'] else 0)
with col2:
lunch = st.selectbox("Lunch Type", ["", "standard", "free/reduced"],
index=["", "standard", "free/reduced"].index(st.session_state.form_data['lunch'])
if st.session_state.form_data['lunch'] else 0)
test_prep = st.selectbox("Test Prep Course", ["", "none", "completed"],
index=["", "none", "completed"].index(st.session_state.form_data['test_preparation_course'])
if st.session_state.form_data['test_preparation_course'] else 0)
reading_score = st.number_input(
"Reading Score (0-100)",
min_value=0,
max_value=100,
value=int(st.session_state.form_data['reading_score']),
step=1,
help="Enter integer score between 0 and 100"
)
writing_score = st.number_input(
"Writing Score (0-100)",
min_value=0,
max_value=100,
value=int(st.session_state.form_data['writing_score']),
step=1,
help="Enter integer score between 0 and 100"
)
submitted = st.form_submit_button("Predict Math Score", type="primary")
# Prediction logic
if submitted:
st.session_state.form_data = {
'gender': gender,
'ethnicity': ethnicity,
'parental_level_of_education': parental_edu,
'lunch': lunch,
'test_preparation_course': test_prep,
'writing_score': writing_score,
'reading_score': reading_score
}
if not all([gender, ethnicity, parental_edu, lunch, test_prep]):
st.session_state.error_message = "Please select values for all dropdown fields"
st.session_state.prediction_result = None
else:
try:
data = CustomData(
gender=gender,
race_ethnicity=ethnicity,
parental_level_of_education=parental_edu,
lunch=lunch,
test_preparation_course=test_prep,
reading_score=float(reading_score),
writing_score=float(writing_score)
)
pred_df = data.get_data_as_data_frame()
predict_pipeline = PredictPipeline()
results = predict_pipeline.predict(pred_df)
st.session_state.prediction_result = results[0]
st.session_state.error_message = None
except Exception as e:
st.session_state.error_message = f"Prediction error: {str(e)}"
st.session_state.prediction_result = None
# Display results
if st.session_state.error_message:
st.error(st.session_state.error_message)
if st.session_state.prediction_result is not None:
score = st.session_state.prediction_result
if score >= 90:
st.balloons()
st.success(f"Exceptional! Predicted Math Score: **{score:.1f}**")
elif score >= 70:
st.success(f"Strong Performance! Predicted Math Score: **{score:.1f}**")
elif score >= 50:
st.warning(f"Room for Growth! Predicted Math Score: **{score:.1f}**")
else:
st.error(f"Needs Attention! Predicted Math Score: **{score:.1f}**")
with st.expander("View Input Summary"):
summary = st.session_state.form_data.copy()
summary['predicted_math_score'] = f"{score:.1f}"
st.json(summary)
st.markdown("---")
st.caption("Scores are restricted to 0-100 integers β’ Model trained on student performance dataset β’ Powered by Streamlit")
Technical Implementation Details
Session State Management:
st.session_statepersists form data across widget interactions- Separate state variables for form inputs, prediction results, and error messages
- Enables form repopulation after prediction execution
Input Validation:
st.selectboxenforces categorical field selection with empty defaultst.number_inputrestricts scores to 0-100 integer range with step=1- Client-side validation prevents invalid submissions before server processing
- Dropdown validation ensures all categorical fields contain values before prediction
Two-Column Layout:
st.columns(2)creates responsive two-column form layout- Categorical inputs grouped in left column
- Lunch type, test prep, and numeric scores grouped in right column
- Improves form density and user experience on desktop displays
Conditional Display Logic:
- Error messages shown only when
st.session_state.error_messagecontains value - Prediction results displayed with conditional formatting based on score thresholds
- Expandable section (
st.expander) shows input summary without cluttering main view
Visual Feedback:
- Score-based conditional styling (success/warning/error)
st.balloons()animation for exceptional scores (β₯90)- Bold formatting for predicted score value
- Color-coded feedback based on performance thresholds
Streamlit Cloud Deployment Procedure
Streamlit Cloud deployment requires specific repository structure and configuration files. The deployment process involves the following steps:
Step 1: Repository Preparation
Create a public GitHub repository containing the complete project structure:
e2e-ml-project/
βββ artifacts/
β βββ data.csv
β βββ model.pkl
β βββ preprocessor.pkl
β βββ test.csv
β βββ train.csv
βββ catboost_info/
β βββ learn/
β β βββ events.out.tfevents
β βββ tmp/
β βββ catboost_training.json
β βββ learn_error.tsv
β βββ time_left.tsv
βββ e2e_ml_project.egg-info/
β βββ dependency_links.txt
β βββ requires.txt
β βββ top_level.txt
β βββ PKG-INFO
β βββ SOURCES.txt
βββ logs/
β βββ 2026_01_30_07_51_20.log
β βββ 2026_01_30_07_51_42.log
β βββ 2026_01_30_07_52_21.log
βββ notebook/
β βββ data/
β β βββ StudentsPerformance.csv
β βββ 1 . EDA STUDENT PERFORMANCE .ipynb
β βββ 2. MODEL TRAINING.ipynb
β βββ student-performance-eda.ipynb
βββ src/
β βββ components/
β β βββ __init__.py
β β βββ data_ingestion.py
β β βββ data_transformation.py
β β βββ model_trainer.py
β βββ pipeline/
β β βββ __init__.py
β β βββ model_trainer.py
β β βββ predict_pipeline.py
β β βββ train_pipeline.py
β βββ __init__.py
β βββ exception.py
β βββ logger.py
β βββ utils.py
β βββ utls-sum.md
βββ templates/
β βββ home.html
β βββ index.html
βββ flask_app.py
βββ info.md
βββ overview.txt
βββ requirements.txt
βββ setup.py
βββ streamlit_app.py
βββ train.py
βββ zen.py
βββ zinal.py
βββ README.md
Step 2: Requirements File Configuration
Ensure requirements.txt contains all dependencies with compatible versions:
setuptools
numpy
pandas
matplotlib
seaborn
scikit-learn
catboost
xgboost
dill
flask
altair==4.2.2
streamlit>=1.23.0
# -e .
Critical Notes:
- Include exact versions to prevent dependency conflicts
- Streamlit must be listed as a dependency
- All model training dependencies must be included for artifact loading
- Exclude development-only packages (e.g., Flask for production deployment)
Step 3: Streamlit Cloud Deployment
- Navigate to Streamlit Cloud
- Click βNew appβ button
- Configure deployment settings:
- Repository: Select GitHub repository containing the project
- Branch: Select branch (typically
mainormaster) - Main file path:
streamlit_app.py - Python version: Select compatible version (3.8+)
- Click βDeployβ to initiate deployment process
Step 4: Deployment Monitoring
Streamlit Cloud provides real-time build logs during deployment:
Building...
Collecting packages...
Installing dependencies from Pipfile...
Successfully installed numpy-1.24.3 pandas-2.0.3 scikit-learn-1.3.0 streamlit-1.28.0
...
Preparing to run streamlit_app.py...
Common Deployment Issues:
- Missing
requirements.txt: Deployment fails with dependency errors - Incorrect main file path: βFile not foundβ error
- Version conflicts: Build fails during package installation
- Large artifact files: Extended deployment time or timeout
Step 5: Post-Deployment Validation
After successful deployment:
- Access application via Streamlit Cloud URL
- Test prediction workflow with sample inputs
- Verify error handling for invalid inputs
- Confirm session state persistence across interactions
- Validate prediction accuracy against local testing results
Complete End-to-End Pipeline Architecture
The completed system implements a production-ready ML workflow with the following architecture:
DATA INPUT LAYER
βββ Raw CSV (notebook/data/StudentsPerformance.csv)
β
PIPELINE EXECUTION LAYER
βββ DataIngestion β artifacts/train.csv, test.csv
βββ DataTransformation β artifacts/preprocessor.pkl, train_arr.npy
βββ ModelTrainer β artifacts/model.pkl
β
ARTIFACT STORAGE LAYER
βββ artifacts/
β βββ model.pkl (serialized trained model)
β βββ preprocessor.pkl (serialized transformation pipeline)
β βββ train.csv, test.csv (data splits)
β βββ train_arr.npy, test_arr.npy (transformed arrays)
β
INFERENCE LAYER
βββ PredictPipeline (loads artifacts, executes prediction)
βββ CustomData (input validation and DataFrame conversion)
β
DEPLOYMENT LAYER
βββ Flask Application (local development: port 8000)
βββ Streamlit Application (cloud deployment: Streamlit Cloud)
Component Integration Flow
Training Workflow:
# Single command execution
python train.py
Internal execution sequence:
-
DataIngestion.initiate_data_ingestion() β Returns: train_path, test_path
-
DataTransformation.initiate_data_transformation(train_path, test_path) β Returns: train_arr, test_arr, preprocessor_path
-
ModelTrainer.initiate_model_trainer(train_arr, test_arr) β Returns: r2_score, saves model.pkl
Inference Workflow:
# Streamlit app execution
data = CustomData(gender="male", race_ethnicity="group B", ...)
pred_df = data.get_data_as_data_frame()
predict_pipeline = PredictPipeline()
prediction = predict_pipeline.predict(pred_df)
# Returns: predicted math score
Project Completion Checklist
-
Data Pipeline Components
- Data ingestion with artifact management
- Feature transformation with preprocessing pipeline
- Train/test split with reproducible random state
-
Model Training Components
- Algorithm tournament with 8 regression models
- Hyperparameter tuning via GridSearchCV
- Model selection with RΒ² quality gate
- Artifact serialization using dill
-
Pipeline Orchestration
- TrainPipeline class for component sequencing
- train.py entry point with pre-validation
- Structured logging and error handling
-
Inference Components
- PredictPipeline for artifact loading and prediction
- CustomData for input validation and schema enforcement
- Training/inference symmetry guarantee
-
Web Deployment
- Flask application for local development (port 8000)
- Streamlit application for cloud deployment
- Streamlit Cloud deployment with public URL
- Input validation and error handling in UI
-
Project Infrastructure
- Virtual environment configuration
- setup.py with dynamic requirements loading
- Git repository with version control
- Logging infrastructure with timestamped files
- Custom exception handling with traceback enrichment
Technical Validation Results
Model Performance:
- Best algorithm: Linear Regression
- Test RΒ² score: 0.88
- Artifact sizes: model.pkl (1 KB), preprocessor.pkl (4 KB)
Deployment Metrics:
- Flask local server: Response time <100ms per prediction
- Streamlit Cloud: Cold start ~8 seconds, subsequent predictions <2 seconds
- Input validation: Prevents 100% of type errors and range violations
- Session persistence: Form data maintained across prediction cycles
Error Handling Coverage:
- Missing raw data file: FileNotFoundError with actionable message
- Invalid numeric inputs: ValueError with user-friendly error display
- Pipeline failures: CustomException with full traceback logging
- Categorical field validation: Prevents incomplete form submissions
Future Enhancement Opportunities
While the current implementation delivers a complete end-to-end ML system, several enhancements could extend its capabilities:
-
Model Monitoring
- Implement prediction logging for drift detection
- Track input distribution changes over time
- Alert on performance degradation
-
Automated Retraining
- Schedule periodic retraining with new data
- Implement CI/CD pipeline for model updates
- Version control for model artifacts
-
API Expansion
- REST API endpoint for programmatic access
- Batch prediction support for multiple records
- Authentication and rate limiting
-
UI Enhancements
- Feature importance visualization
- Prediction confidence intervals
- Historical prediction tracking
-
Infrastructure Improvements
- Docker containerization for consistent environments
- Kubernetes deployment for scalability
- Cloud storage integration for artifact management
Conclusion
This six-day project demonstrates a complete end-to-end machine learning workflow from raw data ingestion to cloud deployment. The implementation includes:
- Reproducible data pipelines with artifact versioning
- Systematic model selection through algorithm tournaments
- Production-ready serialization with training/inference symmetry
- Web deployment via Flask (local) and Streamlit (cloud)
- Comprehensive error handling across all pipeline stages
The system successfully predicts student math scores with an RΒ² score of 0.9247 and provides an accessible web interface for end users. All components integrate through well-defined interfaces, enabling future extension and maintenance without architectural changes.
The complete project is deployed and accessible via Streamlit Cloud, demonstrating that a production-ready ML system can be built systematically with proper engineering practices and component isolation.
Project Repository: GitHub Link
Live Deployment: Streamlit Cloud URL
Completion Date: February 01, 2026
Key Technical Takeaways:
- Streamlit Cloud requires Streamlit-native applications; Flask apps cannot be deployed directly
- Session state management is critical for form persistence in Streamlit applications
- Client-side input validation reduces server errors and improves user experience
- Artifact serialization must preserve exact training-time parameters for inference consistency
- Production ML systems require deployment-specific adaptations beyond core pipeline components
What deployment challenges have you encountered in your ML projects? Share your experiences below!