Publish a Model: Card, Evaluation and a Versioned Release
Hands-on lab · IDE in your browser

Publish a Model: Card, Evaluation and a Versioned Release

Ship a churn model responsibly. Evaluate the candidate overall and per customer segment, write a model card whose claims come from that evaluation, cut a content-hashed semantic-version release, gate promotion on a per-segment recall floor that catches a candidate which is better on average but worse for rural customers, then roll back a release that slipped through.

Time
45 min
Checked steps
5
Level
Intermediate
Setup
None
Read step 1

Hands-on labs require Pro · $29.99/mo · cancel anytime

The job

Cascade Telecom's churn model tells the retention team who to call. A new candidate scores a little better on overall accuracy, and the team wants to ship it. You build the release process that decides whether it should: an evaluation, a model card, a versioned manifest, a promotion gate, and a registry you can roll back.

5 steps, each checked when you finish it

A check runs your work at the end of every step. Hints and the full solution are there if you get stuck.

  1. 1

    Evaluate the candidate

    Cascade Telecom runs a churn model to tell the retention team who to call.

  2. 2

    Write the model card

    A model card is what travels with a release: what it is for, what it was trained on, who owns it, and where it falls short.

  3. 3

    Version the release

    A release has to be identifiable and reproducible: the same model should always get the same id, a changed one should not, and the version number should follow a predictable scheme.

  4. 4

    Gate the promotion

    Now the decision.

  5. 5

    Register and roll back

    The gate is advice that people can override.

Step 1 as it appears in the lab

The lab’s own text. The hint and the solution stay inside the lab.

Step 1: Evaluate the candidate

Evaluate the candidate

Cascade Telecom runs a churn model to tell the retention team who to call. The model in production is v1. A team has a candidate, v2, that scores a little better on overall accuracy and wants to ship it. Before anything is published, you evaluate it the way the release will be judged: overall, and per customer segment.

model.py gives you load(path) for a model release, predict(entry, df) for churn probabilities, load_data() for the held-out set, and the constants SEGMENT ("region"), LABEL ("churn") and THRESHOLD. The dataset has a region column; rural customers are a small share but they still matter.

Write evaluate(entry, data) returning a report dict: n, accuracy, auc (ROC AUC over the predicted probabilities), and recall_by_region: for each region, the share of its actual churners the model flags at the threshold. Run it on both models and watch the rural number.

release.py, the file you edit75 lines
"""Publish the Cascade Telecom churn model. You evaluate the candidate, write its model card from that
evaluation, version it, gate the promotion on per-segment floors, and keep the registry honest so a bad
release can be rolled back."""
import hashlib
import json

from sklearn.metrics import roc_auc_score

import model

# The minimum churn recall we are willing to ship for each region. Rural is small but we will not blind
# ourselves to it.
FLOORS = {"metro": 0.20, "rural": 0.55}
REQUIRED_CARD_SECTIONS = ["intended_use", "training_data", "owner"]


# ---------- Step 1: evaluate the candidate ----------

def evaluate(entry, data):
    """A structured evaluation report for a model on data: overall accuracy and AUC, and the recall of
    churners within each region. Returns {"n", "accuracy", "auc", "recall_by_region": {region: recall}}."""
    # TODO (Step 1): predict on data, threshold at 0.5; return n, accuracy, auc
    # (roc_auc_score on the probabilities) and recall_by_region = churners flagged, per region.
    raise NotImplementedError("Step 1: write evaluate()")


# ---------- Step 2: write the model card ----------

def model_card(report, meta, floors=FLOORS):
    """Assemble the model card. The metrics and the list of segments the model underperforms on come from
    the evaluation itself, so the card cannot claim more than the eval
    shows. meta supplies the human-written sections; raise ValueError if a required one is missing."""
    raise NotImplementedError("model_card() arrives in Step 2")


# ---------- Step 3: version the release ----------

def content_hash(entry):
    """A stable sha256 of the model's weights, so an identical model always versions to the same hash and a
    changed one does not. Hash only the feature list, scaler stats and coefficients."""
    raise NotImplementedError("content_hash() arrives in Step 3")


def next_version(prev, part="minor"):
    """The next semantic version after prev ("major.minor.patch"), bumping the named part and zeroing the
    lower ones."""
    raise NotImplementedError("next_version() arrives in Step 3")


def release_manifest(entry, card, report):
    """The release record that ties a version to an exact model and its evaluation: name, version, the
    model's content hash, the metrics and the full card."""
    raise NotImplementedError("release_manifest() arrives in Step 3")


# ---------- Step 4: gate the promotion ----------

def gate(candidate, prod, floors=FLOORS, tol=0.005):
    """Decide whether the candidate report may be promoted over the production report. Block it if overall
    accuracy regressed by more than tol, or if any region's recall is below its floor. Returns
    {"promote": bool, "reasons": [...]} with a reason for every failure."""
    raise NotImplementedError("gate() arrives in Step 4")


# ---------- Step 5: register and roll back ----------

def promote(registry, manifest):
    """Record manifest as a release and make it current, pushing the previous current onto history."""
    raise NotImplementedError("promote() arrives in Step 5")


def rollback(registry):
    """Undo the last promotion: make the previous version current again. Raise ValueError if there is no
    history to roll back to."""
    raise NotImplementedError("rollback() arrives in Step 5")
Provided for you:data.csvmodel.pymodel_v1.jsonmodel_v2.jsontry_it.py

Frequently asked questions

What goes in a model card?

At least the intended use, the training data, an owner, the evaluation metrics and the known limitations. The metrics and limitations should be generated from the evaluation rather than written by hand, so the card cannot claim more than the model was shown to do.

Why gate a release on per-segment metrics?

A model can improve on an aggregate metric while getting worse for a smaller group, because the group barely moves the average. Gating on a recall floor for every segment catches that regression before it ships and quietly harms those users.

What is a content hash for a model?

A hash of the model's weights, so an identical model always gets the same id and any change produces a different one. It makes a release reproducible and lets a registry tell two versions apart with certainty.

Publishing a machine learning model

Publishing a model is more than saving a file. A release needs an evaluation that looks past the average into the segments that matter, a model card whose claims match that evaluation, a version and content hash so it is reproducible, and a gate that blocks a model which looks better overall while getting worse for a subgroup. In this lab you evaluate a churn candidate per region, generate its model card from the evaluation, cut a content-hashed semantic-version release, gate the promotion on a per-segment recall floor, and roll back a release that shipped when it should not have.