Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

artefact
A saved file a release needs, such as the model, the code that prepares its inputs or the exact package versions.
batch
A run that scores many rows at once on a schedule and writes the results to a table.
canary
A release shown to a small random slice of traffic first, so a bad model reaches few users.
drift
A change in the inputs a model sees, or in the link between those inputs and the outcome it predicts.
evaluation gate
A check before release that stops a model when its measured quality falls below a set bar.
feature contract
An agreed promise about one input column — its type, range, missing values, uniqueness, freshness and owner — checked as data arrives.
incident
An event that degrades a service beyond its agreed level and needs a coordinated response.
inference contract
The agreed inputs, outputs and error cases for a model's prediction service, written down and tested.
latency budget
The time a request may spend in the model path, after network and rendering are subtracted from what users will tolerate.
model card
A short document describing what a model is for, how it was trained, its limits and who owns it.
monitoring
Watching inputs, outputs, service health and later outcomes so a change is noticed before users report it.
rollback
Returning to the last known good version, using a saved copy and a command that has been rehearsed.
serving
Running a model to answer requests in production, either one at a time or in scheduled groups.
training-serving skew
When an input is computed one way for model fitting and another way at request time, so the model reads different data.

MLOps from the ground up

Packaging and environments

The environment is part of the model. A prediction is reproducible only when the code, the weights and every pinned dependency are.

Part 2 of 10

Intermediate16 min readBack to Courses

The dependency is the model

A model trained with one version of a library and served with another can change behaviour without a single weight changing. Numerical libraries, tokenisers and even the order of a dictionary can shift a score. The environment is not a detail around the model; it is part of the computation that produces the prediction.

This is why a model card that lists only the algorithm and the data is incomplete. It should also name the Python version, the framework version and the preprocessing library, because those decide what the numbers mean.

Preprocessing is the usual culprit. A tokenizer that lowercases differently, a scaler fitted on a different sample, or a category mapping that gained an entry can change the input before the model sees it. The weights are identical and the prediction is not, which is the hardest kind of regression to explain. The same rule applies to system libraries: a Python package that links against an unpinned libc is only half pinned.

Lock, do not list

A requirements file with direct dependencies is a wish; a lockfile with every transitive package and its hash is a record. Resolve once in CI, build the image from the lock, and promote that image rather than rebuilding it per environment. A rebuild at deploy time is a new environment wearing the old name.

Pinning everything by hand is not the same as locking. The resolver knows about conflicts and platform wheels that a human list does not. Use the resolver, commit its output, and review the diff when it changes.

Lockfiles are platform-specific more often than teams expect. A wheel for macOS and a wheel for Linux can resolve to different transitive sets, and a lock generated on a laptop may not install in the container. Generate the lock in the same image family that will run it, and test the install there.

Read the versions you actually have

from importlib.metadata import version

for package in ["numpy", "pandas", "scikit-learn", "mlflow"]:
    print(f"{package}=={version(package)}")

Two environments, one model

Run the comparison and identify which difference is most likely to change predictions. Then pin the serving environment to the training versions and confirm the report is empty.

Output
Click "Run" to execute your code

Containers and the base image

The base image is a dependency too. A slim Python image, a fixed digest and no build tools at runtime is the usual shape. The digest matters: a tag such as latest can point at a different image tomorrow, and the rebuild that worked yesterday now starts from a patched operating system.

Keep large weights out of the image when they would make every build slow. Mount them or fetch them by immutable version, and make that fetch a startup check. A service that starts without its weights and answers with a default is worse than one that refuses to start.

Keep build tools out of the runtime image. A compiler, a package manager cache and a test suite add attack surface and size, and none of them helps serve a request. Build in one stage, copy the installed environment into a slim runtime stage, and run as a non-root user.

Pin direct dependencies and lock transitive ones. The first makes intent readable; the second makes the build reproducible.

The reproducibility manifest

Record the Python version, the package versions, the data snapshot and the commit. A manifest turns “it worked on my machine” into a diff. It also makes a rollback possible: you can rebuild the old environment instead of guessing which package changed.

Store the manifest with the artefact, not in a wiki. When an incident asks what was different about Tuesday’s model, the answer should be one file away, and it should be the file the pipeline actually used.

The manifest is also a debugging tool. When two runs disagree, diff their manifests before reading the code. The answer is often a package patch, a different data snapshot or a Python minor version, and the diff finds it in seconds instead of an afternoon. Keep it in a text format a diff can read, not a screenshot of a terminal or a wiki page.

A fingerprint for the data

Run the manifest twice, then change one value in DATA and confirm the hash changes. A manifest that does not cover the data cannot explain a different result.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

Why is the environment part of the model?