The Problem
Teams that want to turn a Jupyter prototype into a reproducible, production‑grade ML service often lack a single, opinionated code base that demonstrates end‑to‑end data loading, training, tuning, evaluation, and serving while also exposing a CI pipeline and Kubernetes deployment. Maintaining consistency across notebooks, Python modules, and YAML manifests is error‑prone without a clear wiring diagram.
What This Does
The repository bundles a minimal ML product line in the madewithml/ package. Core responsibilities are split across a handful of files:
madewithml/data.py– loads CSV datasets, performs text cleaning (clean_text) and stratified splitting.madewithml/train.py&madewithml/tune.py– implement a training loop (train_model) and hyper‑parameter sweep (tune_models).madewithml/predict.py– wraps a PyTorch model withpredict_proba,decode, andformat_prob.madewithml/serve.py– provides the entry points_evaluateand_predictfor a FastAPI‑style service.
Documentation lives under docs/ and example notebooks in notebooks/. CI is defined in .github/workflows/ and deployment manifests for a Kubernetes cluster are in deploy/.
How It Is Wired
Execution starts in madewithml/serve.py:
_evaluate(line 49) callsevaluate()(frommadewithml/evaluate.py).evaluate()pulls a preprocessor viaget_preprocessor(), transforms data (transform()), and finally writes results withsave_dict()(callsos.makedirs→ filesystem).
The other entry point, _predict (line 55), follows:
- Calls
madewithml/predict.__call__, which invokespredict_proba. predict_probaroutes throughcollate_fn→pad_array, then through the model’sforward(inmadewithml/models.py) and finally formats probabilities withformat_prob.
The internal call graph shows 25 modules, 28 import edges and no circular dependencies. The most connected hub is madewithml/__init__ (imported by 11 other modules). Functions with the widest blast radius include transform (called from 7 places) and from_checkpoint (5 places).
External interactions are limited to:
- File I/O (
save_dict,load_dict) – 2 functions. - One outbound network call (presumably a model‑registry fetch).
- One external command execution (via
subprocessin the deployment scripts).
No database writes are present in the core library; the only persistence is filesystem‑based checkpoint storage.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/Made-With-ML
cd Made-With-ML
# Install dependencies (pyproject.toml defines runtime, requirements.txt pins exact versions)
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt # or: pip install .
# Run the unit test suite (CI currently lacks a test step, but tests exist)
pytest tests/code
# Start the service locally (entry point)
python -m madewithml.serve # invokes _predict/_evaluate via the server code
Configuration lives in madewithml/config.py; adjust paths or model hyper‑parameters there before training. Deploy to Kubernetes with the manifests in deploy/ (e.g., kubectl apply -f deploy/services/serve_model.yaml).
Real‑World Use
A data‑science team can iterate on a text classification model by:
from madewithml.train import train_model
from madewithml.tune import tune_models
# Train a baseline
train_model(num_epochs=5, batch_size=32)
# Run a hyper‑parameter sweep
tune_models(search_space={"lr": [1e-4, 1e-3], "batch_size": [16, 32]})
After validation, the same madewithml/serve.py module is packaged into a Docker image and deployed with the provided Helm‑style YAML, ensuring the production service uses identical code to the notebook experiments.
Code Health & Issues
- Critical –
.github/workflows/serve.yamlgrantspermissions: write-all. - High – GitHub Actions are not pinned to commit SHAs; no lockfile beside
pyproject.toml; CI workflow never runs tests; missing least‑privilege token declarations. - Medium –
deploy/services/serve_model.pyopens files without a context manager; duplicated logic intrain.py/tune.py; deep nesting and high branching in.github/workflows/json_to_md.py; lack of Dependabot; missing job timeouts; notebook outputs not stripped; checkout persists credentials. - Low – No job timeout set in
documentation.yaml.
All findings are derived from static analysis; no additional issues were inferred beyond the repository contents.
The Bottom Line
The repo offers a coherent, end‑to‑end ML pipeline with clear module boundaries and reproducible notebooks, making it a solid learning and starter kit. However, production readiness is hampered by several CI and security misconfigurations and a few code‑quality smells that should be addressed before commercial deployment. It is best suited for teams that need a reference implementation and are prepared to tighten the CI/security posture.