MZQMohd Zamin Quadri
All work

ML systems engineering

A Testable End-to-End MLOps Pipeline

A pipeline that stops a weak model from shipping.

Role
Project author and engineer
Classification
Reference implementation
Inspect repository
Promotion is a conjunction of four checks, not the next box along. One short check and the candidate stops here.A single model bundle travelling one track. The 1800/600/600 split is drawn, a candidate bundle is assembled, and it carries to a promotion gate of four plates: accuracy, weighted F1, margin over baseline, and p95 latency, each against its real threshold. The margin check refuses and the candidate is diverted rather than scored anyway; a second candidate is built, all four checks hold at the tracked values, and the bundle moves into staging and then to production, which is reachable only by promotion. The refusal is a staged demonstration of the gate's logic, not a recorded incident.
  1. Problem

    A perfect score came from duplicated rows.

  2. How it was solved

    I added real data, a held-out test and a gate that can refuse a release.

    1. Validate
    2. Train
    3. Gate
    4. Serve
  3. Result

    0.8067

    held-out accuracy on licensed data

    • Baseline0.5000
    • Model0.8067
  4. Built with

    • scikit-learn
    • FastAPI
    • Docker
    • pytest
    • GitHub Actions
    See the evidence on GitHub
A single model bundle travelling one track. The 1800/600/600 split is drawn, a candidate bundle is assembled, and it carries to a promotion gate of four plates: accuracy, weighted F1, margin over baseline, and p95 latency, each against its real threshold. The margin check refuses and the candidate is diverted rather than scored anyway; a second candidate is built, all four checks hold at the tracked values, and the bundle moves into staging and then to production, which is reachable only by promotion. The refusal is a staged demonstration of the gate's logic, not a recorded incident.

Reference implementation / Delivery

Promotion is not the next box along a diagram. It is a conjunction of four checks, and one short check stops the candidate where it stands.

Held-out accuracy0.8067

One fitting surface, and a gate that stops the run

train onlyheld outfitted stateheld-out matricescandidateevery check passesany check failspromotesame transformersLicensed dataHashed and checked forduplicates before anything…PartitionTrain, validation and test,fixed before a feature exists.Fit transformersThe only place anything isfitted. Train partition only.TransformCannot fit. Validation, testand serving share this path.Train candidateProduces a bundle withchecksummed artifacts.Promotion gateA conjunction of checks onheld-out metrics and latency.RegistryRegistered as staging, thentransitioned to production.Prediction serviceLoads only a bundle whose gateflag and checksums verify.Gate failureThe run raises and stops.Nothing is registered, and…
Fitting happens in exactly one box; validation, test and serving all go through a path that cannot fit. The gate is a conjunction - one failed check and nothing is registered at all.

This diagram in words

  1. Licensed data source — Hashed and checked for duplicates before anything reads it.
  2. Partition process — Train, validation and test, fixed before a feature exists.
  3. Fit transformers process — The only place anything is fitted. Train partition only.
    scikit-learn
  4. Transform process — Cannot fit. Validation, test and serving share this path.
  5. Train candidate process — Produces a bundle with checksummed artifacts.
    scikit-learn
  6. Promotion gate gate — A conjunction of checks on held-out metrics and latency.
  7. Registry store — Registered as staging, then transitioned to production.
  8. Prediction service sink — Loads only a bundle whose gate flag and checksums verify.
    FastAPI
  9. Gate failure sink — The run raises and stops. Nothing is registered, and nothing retries.

01 / 13The machine, closedTraining a model is the easy part. This is everything around it.

Task
Text classification, sentimentTF-IDF features into logistic regression
Lifecycle stages
Ninedata through serving, in one repository
Tests
164153 offline, 5 on the licensed data, 6 in a container
Accuracy0.8067>= 0.75Weighted F10.8067>= 0.75Margin over baseline0.3067>= 0.2Latency p950.067<= 100
Four checks, evaluated independently and then combined with all(). A candidate that fails any one of them cannot be registered to staging, and production cannot be registered directly at all — it is reachable only by promoting something already in staging. Held-out accuracy 0.8067 against a majority-class baseline of 0.5; the margin between them is the check that carries the meaning.
  1. UnregisteredA bundle on disk with no claim attached to it.
  2. StagingRequires an evaluated bundle whose gate passed.
  3. ProductionNot directly registrable. Reached only by promoting from staging.
  4. ArchivedA previous production version, kept rather than overwritten.
  • A reference implementationNo production traffic has ever hit it.
  • Six hundred test rowsThe interval on accuracy is roughly three points, so a one-point difference between models is noise.
  • Metrics are aggregateThe dataset pools three sources; per-source performance is not reported and would likely differ.
  • Latency is in-processMeasured on one machine as a regression guard, not a service level objective.
  • Monitoring is a counterA JSON counter endpoint, not a monitoring system.
  • The synthetic fixture is not evidenceIt exists so tests can run offline; a linear model scores about 1.0 on it, and nothing quoted as a result comes from it.
Read the full background

Problem

Why this work exists

The first version of this pipeline had every stage in place and proved nothing. Its only data path generated review text from ten templates, so the classifier scored a perfect 1.000 on rows that were 99% duplicates. The validator detected those duplicates, logged a warning, and let the run continue. A gate that cannot fail is not a gate, and a metric produced that way describes the fixture rather than the model.

Contribution

What I can claim

Replaced the synthetic evidence path with a checksum-verified licensed dataset, split it three ways so the test partition is read exactly once, restricted feature fitting to a single function so leakage is testable rather than asserted, added a majority-class baseline and a gate that requires a margin over it, and made the container prove a real prediction in CI instead of only building.

Reference lifecycle, with an explicit contract between each stage
  1. 01ValidateCheck, fingerprint, and version input data
  2. 02TransformFit features once for training and serving
  3. 03EvaluateTrack runs and apply the promotion gate
  4. 04RegisterPackage an immutable model bundle
  5. 05ServeLoad the approved bundle behind FastAPI
The reference runOne command trains, evaluates against the gate, registers and promotes. A second asserts the published result still reproduces.
$ python -m src.pipeline --config configs/train_config.yaml
[1/5] Acquiring and validating data
[2/5] Fitting features on train only, then training
[3/5] Evaluating on the held-out test split
[4/5] Registering the passing candidate as staging
[5/5] Promoting v1 to production

$ python scripts/check_reference_run.py
Reference run matches the documented result: accuracy 0.8067, baseline 0.5000, margin 0.3067,
sentiment-classifier v1 in production.

The second command exists so the numbers quoted on this page cannot drift away from the repository without failing its build. See the evaluation record.

What the held-out split actually says600 test rows, read once. The baseline is a majority-class predictor measured on exactly the same rows.

Confusion matrix

Held-out confusion matrix: rows are the actual class, columns the predicted class.
pred negativepred positive
actual negative24159
actual positive57243

484 of 600 correct. The errors are close to symmetric, so the model is not simply favouring one class.

Accuracy against its baseline

  • This model0.8067
  • Majority class0.5000

An accuracy figure means nothing without this comparison. The promotion gate requires a margin over the baseline, not just a raw floor, so a model that only predicts the larger class cannot pass.

Method

Promotion is conditional, and the pipeline has to be able to refuse

A pipeline that always finishes is not a pipeline, it is a conveyor.

  1. Ingest

    Data enters under a licence the pipeline can verify.

  2. Separate

    Train, validation and test are split before anything is fitted, so the held-out set is genuinely held out rather than nominally so.

  3. Fit

    Features are fitted on the training split alone.

  4. Gate

    The held-out result is compared against a threshold.

  5. Promote

    Only an artifact that passed is registered and served, and what it passed is recorded alongside it.

  6. What this does not show

    The gate proves the artifact met a threshold on one held-out split at one moment. It does not establish that the split is representative, that the threshold is the right one, or that the model will hold up on data collected later.

How it was built

System

Workflow and decisions

Each stage is a contract. Data is verified against a pinned checksum and can be refused, preprocessing is fitted on the training partition alone, evaluation reconstructs the split recorded in the candidate's lineage rather than accepting one supplied later, and the service loads only a checksum-valid bundle the registry marks as production.

  1. 1Verify and validate licensed data
  2. 2Split train / validation / test
  3. 3Fit features on train only
  4. 4Evaluate held-out and gate
  5. 5Register, promote, and serve
  • scikit-learn
  • FastAPI
  • Docker
  • pytest
  • GitHub Actions
  • ruff

Evidence

What is actually versioned

Held-out accuracy0.8067

Weighted F1 0.8067 on 600 test rows, against a majority-class baseline of 0.5000, a margin of 0.3067.

Ranking qualityROC-AUC 0.8795

PR-AUC 0.8895 on the same held-out rows.

Licensed data3,000 rows

UCI Sentiment Labelled Sentences, 1,500 positive / 1,500 negative, CC BY 4.0. Downloaded on demand against a pinned SHA-256 and never redistributed.

Split discipline1,800 / 600 / 600

Train, validation, test. Gate thresholds were derived from the validation split and the baseline; the test split was not used to select a threshold, model, or hyperparameter.

ReproducibilityByte-identical artifacts

The same model and transformer files, and metrics agreeing to twelve decimal places, across Windows, a clean virtual environment, and Ubuntu CI.

Automated tests164 tests

Unit, integration, and container layers. The container test builds the image, mounts a promoted bundle, and makes a real HTTP prediction.

Inspection points

Go directly to the evidence

How the work is checked

Quality controls

How the work is checked

  • Input validation can refuse: a degenerate dataset stops the run instead of producing a flattering score on it
  • One function fits preprocessing state; a test asserts that a token seen only outside training never enters the vocabulary
  • The promotion gate requires a margin over a majority-class baseline, so a raw accuracy floor cannot pass an imbalanced non-model
  • A published bundle carries SHA-256 checksums for 5 artifacts plus dataset provenance and licence, all validated before loading
  • The service separates liveness from readiness and starts unready rather than crashing when no model is available
  • CI runs offline tests, the licensed-data reference run, and container integration as separate jobs

Limitations

Where the evidence stops

  • Only 600 held-out test rows, so the confidence interval on accuracy is roughly three points; a one-point difference between models on this split is noise
  • Metrics are pooled across the dataset's three sources; per-source performance is not reported and would very likely differ
  • No probability calibration is reported, because nothing downstream consumes the probabilities as probabilities
  • A reference implementation that has never carried production traffic
  • Monitoring is an in-process counter endpoint over a bounded window, not a monitoring system
  • No delayed-label path exists, so nothing measures accuracy after deployment, only behaviour
  • MLflow tracking is optional and disabled by default; the bundle, not a tracking server, is the source of truth for promotion
What this changed in my practice

What this changed in my practice

A pipeline can have every correct stage and still prove nothing. The evidence a run produces is only as good as the data underneath it, and the fastest way to find that out is to give the quality gate something it can actually refuse.

Next evidence milestone: Slice-aware evaluation. The dataset pools Amazon, IMDb, and Yelp sentences into one score, and an aggregate number hides per-segment failure. The next evidence milestone is per-source metrics on the held-out split, and a gate that can refuse a model which is strong overall but weak on one source.

LearnThe reasoning, written up

tutorial5 min read

Evidence Provenance for Knowledge Systems

What a stored generation has to carry so a claim about it can be re-checked later: captured bytes, ruleset digests, and the difference between recorded and installed.

tutorial4 min read

Promotion Gates for Model Release

Promotion is not the next box along a diagram. It is a conjunction of independent checks, and one short check stops the candidate where it stands.