Mohd Zamin Quadri

GitHubLinkedIn

All work

ML systems engineering

A Testable End-to-End MLOps Pipeline

A runnable reference for the lifecycle around a text classifier, rebuilt on a licensed dataset so the pipeline's own quality gate has something real to refuse.

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.

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
9993 offline, 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.

01Problem

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.

02Contribution

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. What makes this one a reference is that each stage can decline to pass the artifact on, and the refusal is the interesting behaviour.

  1. Ingest

    Data enters under a licence the pipeline can verify. A gate that cannot check its input is decoration.

  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. This is where leakage usually enters, quietly, and never announces itself later.

  4. Gate

    The held-out result is compared against a threshold. If it falls short the artifact stops here - it is not registered, not promoted, and not served.

  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.

03System

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. 01Verify and validate licensed data
  2. 02Split train / validation / test
  3. 03Fit features on train only
  4. 04Evaluate held-out and gate
  5. 05Register, promote, and serve
  • scikit-learn
  • FastAPI
  • Docker
  • pytest
  • GitHub Actions
  • ruff

04Evidence

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 tests99 tests

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

05Inspection points

Go directly to the evidence

06Quality 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

07Limitations

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

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.