End-to-end ML platform for retail demand forecasting and marketing budget optimization using UCI Online Retail dataset
Predicts daily retail revenue and optimally allocates marketing budget across channels. Given historical sales data and marketing spend, the platform returns:
- Revenue Forecast -- predicted daily revenue using LightGBM
- Budget Allocation -- optimal spend distribution across search, email, social, and affiliate channels
- Model Comparison -- LightGBM baseline benchmarked against H2O AutoML
Trained on the UCI Online Retail dataset (~541K transactions, ~4,300 customers, Dec 2010 -- Dec 2011).
+------------------+
| Dashboard UI |
| (localhost:8000) |
+--------+---------+
|
+--------v---------+
| FastAPI |
| Predict / Train |
| Optimize Budget |
+--+-----+-----+---+
| | |
+----------+ +--+--+ +----------+
| | | |
+-------v---+ +------v-+ +v--------+ +v-----------+
| LightGBM | |Budget | | Metrics | | Storage |
| Demand | |Optimizer| | Prom. | | DuckDB |
| Forecast | +--------+ +---------+ | MinIO |
+-----------+ +------------+
Training Pipeline (Prefect):
Ingest -> Validate -> Aggregate -> Synthetic -> Features -> Train -> Evaluate -> Register
| Layer | Technology |
|---|---|
| API | FastAPI, Uvicorn |
| Models | LightGBM (baseline), H2O AutoML (benchmark) |
| Features | Pandas, NumPy (80 features: lag, rolling, adstock, momentum, channel interactions) |
| Pipeline Orchestration | Prefect (DAG visualization, task state tracking) |
| Experiment Tracking | MLflow + MinIO (S3-compatible artifact storage) |
| Monitoring | Prometheus (metrics collection) + Grafana (dashboards) |
| Storage | DuckDB (features/analytics), MinIO (model artifacts), SQLite (MLflow/Prefect metadata) |
| Validation | Pandera (schema validation), Pydantic (API schemas, config) |
| Deployment | Docker, Docker Compose (profiles), GCP free-tier VM |
git clone https://github.com/sherozshaikh/retail-demand-allocator.git
cd retail-demand-allocatorStart Docker (Docker Desktop, or Colima on macOS: colima start --memory 4 --cpu 2).
make upOpen http://localhost:8000 -- pre-trained model is included. Select a scenario and click Predict Revenue, or run a Budget Optimization.
uv venv .venv --python 3.11 && source .venv/bin/activate
uv pip install -e ".[dev]"
PYTHONPATH=. uvicorn services.api.app.main:app --host 0.0.0.0 --port 8000When running with make up, all services are available:
| URL | Service |
|---|---|
| http://localhost:8000 | Platform Dashboard |
| http://localhost:8000/docs | Swagger API Docs |
| http://localhost:4200 | Prefect Pipeline UI |
| http://localhost:5000 | MLflow Experiment Tracking |
| http://localhost:9001 | MinIO Console (minioadmin / minioadmin) |
| http://localhost:9090 | Prometheus |
| http://localhost:3000 | Grafana (admin / admin) |
Predicts daily revenue from 78 engineered features (lag, rolling mean/std, adstock-transformed marketing spend, promo intensity, channel interactions) across 305 trading days.
| Metric | Value |
|---|---|
| RMSE | 15,613.84 |
| MAE | 7,828.72 |
| R2 | 0.669 |
| MAPE | 11.18% |
| Training Time | 0.28s |
H2O AutoML was benchmarked against LightGBM on the same hold-out test set (last 20%, time-ordered). H2O trains GBM, XGBoost, DRF, Deep Learning, and Stacked Ensembles, then picks the best model.
| Metric | LightGBM | H2O Best (Stacked Ensemble) | Winner |
|---|---|---|---|
| RMSE | 15,613.84 | 12,951.29 | H2O |
| MAE | 7,828.72 | 7,449.79 | H2O |
| R2 | 0.669 | 0.773 | H2O |
| MAPE | 11.18% | 11.85% | LightGBM |
| Training Time | 0.28s | 121.28s | LightGBM (433x faster) |
Decision: LightGBM is the production model. H2O's stacked ensemble has better RMSE/R2 but is 433x slower, requires a JVM, and adds ~500MB to the image. LightGBM's sub-second training enables rapid iteration and fits on a free-tier VM.
A hyperparameter search (make ablation) was performed across 60 random LightGBM configurations to find the optimal baseline. The best config was auto-applied to configs/base.yaml. Results saved to data/artifacts/ablation_lightgbm.json.
Run the benchmark locally: make benchmark (requires uv pip install -e ".[dev,benchmark]")
Three allocation strategies for distributing marketing budget across channels:
| Method | Description |
|---|---|
| Equal Split | Divide budget equally across channels (baseline) |
| Heuristic | Weight channels by historical ROI coefficients |
| Evolutionary Search | Dirichlet-initialized population with mutation annealing; finds optimal allocation maximizing predicted revenue |
curl -X POST http://localhost:8000/v1/optimize-budget \
-H "Content-Type: application/json" \
-d '{
"total_budget": 10000,
"channels": ["search", "email", "social", "affiliate"],
"method": "search"
}'| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/predict |
Predict daily revenue from feature inputs |
| POST | /v1/optimize-budget |
Optimize marketing budget allocation |
| POST | /v1/train |
Trigger model retraining in background |
| GET | /v1/runs/{run_id} |
Check training run status |
| GET | /v1/health |
Health check (model load status) |
| GET | /v1/metrics |
Prometheus metrics |
curl -s -X POST http://localhost:8000/v1/predict \
-H "Content-Type: application/json" \
-d '{"features": {"day_of_week": 3, "month": 6, "total_revenue_lag_1": 12500, "total_revenue_roll_mean_7": 11800, "total_spend": 1200}}' | python3 -m json.toolcurl -X POST http://localhost:8000/v1/train \
-H "Content-Type: application/json" \
-d '{"config_path": "configs/base.yaml", "skip_h2o": true}'The training pipeline is orchestrated by Prefect with @flow and @task decorators:
Ingest Data -> Validate Schema -> Daily Aggregation -> Generate Synthetic Marketing
-> Build Features -> Train LightGBM -> [Train H2O] -> Evaluate Models -> Register to MLflow
make up # start API + all infra
make train # run one-shot training pipelineView the DAG and task states at http://localhost:4200. Model metrics and artifacts at http://localhost:5000.
make train-local # LightGBM only, fast (~6s)
make ablation # hyperparameter search (60 trials, ~1min)
make benchmark # LightGBM vs H2O comparisonThe API Docker image includes a pre-trained LightGBM model. Deploy in under 5 minutes:
# On any Ubuntu 22.04 VM (GCP e2-micro or AWS t2.micro):
sudo apt-get update && sudo apt-get install -y docker.io
sudo fallocate -l 1G /swapfile && sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
sudo docker pull sherozshaikh/retail-demand-allocator-api:v1.0.0
sudo docker run -d --name rda-api --restart unless-stopped -p 8000:8000 -e PYTHONPATH=/app -e RDA_CONFIG_PATH=configs/base.yaml sherozshaikh/retail-demand-allocator-api:v1.0.0Open http://<VM_EXTERNAL_IP>:8000 (ensure port 8000 is open in the firewall).
Full step-by-step guides:
- GCP: docs/DEPLOY_GCP.md (e2-micro, free tier)
- AWS: docs/DEPLOY_AWS.md (t2.micro, free tier)
make test-local # 69 tests, local
make test # 69 tests, inside Docker containerretail-demand-allocator/
├── configs/
│ ├── base.yaml # Production config
│ ├── dev.yaml # Development overrides
│ └── test.yaml # Test overrides (in-memory DuckDB)
├── data/
│ ├── raw/ # UCI Online Retail dataset (.xlsx/.csv)
│ ├── processed/ # Daily aggregates, feature datasets
│ ├── synthetic/ # Generated marketing spend & promo calendar
│ └── artifacts/ # Trained models, benchmark results
│ ├── lightgbm/ # LightGBM model + feature importance
│ ├── ablation_lightgbm.json # Hyperparameter search results
│ └── benchmark_results.json # LightGBM vs H2O comparison
├── ml/
│ ├── contracts/ # Pydantic config, structured logging
│ ├── data_access/ # DuckDB store, CSV/XLSX ingestion
│ ├── validation/ # Pandera schemas + validator
│ ├── transforms/ # Daily sales aggregation
│ ├── synthetic/ # Deterministic marketing data generator
│ ├── features/ # Feature engineering (80 features)
│ ├── models/ # LightGBM, H2O AutoML, evaluation, registry
│ ├── optimization/ # Budget allocation (equal, heuristic, evolutionary)
│ ├── tracking/ # MLflow wrapper
│ └── metrics/ # Prometheus metrics
├── services/
│ ├── api/ # FastAPI service + dashboard UI
│ │ ├── Dockerfile
│ │ └── app/
│ │ ├── main.py # App entry point
│ │ ├── routes/ # API endpoints
│ │ ├── schemas/ # Request/response models
│ │ ├── static/index.html # Dashboard UI
│ │ └── telemetry/ # Prometheus middleware
│ ├── pipeline/ # Prefect training pipeline
│ │ ├── Dockerfile
│ │ └── app/
│ │ ├── flows/ # @flow definition
│ │ ├── tasks/ # @task definitions (data, features, model)
│ │ └── orchestration/ # Pipeline runner
│ └── scheduler/ # Prefect scheduler
├── scripts/
│ ├── run_flow.py # CLI for local training
│ ├── ablation_lightgbm.py # LightGBM hyperparameter search
│ └── benchmark_h2o.py # LightGBM vs H2O AutoML benchmark
├── notebooks/
│ └── eda.ipynb # Exploratory data analysis (7 sections)
├── tests/ # 69 tests (unit, integration, smoke)
├── infra/
│ ├── mlflow/Dockerfile # MLflow server image
│ └── monitoring/ # Prometheus + Grafana configs
├── docs/
│ ├── DEPLOY_GCP.md # GCP free-tier deployment guide
│ └── screenshots/ # Dashboard, Prefect, MLflow, Grafana screenshots
├── docker-compose.yml # Full stack with profiles (infra, train)
├── pyproject.toml # Dependencies and project metadata
└── Makefile # Development and deployment commands
make up Start everything (API + MLflow + MinIO + Prefect + Prometheus + Grafana)
make down Stop all containers
make down-clean Stop all containers and remove volumes
make test Run tests inside Docker container
make test-local Run tests locally
make train Retrain models via Docker pipeline (with Prefect DAG + MLflow logging)
make train-local Train locally (LightGBM only, ~6s)
make ablation Ablation study: find best LightGBM hyperparameters (~1min)
make benchmark Benchmark LightGBM vs H2O AutoML (~2min)
make build Build Docker images for Docker Hub (linux/amd64)
make push Build and push images to Docker Hub
make pull Pull pre-built images from Docker Hub
make verify Check all service health endpoints
make format Format code (isort, black, ruff)
make lint Lint code (ruff)
make clean Remove generated files
- The base retail transaction data is from the UCI Machine Learning Repository (public domain research dataset)
- Marketing spend and promotion calendar tables are synthetic but deterministic -- generated programmatically with fixed seeds for reproducibility
- All synthetic generation logic is transparent and versioned in
ml/synthetic/ - Raw data is cached as CSV after first xlsx read for fast subsequent loads (<1s vs ~80s)
MIT License - see LICENSE file for details.








