← BlogMLOps Project

MLOps Project, Part 4a: Machine learning model monitoring

Isaac Kargar7 min read

  • MLOps
  • Model Monitoring
  • Production
  • Machine Learning

A prediction service can return successful HTTP responses while its predictions become less useful. A schema change, a missing input field, or a different mix of customers can affect the model without taking the endpoint offline. After deploying a model, we need to watch the data and outcomes as well as the service.

This post compares two subsets of the California Housing dataset with Evidently, then uses Alibi Detect to show how numerical and categorical drift tests work together. The examples run locally and do not require a hosted monitoring account.

What to monitor

Service uptime is only one monitoring signal; data pipelines, schema, drift, model accuracy, bias, and performance across segments also need attention
The illustration separates service health from data and model health. Source: Evidently.
LayerUseful signalsWhat a change can mean
ServiceError rate, latency, resource use, failed dependenciesThe application cannot reliably process requests
Data qualityMissing values, types, ranges, duplicate records, fresh inputsA collection or transformation step may be broken
Data distributionFeature, prediction, and target distributionsThe current population differs from the reference population
Model qualityError against observed outcomes, calibration, performance by segmentPredictions may no longer meet the task’s requirements

Data drift is a change in the input distribution. Concept drift is a change in the relationship between inputs and the outcome. Neither follows automatically from a single monitoring alert. A geographic shift in housing data, for example, may change the price distribution while a model that uses location still predicts well.

Prediction drift can be measured before labels arrive. Prediction quality needs observed outcomes: classification labels, actual prices, or another task-specific target. Label delays and missing labels matter because the evaluated subset may differ from all requests.

Compare two datasets with Evidently

Evidently reports combine metrics and presets over a dataset. A drift report needs a reference dataset and a current dataset. Map their columns consistently so a numeric identifier is not accidentally treated as a continuous measurement.

Use Python 3.12 and these package versions for the examples:

python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install evidently==0.7.21 alibi-detect==0.13.0 \
  scikit-learn==1.9.0 pandas==2.3.3 numpy==1.26.4 scipy==1.17.1

California Housing contains eight input features and the target MedHouseVal. We take 1,000 rows from the first 15,000 records as the reference and 1,000 from the remaining records as the comparison. This is a comparison between dataset subsets, not evidence of temporal drift in a deployed housing model.

from sklearn.datasets import fetch_california_housing
from evidently import DataDefinition, Dataset, Report
from evidently.presets import DataDriftPreset

housing = fetch_california_housing(as_frame=True)
frame = housing.frame
columns = frame.columns.tolist()

reference_frame = frame.iloc[:15000].sample(1000, random_state=0)
current_frame = frame.iloc[15000:].sample(1000, random_state=0)

definition = DataDefinition(numerical_columns=columns)
reference = Dataset.from_pandas(reference_frame, data_definition=definition)
current = Dataset.from_pandas(current_frame, data_definition=definition)

report = Report([
    DataDriftPreset(
        columns=columns,
        method="ks",
        threshold=0.05,
        drift_share=0.5,
    )
])
result = report.run(current_data=current, reference_data=reference)
result.save_html("housing-drift.html")
result.save_json("housing-drift.json")

The first run downloads the public dataset. Open housing-drift.html to inspect distributions and the report’s column-level results. The JSON file contains the corresponding machine-readable results. The data definition guide explains how to map targets, predictions, categories, and text for other reports.

We explicitly select the two-sample Kolmogorov-Smirnov test rather than relying on automatic test selection. It compares the empirical cumulative distributions of a numerical column. At the chosen per-column threshold of 0.05, seven of the nine monitored columns are flagged:

ColumnK-S p-value, roundedDrift at 0.05
MedInc0.000348Detected
HouseAge0.00000175Detected
AveRooms0.001449Detected
AveBedrms0.465960Not detected
Population0.500567Not detected
AveOccup0.004544Detected
Latitude3.41e-100Detected
Longitude2.92e-125Detected
MedHouseVal6.98e-13Detected

That is 77.78% of the monitored columns, which includes the target. It is not seven of eight input features. The dataset-level flag also fires because the share exceeds the configured 50% threshold. Evidently’s drift preset documentation describes this aggregation and the available test choices.

A p-value displayed as 0.000000 can be a rounded very small value. It is not a probability that the datasets are identical, nor a measure of the business impact of the shift. Check the distributions, sample sizes, and affected segments before deciding what to do. Testing many columns also increases the chance of a false alert; the per-column tests above do not apply a multiple-testing correction.

Drift tests do not replace quality checks. Missing values, invalid categories, and delayed records deserve separate checks because a distribution test may operate only on the nonmissing values. If actual sale prices become available, compare predictions with those prices to measure model error directly.

Numerical and categorical drift with Alibi Detect

Alibi Detect provides detectors for distribution changes, outliers, and adversarial inputs. These ask different questions. An outlier detector scores an individual observation against its reference behavior; a drift detector usually compares batches or monitors a stream for a distribution change.

For mixed tabular data, TabularDrift combines K-S tests for numerical columns with chi-squared tests for categorical columns. The category mapping tells it which positions in the input array represent categories. The TabularDrift API reference documents that mapping and its multiple-testing options.

The next example uses income and house age from the same housing samples. It adds a binary longitude band solely to demonstrate categorical handling: 1 means longitude is below -120, and 0 means it is not.

import numpy as np
from alibi_detect.cd import TabularDrift


def mixed_features(data):
    return np.column_stack([
        data["MedInc"].to_numpy(),
        data["HouseAge"].to_numpy(),
        (data["Longitude"] < -120).astype(int).to_numpy(),
    ])


feature_names = ["MedInc", "HouseAge", "Longitude below -120"]
detector = TabularDrift(
    mixed_features(reference_frame),
    p_val=0.05,
    categories_per_feature={2: 2},
    correction="bonferroni",
)
drift = detector.predict(mixed_features(current_frame))["data"]

print("Dataset drift:", bool(drift["is_drift"]))
print("Corrected threshold:", drift["threshold"])
for name, p_value in zip(feature_names, drift["p_val"]):
    print(name, f"p={p_value:.6g}")

The detector flags a shift in this comparison. With three tested columns, the Bonferroni threshold is 0.05 / 3, or approximately 0.0167. Bonferroni controls the family-wise probability of at least one false rejection under the test assumptions. False-discovery-rate procedures instead control an expected proportion of false discoveries among rejections. These are different objectives, so choose the correction before interpreting alerts.

The test should also match the type and structure of the data:

MethodWhat it comparesA limit to consider
K-S testTwo univariate numerical distributionsSeparate feature tests can miss a change in dependence between features
Chi-squared testCategory frequenciesSparse expected counts can make the approximation unsuitable
Fisher’s exact testA contingency table, such as counts for a binary outcome in two batchesA binary test does not directly handle arbitrary continuous measurements
Maximum mean discrepancyMultivariate distributions through a kernelKernel choice, representation, and sample size affect sensitivity
Learned-kernel testDistributions using a kernel fitted to distinguish themKernel fitting and the final test need appropriate data separation

Alibi also includes outlier methods such as autoencoders, autoencoding mixture models, and time-series detectors. An autoencoder uses reconstruction error, while a forecasting detector compares observations with its expected range. Those scores need calibration on data representing acceptable behavior. A reconstruction error alone does not establish that an input is malicious.

Run monitoring around the prediction service

Keep monitoring work outside the request’s critical path. Record enough information to join a prediction with its later outcome, then evaluate bounded batches on a schedule. Restrict access and retention for captured inputs because monitoring data can contain the same sensitive fields as inference traffic.

A practical sequence is:

  1. Choose a reference period and record which population it represents.
  2. Capture input-quality statistics, predictions, timestamps, and the model version used.
  3. Compare a current window with an appropriate reference window, accounting for seasonality and segment size.
  4. Join delayed outcomes when they arrive and measure prediction error on the labelled subset.
  5. Route alerts to an owner who can investigate the data pipeline, serving changes, and model behavior.

Managed services can handle parts of this workflow. Existing users of Amazon SageMaker Model Monitor can capture inference data, run baseline comparisons on a schedule, and send metrics to CloudWatch. AWS no longer accepts new customers for that service. Its data-quality checks and its model-quality checks use different inputs: measuring prediction quality requires outcomes joined to the predictions.

A drift alert should start an investigation. If a schema bug caused the shift, fix the pipeline. If the population changed but prediction quality remains acceptable, retraining may not help. When a model change is warranted, evaluate the candidate against the current model and the affected segments before replacing it. Monitoring is useful when its signals lead to a specific, checkable action.

Work with Nazmi

Build your AI system with Nazmi.

Tell us what you are building, what exists today, and where your team needs help.

Start a conversation or book a 20-minute call →