HardMLOps

Retraining Trigger Design

MLOps

Hard

Problem

Choose the days on which an ML system retrains. Process daily_stats in order while tracking remaining budget, the last retraining day, and days since retraining.

A day requests retraining when at least one condition holds:

  • drift_score > drift_threshold
  • performance < performance_threshold
  • days_since_retrain >= max_staleness

The retraining occurs only when day - last_retrain_day >= cooldown and the remaining budget covers retrain_cost. Start days_since_retrain at zero, increment it before checking each day, and reset it after retraining. The initial cooldown is already satisfied. Return the retraining day numbers in chronological order.

Theory

Machine learning models are not static. They are trained on historical data, but the world evolves. User preferences shift, market conditions change, new products launch, and seasonal patterns emerge. Over time, the patterns the model learned become less representative of current reality, and performance degrades.

Retraining is the solution, but it comes with costs:

The challenge is finding the right balance: retrain often enough to maintain performance, but not so often that you waste resources or introduce unnecessary risk.

A retraining trigger policy codifies the rules for when to retrain. Instead of arbitrary schedules ("retrain every Monday") or reactive firefighting ("retrain when customers complain"), a trigger policy uses data-driven signals to make principled decisions.


Types of Retraining Triggers

There are three main categories of signals that can trigger retraining:

Drift-Based Triggers: Detect when the input data distribution has changed significantly from the training data. If the data looks different, the model may no longer be appropriate for it.

Performance-Based Triggers: Detect when model performance (accuracy, precision, F1, etc.) has dropped below acceptable levels. This is the most direct signal that something is wrong.

Time-Based Triggers (Staleness): Ensure the model does not go too long without an update, even if drift and performance metrics look acceptable. This provides a safety net against subtle degradation that metrics might miss.

A robust policy uses multiple triggers in combination, retraining when ANY of them fires.


Drift Trigger in Detail

Data drift occurs when the statistical properties of input features change over time. A drift score quantifies this change, typically using metrics like Population Stability Index (PSI), Total Variation Distance (TVD), or Kolmogorov-Smirnov statistics.

The drift trigger fires when:

\text{drift\_score} > \text{drift\_threshold}

Note the strict inequality. A drift score exactly equal to the threshold is not sufficient to trigger retraining.

Why use drift as a trigger?

Limitations:


Performance Trigger in Detail

Performance monitoring compares model predictions to actual outcomes (when available). If the model accuracy or other metrics fall below expectations, something is wrong.

The performance trigger fires when:

\text{current\_performance} < \text{performance\_threshold}

Again, strict inequality. Performance exactly at the threshold is acceptable.

Why use performance as a trigger?

Limitations:


Staleness Trigger in Detail

Even if drift and performance look acceptable, models should not run indefinitely without updates. Subtle degradation may accumulate, and periodic retraining ensures the model incorporates recent data.

The staleness trigger fires when:

\text{days\_since\_retrain} \geq \text{max\_staleness}

Note the "greater than or equal to" comparison. Once the model reaches maximum age, it must be retrained.

Why use staleness as a trigger?

Limitations:


Operational Constraints

Triggers tell you when retraining SHOULD happen. But operational constraints determine when it CAN happen.

Cooldown Period: After retraining, you should observe the new model in production before considering another retrain. This prevents rapid cycling and allows time to validate performance.

\text{days\_since\_last\_retrain} \geq \text{cooldown}

Budget Constraint: Retraining costs money. You may have a limited budget for the period. Each retrain must fit within remaining budget.

\text{remaining\_budget} \geq \text{retrain\_cost}

A trigger can only result in actual retraining if BOTH constraints are satisfied.


The Complete Decision Logic

Each day, the system evaluates whether to retrain using this algorithm:

Step 1: Check if any trigger fires

If none fire, do not retrain.

Step 2: Check if constraints allow retraining

If either constraint is not met, do not retrain (even though a trigger fired).

Step 3: If trigger fired AND constraints satisfied, retrain

Step 4: Update state for next day


State Management

The policy maintains state across days:

days_since_retrain: Starts at 0 (assuming a recent train before monitoring period). Increments by 1 each day. Resets to 0 after each retrain.

remaining_budget: Starts at the initial budget. Decreases by retrain_cost after each retrain. Never increases.

last_retrain_day: Tracks when the last retrain occurred for cooldown calculation. Initialize such that cooldown is satisfied on day 1.

The initial state assumes the model was freshly trained before the monitoring period begins, so:


A Detailed Worked Example

Configuration:

Daily Stats (10 days):

Day-by-day analysis:

Day 1:

Day 2:

Day 3:

Day 4:

Day 5:

Day 6:

Day 7:

Day 8:

Day 9:

Day 10:

Result: Retraining occurred on Days 3 and 7. Output: [3, 7]


Trade-offs in Policy Design

Aggressive vs. Conservative Triggers: Lower thresholds trigger more retrains, keeping the model fresher but consuming more budget. Higher thresholds save resources but risk longer periods of degraded performance.

Cooldown Length: Longer cooldown prevents thrashing but may delay necessary retrains. Shorter cooldown is more responsive but may lead to excessive retrains during volatile periods.

Budget Allocation: A limited budget forces prioritization. The policy may miss triggers late in the period if budget was exhausted early.

Multiple Triggers: Using all three trigger types provides defense in depth but may lead to more retrains than strictly necessary.


Where Retraining Triggers Show Up

Continuous Training Systems: Major ML platforms (Google, Meta, Amazon) run automated pipelines that monitor drift and performance, triggering retrains without human intervention.

Recommendation Systems: E-commerce and content platforms retrain models frequently to incorporate new items and evolving user preferences.

Fraud Detection: Financial institutions balance retraining frequency against validation requirements and regulatory constraints.

Demand Forecasting: Retail and logistics systems retrain models to capture seasonal patterns and market changes.

Healthcare AI: Medical ML systems require careful retraining policies that balance model freshness with clinical validation requirements.

Examples

Example 1

Input
daily_stats = [{"day": 1, "drift_score": 0.1, "performance": 0.95}, {"day": 2, "drift_score": 0.3, "performance": 0.93}, {"day": 3, "drift_score": 0.6, "performance": 0.9}, {"day": 4, "drift_score": 0.2, "performance": 0.94}], config = {"drift_threshold": 0.5, "performance_threshold": 0.7, "max_staleness": 30, "cooldown": 1, "retrain_cost": 100, "budget": 500}
Output
[3]
Explanation
Only day 3 exceeds the drift threshold, and both operational constraints permit retraining.

Example 2

Input
daily_stats = [{"day": 1, "drift_score": 0.1, "performance": 0.85}, {"day": 2, "drift_score": 0.15, "performance": 0.65}, {"day": 3, "drift_score": 0.1, "performance": 0.9}], config = {"drift_threshold": 0.5, "performance_threshold": 0.7, "max_staleness": 30, "cooldown": 1, "retrain_cost": 100, "budget": 500}
Output
[2]

Hints

  1. Initialize last_retrain_day = -config["cooldown"] so the first trigger can run.
  2. Combine the three triggers with or, then combine cooldown and budget checks with and.

Requirements

Constraints

Starter Code

def retraining_policy(daily_stats: list, config: dict) -> list:
    """
    Returns a list of retraining day numbers.
    """
    # Write code here
    pass

Test Cases

CaseMatches
Drift triggerpublic
Performance triggerpublic