Resume
All systems

PRICING ML

PromotionsAI

Automated SKU selection for promotions across every customer touchpoint, pushing recommendations straight into the merchandising API.

  • Python
  • PySpark
  • Azure Databricks
  • Delta Lake
  • MLflow
  • XGBoost
  • Docker
  • Azure Pipelines

Context

Promotional SKU selection was manual and slow, and the merchandising teams could not evaluate candidate promotions across all touchpoints at once.

That is a throughput problem before it is a modelling problem. The people making the calls were choosing from the slice of the catalogue they had time to look at, not from the whole of it.

Constraints

The serving contract was fixed before the model was. Recommendations had to arrive in the merchandising API the teams already worked in, because an output that lands anywhere else does not get used.

Nothing reaches production without passing through the MLflow registry, so the whole path had to be versioned and promotable rather than a notebook that happened to run.

Revenue is the acceptance metric. An offline lift estimate is an argument, not evidence, which is the constraint that shaped the first two experiment cycles.

What I built

A model and serving path that scores promotion candidates, selects SKUs and writes recommendations directly into the merchandising API, with the whole path versioned through the MLflow registry.

Candidate generation and scoring run as PySpark jobs on Azure Databricks over Delta Lake tables, so the training data and the serving features come out of the same versioned source.

Architecture

Feature computation and candidate scoring run on the shared platform: PySpark on Azure Databricks reading Delta Lake, with feature registration automated rather than hand-run.

The gradient boosted scorer is tracked in MLflow through training, so every promoted version carries its parameters, metrics and data lineage with it.

Inference is Dockerized and released through Azure Pipelines on the same blue-green path as the rest of the platform, which is what makes a bad release a rollback instead of an incident.

The output is a write into the merchandising API, not a dashboard. The recommendation shows up where the decision is already being made.

Results

5.5 million dollars in incremental revenue in its first full fiscal year.

2.3 million dollars in the following quarter.

The approach has since been extended to additional merchandising use cases.

What I would do differently

Invest earlier in the offline-to-online metric correlation, because the first two experiment cycles were spent establishing that the offline lift estimate tracked the realized revenue lift.

That work had to happen either way. Doing it first would have bought back two cycles of calendar time and made the first result easier to defend.