LeetLLM
My PlanLearnGlossaryTracksPracticeBlog
LeetLLM

Your go-to resource for mastering AI & LLM systems.

Product

  • Learn
  • Glossary
  • Tracks
  • Practice
  • Blog
  • RSS

Legal

  • Terms of Service
  • Privacy Policy

© 2026 LeetLLM. All rights reserved.

All Topics
Your Progress
0%

0 of 158 articles completed

🛠️Computing Foundations0/9
Git, Shell, Linux for AIDocker for Reproducible AIPython for AI EngineeringNumPy and Tensor ShapesCUDA for ML TrainingMPS & Metal for ML on MacData Structures for AISQL and Data ModelingAlgorithms for ML Engineers
📊Math & Statistics0/8
Gradients and BackpropVectors, Matrices & TensorsLinear Algebra for MLAdam, Momentum, SchedulersProbability for Machine LearningStatistics and UncertaintyDistributions and SamplingHypothesis Tests, Intervals, and pass@k
📚Preparation & Prerequisites0/13
Neural Networks from ScratchCNNs from ScratchTraining & BackpropagationSoftmax, Cross-Entropy & OptimizationRNNs, LSTMs, GRUs, and Sequence ModelingAutoencoders and VAEsThe Transformer Architecture End-to-EndLanguage Modeling & Next TokensFrom GPT to Modern LLMsPrompt Engineering FundamentalsCalling LLM APIs in ProductionFirst AI App End-to-EndThe LLM Lifecycle
🧮ML Algorithms & Evaluation0/11
Linear Regression from ScratchLogistic Regression and MetricsDecision Trees, Forests, and BoostingReinforcement Learning BasicsValidation and LeakageClustering and PCACore Retrieval AlgorithmsDecoding AlgorithmsExperiment Design and A/B TestingPyTorch Training LoopsDataset Pipelines and Data Quality
📦Production ML Systems0/6
Feature Engineering for Production MLBatch and Streaming Feature PipelinesGradient Boosted Trees in ProductionRanking and Recommendation SystemsForecasting and Anomaly DetectionMonitoring Predictive Models
🧪Core LLM Foundations0/8
The Bitter Lesson & ComputeBPE, WordPiece, and SentencePieceStatic to Contextual EmbeddingsPerplexity & Model EvaluationFile Ingestion for AIChunking StrategiesLLM Benchmarks & LimitationsInstruction Tuning & Chat Templates
🧰Applied LLM Engineering0/23
Dimensionality Reduction for EmbeddingsCoT, ToT & Self-Consistency PromptingFunction Calling & Tool UseMCP & Tool Protocol StandardsPrompt Injection DefenseResponsible AI GovernanceData Labeling and Human FeedbackEvaluating AI AgentsProduction RAG PipelinesHybrid Search: Dense + SparseReranking and Cross-Encoders for RAGRAG Evaluation for Reliable AnswersLLM-as-a-Judge EvaluationBias & Fairness in LLMsHallucination Detection & MitigationLLM Observability & MonitoringExperiment Tracking with MLflow and W&BMixed Precision TrainingModel Versioning & DeploymentSemantic Caching & Cost OptimizationLLM Cost Engineering & Token EconomicsModel Gateways, Routing, and FallbacksDesign an Automated Support Agent
🎓Portfolio Capstones0/9
Capstone: Delivery ETA PredictionCapstone: Product RankingCapstone: Demand ForecastingCapstone: Image Damage ClassifierCapstone: Production ML PipelineCapstone: Document QACapstone: Eval DashboardCapstone: Fine-Tuned ClassifierCapstone: Production Agent
🧠Transformer Deep Dives0/8
Sentence Embeddings & Contrastive LossEmbedding Similarity & QuantizationScaled Dot-Product AttentionVision Transformers and Image EncodersPositional Encoding: RoPE & ALiBiLayer Normalization: Pre-LN vs Post-LNMechanistic InterpretabilityDecoding Strategies: Greedy to Nucleus
🧬Advanced Training & Adaptation0/16
Scaling Laws & Compute-Optimal TrainingPre-training Data at ScaleBuild GPT from Scratch LabContinued Pretraining for Domain ShiftSynthetic Data PipelinesSupervised Fine-Tuning PipelineDistributed Training: FSDP & ZeROLoRA & Parameter-Efficient TuningReward Modeling from Preference DataRLHF & DPO AlignmentConstitutional AI & Red TeamingRLVR & Verifiable RewardsKnowledge Distillation for LLMsModel Merging and Weight InterpolationPrompt Optimization with DSPyRecursive Language Models (RLM)
🤖Advanced Agents & Retrieval0/14
Vector DB Internals: HNSW & IVFAdvanced RAG: HyDE & Self-RAGGraphRAG & Knowledge GraphsRAG Security & Access ControlStructured Output GenerationReAct & Plan-and-ExecuteGuardrails & Safety FiltersCode Generation & SandboxingComputer-Use / GUI / Browser AgentsHuman-in-the-Loop Agent ArchitectureAI Coding Workflow with AgentsAgent Memory & PersistenceAgent Failure & RecoveryMulti-Agent Orchestration
⚡Inference & Production Scale0/20
Inference: TTFT, TPS & KV CacheMulti-Query & Grouped-Query AttentionKV Cache & PagedAttentionPrefix Caching and Prompt CachingFlashAttention & Memory EfficiencyContinuous Batching & SchedulingScaling LLM InferenceModel Parallelism for LLM InferenceModel Quantization: GPTQ, AWQ & GGUFLocal LLM DeploymentSLM Specialization & Edge DeploymentSpeculative DecodingLong Context Window ManagementContext EngineeringMixture of Experts ArchitectureMamba & State Space ModelsReasoning & Test-Time ComputeAdvanced MLOps & DevOps for AIGPU Serving & AutoscalingA/B Testing for LLMs
🏗️System Design Capstones0/9
Content Moderation SystemCode Completion SystemMulti-Tenant LLM PlatformLLM-Powered Search EngineVision-Language Models & CLIPMultimodal LLM ArchitectureDiffusion Models: Images & TextReal-Time Voice AI AgentReasoning & Test-Time Compute
🎤AI Lab Interviewing0/4
AI Lab Coding Interview: Python SystemsAI Lab System Design InterviewAI Lab Behavioral InterviewAI Lab Technical Presentation
Back to Topics
LearnProduction ML SystemsGradient Boosted Trees in Production
⚙️MediumMLOps & Deployment

Gradient Boosted Trees in Production

Train a boosted SLA-risk baseline from tabular features, evaluate slices, and package deployment evidence.

14 min read
Learning path
Step 44 of 158 in the full curriculum
Batch and Streaming Feature PipelinesRanking and Recommendation Systems

The feature pipeline now gives you honest rows: input size, heartbeat age, backlog, priority class, and a later label saying whether a job missed its SLA. The next job is to turn those rows into a useful decision: which jobs need intervention before they miss their SLA?

A gradient boosted tree classifier is a strong candidate for this kind of table. It can learn nonlinear rules without replacing the evaluation discipline from the previous chapters. A fitted model earns promotion only when it beats a declared baseline on later jobs, survives important slices, and travels with enough evidence to reproduce the decision.

Production boosted-tree evidence dashboard with April validation loss falling from 0.6805 to a minimum of 0.5643 at round 50 before rising to 0.5922 at round 160, threshold-cost bars selecting 0.15, and May priority and large-model recall clearing their support-aware release gates. Production boosted-tree evidence dashboard with April validation loss falling from 0.6805 to a minimum of 0.5643 at round 50 before rising to 0.5922 at round 160, threshold-cost bars selecting 0.15, and May priority and large-model recall clearing their support-aware release gates.
April freezes both model size and action policy: round 50 minimizes validation loss and threshold 0.15 minimizes declared cost. Only then does May provide final evidence, where priority recall 0.944 and large-model recall 0.903 pass their support-aware gates.

Freeze the clock before training

Suppose missed_sla = 1 means a training job missed its SLA. Before training, publish a split:

SplitCalendar rangePurpose
trainJanuary through Marchfit model
validationAprilselect round count and threshold
testMayfinal evidence after choices freeze

Random rows could place near-identical traffic patterns from the same disruption into train and test. Time order better represents a model facing tomorrow's jobs.

Time order doesn't erase every dependency. If one job incident can contribute several rows, document the split unit and keep linked rows together whenever those correlations would make holdout artificially easy.

Build a deterministic fixture that follows that calendar. The generated labels depend on heartbeat age, backlog, service tier, input size, and a small amount of noise. May also gets a slight drift term, so the test month isn't identical to training.

build-monthly-fixture.py
1import json 2from hashlib import sha256 3 4import numpy as np 5from sklearn.ensemble import GradientBoostingClassifier 6from sklearn.metrics import log_loss, roc_auc_score 7 8rng = np.random.default_rng(11) 9FEATURES = [ 10 "input_gb", 11 "heartbeat_age_hours", 12 "backlog", 13 "priority", 14 "large_model", 15] 16 17def sigmoid(value): 18 return 1 / (1 + np.exp(-value)) 19 20def make_jobs(month, count, drift=0.0): 21 input_gb = rng.integers(40, 1601, count) 22 heartbeat_age = rng.integers(1, 41, count) 23 backlog = rng.integers(0, 31, count) 24 priority = rng.integers(0, 2, count) 25 large_model = (input_gb >= 900).astype(int) 26 logit = ( 27 -5.2 28 + 0.13 * heartbeat_age 29 + 0.07 * backlog 30 + 0.85 * priority 31 + 0.75 * large_model 32 + drift 33 ) 34 missed_sla = rng.binomial(1, sigmoid(logit)) 35 X = np.column_stack([input_gb, heartbeat_age, backlog, priority, large_model]) 36 return {"month": month, "X": X, "y": missed_sla} 37 38train = make_jobs("Jan-Mar", 360) 39valid = make_jobs("April", 140) 40test = make_jobs("May", 140, drift=0.18) 41 42for split in (train, valid, test): 43 print(f"{split['month']}: rows={len(split['y'])} missed_sla={int(split['y'].sum())}")
Output
1Jan-Mar: rows=360 missed_sla=119 2April: rows=140 missed_sla=58 3May: rows=140 missed_sla=55

The fixture is synthetic so you can rerun the whole lab. In a real training job, X must come from the point-in-time replay contract you built in Batch and Streaming Feature Pipelines. A row created after its decision timestamp doesn't belong in training.

Publish the cheapest baseline

Your first candidate can be a rule: predict an SLA miss when heartbeat_age_hours >= 18. It's weak, but it makes the boosted model prove its added complexity rather than receiving credit for any nonzero result.

Define one cost policy before comparing models:

  • A missed priority job costs 150.
  • A missed standard job costs 60.
  • A false alarm costs 8.

The exact numbers will differ by product. Publishing them matters because model quality is inseparable from the action the score triggers.

score-rule-baseline.py
1def confusion(y, predicted): 2 return { 3 "tp": int(np.sum((y == 1) & (predicted == 1))), 4 "fp": int(np.sum((y == 0) & (predicted == 1))), 5 "fn": int(np.sum((y == 1) & (predicted == 0))), 6 "tn": int(np.sum((y == 0) & (predicted == 0))), 7 } 8 9def cost_stats(split, scores, threshold): 10 predicted = (scores >= threshold).astype(int) 11 priority = split["X"][:, FEATURES.index("priority")] == 1 12 missed = (split["y"] == 1) & (predicted == 0) 13 false_alarm = (split["y"] == 0) & (predicted == 1) 14 return { 15 "threshold": threshold, 16 "cost": int( 17 150 * np.sum(missed & priority) 18 + 60 * np.sum(missed & ~priority) 19 + 8 * np.sum(false_alarm) 20 ), 21 "priority_misses": int(np.sum(missed & priority)), 22 **confusion(split["y"], predicted), 23 } 24 25heartbeat_age_index = FEATURES.index("heartbeat_age_hours") 26rule_valid = (valid["X"][:, heartbeat_age_index] >= 18).astype(int) 27print("rule validation:", cost_stats(valid, rule_valid.astype(float), threshold=0.50))
Output
1rule validation: {'threshold': 0.5, 'cost': 1560, 'priority_misses': 8, 'tp': 48, 'fp': 30, 'fn': 10, 'tn': 52}

This baseline is intentionally simple. It already catches 48 April SLA misses. A boosted model has to improve the decision policy, rather than produce a decimal score that changes no action.

Rebuild one boosting step by hand

A shallow decision tree partitions rows into a few rules. Gradient boosting adds shallow trees sequentially: each new tree moves predictions toward errors left by the earlier ensemble. Friedman describes this process as stage-wise function approximation using loss gradients.[1]Reference 1Greedy Function Approximation: A Gradient Boosting Machinehttps://projecteuclid.org/journals/annals-of-statistics/volume-29/issue-5/Greedy-Function-Approximation--A-Gradient-Boosting-Machine/10.1214/aos/1013203451.full

For intuition, switch from a binary outcome to delay hours:

LaneActual delayFirst predictionResidual
local standard26-4
regional standard86+2
large-model economy206+14

A small correction tree might add little for local jobs and more for long economy workloads. For squared-error regression, its training target is the residual actual - prediction. A learning rate applies only part of that correction, so repeated trees refine mistakes without letting one tree dominate.

apply-one-residual-correction.py
1actual_delay = np.array([2.0, 8.0, 20.0]) 2prediction = np.array([6.0, 6.0, 6.0]) 3large_model = np.array([0, 0, 1]) 4residual = actual_delay - prediction 5 6correction = np.where( 7 large_model == 1, 8 residual[large_model == 1].mean(), 9 residual[large_model == 0].mean(), 10) 11learning_rate = 0.25 12updated = prediction + learning_rate * correction 13 14print("residuals:", residual.tolist()) 15print("tree correction:", correction.tolist()) 16print("before mae:", round(float(np.mean(np.abs(actual_delay - prediction))), 2)) 17print("after mae:", round(float(np.mean(np.abs(actual_delay - updated))), 2))
Output
1residuals: [-4.0, 2.0, 14.0] 2tree correction: [-1.0, -1.0, 14.0] 3before mae: 6.67 4after mae: 5.5

The correction tree predicts -1 hour for shorter workloads and 14 hours for the large-model workload. Shrinkage applies only one quarter of that proposal. One round lowers mean absolute error without pretending the first correction is perfect.

Pause and predict: If learning rate changed from 0.25 to 1.0, which job would move most? The large-model job would jump by the full 14 hours because its residual-tree leaf has the largest correction.

Classification needs one extra step. An SLA-risk classifier doesn't fit raw delay-hour residuals. With log loss, each new tree fits the negative gradient of that loss. At an initial probability of 0.50, the binary log-loss gradient points toward gold - probability.

inspect-log-loss-gradient.py
1gold = np.array([0.0, 1.0, 1.0]) 2probability = np.full(3, 0.50) 3negative_gradient = gold - probability 4 5print("initial probability:", probability.tolist()) 6print("gold - probability:", negative_gradient.tolist())
Output
1initial probability: [0.5, 0.5, 0.5] 2gold - probability: [-0.5, 0.5, 0.5]

Negative values push SLA-risk scores down. Positive values push them up. A real classification booster repeats this process across many rows and trees.

XGBoost extends tree boosting with a regularized objective, sparse-aware split handling, column blocks, and parallel techniques for scalable training.[2]Reference 2XGBoost: A Scalable Tree Boosting System.https://www.kdd.org/kdd2016/papers/files/rfp0697-chenAemb.pdf For an engineer, the important artifact is still the evaluation contract: a fitted booster and its threshold must be linked to feature version, split manifest, metrics, and serving schema.

Diagram showing Feature snapshot v1 time split, Rule baseline heartbeat age only, Boosted trees depth + rounds, and Validation comparison. Diagram showing Feature snapshot v1 time split, Rule baseline heartbeat age only, Boosted trees depth + rounds, and Validation comparison.
Feature snapshot v1 time split, Rule baseline heartbeat age only, Boosted trees depth + rounds, and Validation comparison.

Fit a booster with an explicit stopping rule

The mechanics stay the same across libraries. This lab uses scikit-learn's GradientBoostingClassifier so the code stays small. Its classifier builds an additive model stage by stage and fits trees against loss gradients. A larger production job might swap in XGBoost for its scalable training system, but the evidence contract doesn't change.

Fix tree depth and learning rate first. Train a generous maximum number of rounds on January through March, then inspect April loss after each stage.

fit-round-search.py
1round_search = GradientBoostingClassifier( 2 loss="log_loss", 3 n_estimators=160, 4 learning_rate=0.05, 5 max_depth=2, 6 random_state=11, 7) 8round_search.fit(train["X"], train["y"]) 9print("trained stage cap:", round_search.n_estimators_)
Output
1trained stage cap: 160
choose-early-stop-round.py
1validation_losses = [ 2 log_loss(valid["y"], probabilities[:, 1], labels=[0, 1]) 3 for probabilities in round_search.staged_predict_proba(valid["X"]) 4] 5best_round = int(np.argmin(validation_losses) + 1) 6 7print("round 1 validation loss:", round(validation_losses[0], 4)) 8print("best round:", best_round) 9print("best validation loss:", round(validation_losses[best_round - 1], 4)) 10print("round 160 validation loss:", round(validation_losses[-1], 4))
Output
1round 1 validation loss: 0.6805 2best round: 50 3best validation loss: 0.5643 4round 160 validation loss: 0.5922

April loss reaches its minimum at round 50, then gets worse by round 160. More trees improved the training objective but stopped helping future jobs. That's the overfitting symptom early stopping catches.

Some libraries expose built-in early stopping flags. A production job still needs to record the exact validation slice, monitored metric, patience policy, and selected round count. Here the stopping rule is visible: choose the stage with minimum April log loss, then fit the candidate with that frozen count.

fit-frozen-candidate.py
1candidate = GradientBoostingClassifier( 2 loss="log_loss", 3 n_estimators=best_round, 4 learning_rate=0.05, 5 max_depth=2, 6 random_state=11, 7) 8candidate.fit(train["X"], train["y"]) 9valid_scores = candidate.predict_proba(valid["X"])[:, 1] 10 11print("frozen rounds:", candidate.n_estimators_) 12print("rule validation auc:", round(roc_auc_score(valid["y"], rule_valid), 3)) 13print("boosted validation auc:", round(roc_auc_score(valid["y"], valid_scores), 3))
Output
1frozen rounds: 50 2rule validation auc: 0.731 3boosted validation auc: 0.792

The boosted model improves April AUC from 0.731 to 0.792. Area under the receiver operating characteristic curve (AUC) summarizes ranking quality across thresholds. It doesn't choose an operations policy.

Measure threshold cost, not AUC alone

The classifier outputs an SLA-risk score. Operations needs a choice: intervene, notify, or leave the job on its normal path. A missed SLA on priority jobs may be more expensive than an unnecessary proactive notification.

Search candidate thresholds on April only. The sort key minimizes declared cost and uses lower threshold as tie-breaker.

choose-sla-threshold.py
1thresholds = [0.15, 0.20, 0.25, 0.30, 0.40] 2for threshold in thresholds: 3 stats = cost_stats(valid, valid_scores, threshold) 4 print( 5 f"threshold={threshold:.2f} cost={stats['cost']} " 6 f"priority_misses={stats['priority_misses']}" 7 ) 8 9selected_threshold = min( 10 thresholds, 11 key=lambda threshold: (cost_stats(valid, valid_scores, threshold)["cost"], threshold), 12) 13print("selected threshold:", selected_threshold)
Output
1threshold=0.15 cost=1316 priority_misses=6 2threshold=0.20 cost=1884 priority_misses=10 3threshold=0.25 cost=1912 priority_misses=10 4threshold=0.30 cost=2038 priority_misses=11 5threshold=0.40 cost=2604 priority_misses=14 6selected threshold: 0.15

The selected threshold is 0.15, much lower than the common default of 0.50. Missing a priority SLA miss is expensive in this policy, so the model accepts more false alarms. If alert fatigue mattered more, the cost table would need another term.

Now freeze the round count, threshold, and cost policy. May can be opened once for final candidate evidence.

score-frozen-test-month.py
1test_scores = candidate.predict_proba(test["X"])[:, 1] 2test_stats = cost_stats(test, test_scores, selected_threshold) 3 4print("test auc:", round(roc_auc_score(test["y"], test_scores), 3)) 5print("test policy:", test_stats)
Output
1test auc: 0.882 2test policy: {'threshold': 0.15, 'cost': 728, 'priority_misses': 2, 'tp': 50, 'fp': 31, 'fn': 5, 'tn': 54}

The holdout result is useful, but aggregate AUC and cost can still hide a serious workload failure.

Gate slices before promotion

Pick required slices before reading test results. A slice gate also needs enough positive cases to mean anything: perfect recall on one SLA-miss job is weak evidence, and an empty slice can't produce recall at all. This lab requires at least 10 SLA-miss jobs per slice, recall of at least 0.90 for priority jobs, and recall of at least 0.85 for large-model jobs.

check-required-slices.py
1def slice_gate(split, scores, threshold, name, mask, minimum_recall, minimum_positives): 2 predicted = (scores >= threshold).astype(int) 3 stats = confusion(split["y"][mask], predicted[mask]) 4 positives = stats["tp"] + stats["fn"] 5 measured_recall = stats["tp"] / positives if positives else 0.0 6 enough_support = positives >= minimum_positives 7 passed = enough_support and measured_recall >= minimum_recall 8 result = { 9 "name": name, 10 "rows": int(np.sum(mask)), 11 "positives": positives, 12 "recall": round(measured_recall, 3), 13 "minimum_recall": minimum_recall, 14 "minimum_positives": minimum_positives, 15 "confusion": stats, 16 "passed": passed, 17 } 18 print( 19 f"{name}: positives={positives} recall={measured_recall:.3f} " 20 f"minimum_recall={minimum_recall:.2f} " 21 f"minimum_positives={minimum_positives} pass={passed}" 22 ) 23 return result 24 25priority_index = FEATURES.index("priority") 26large_model_index = FEATURES.index("large_model") 27required_slice_results = [ 28 slice_gate( 29 test, 30 test_scores, 31 selected_threshold, 32 "priority", 33 test["X"][:, priority_index] == 1, 34 minimum_recall=0.90, 35 minimum_positives=10, 36 ), 37 slice_gate( 38 test, 39 test_scores, 40 selected_threshold, 41 "large_model", 42 test["X"][:, large_model_index] == 1, 43 minimum_recall=0.85, 44 minimum_positives=10, 45 ), 46] 47required_slices_pass = all(result["passed"] for result in required_slice_results) 48print("all required slices pass:", required_slices_pass)
Output
1priority: positives=36 recall=0.944 minimum_recall=0.90 minimum_positives=10 pass=True 2large_model: positives=31 recall=0.903 minimum_recall=0.85 minimum_positives=10 pass=True 3all required slices pass: True

Both required slices pass for the frozen candidate. Keep the gate anyway. A future retrain, feature drift, or threshold edit can break one segment while aggregate metrics still look acceptable.

To see the failure symptom, test a careless threshold edit from 0.15 to 0.50.

reproduce-threshold-regression.py
1bad_threshold = 0.50 2bad_slice_results = [ 3 slice_gate( 4 test, 5 test_scores, 6 bad_threshold, 7 "priority", 8 test["X"][:, priority_index] == 1, 9 minimum_recall=0.90, 10 minimum_positives=10, 11 ), 12 slice_gate( 13 test, 14 test_scores, 15 bad_threshold, 16 "large_model", 17 test["X"][:, large_model_index] == 1, 18 minimum_recall=0.85, 19 minimum_positives=10, 20 ), 21] 22print("release blocked:", not all(result["passed"] for result in bad_slice_results))
Output
1priority: positives=36 recall=0.722 minimum_recall=0.90 minimum_positives=10 pass=False 2large_model: positives=31 recall=0.645 minimum_recall=0.85 minimum_positives=10 pass=False 3release blocked: True

Nothing about the fitted trees changed. The threshold edit alone drops priority recall from 0.944 to 0.722. Versioning only the model file would miss the regression.

Ship a model that can be operated

The candidate should export:

ArtifactWhy it matters
feature_contract.jsonproves column meanings and time boundary
split_manifest.jsonproves evaluation wasn't random or leaky
model.skopsversioned fitted scikit-learn model
threshold_policy.jsonturns score into action
slice_metrics.jsonrecords required-slice support and regressions
serving_schema.jsonvalidates incoming row shape

Create a compact receipt for the candidate. The real deployment bundle would serialize the fitted estimator with skops.io, inspect any untrusted types before loading it, and pin Python, scikit-learn, NumPy, SciPy, and skops versions. Loading a persisted estimator across scikit-learn versions isn't supported.[3]Reference 3Model persistencehttps://scikit-learn.org/stable/model_persistence.html It would also include the schema files named above.

publish-candidate-receipt.py
1receipt = { 2 "artifact": "sla-risk-candidate-v1", 3 "feature_contract": "point-in-time-snapshot-v1", 4 "model": { 5 "family": "gradient_boosted_trees", 6 "learning_rate": 0.05, 7 "max_depth": 2, 8 "rounds": best_round, 9 }, 10 "policy": { 11 "sla_risk_threshold": selected_threshold, 12 "costs": { 13 "false_alarm": 8, 14 "standard_miss": 60, 15 "priority_miss": 150, 16 }, 17 }, 18 "split_manifest": {"train": "Jan-Mar", "validation": "April", "test": "May"}, 19 "test_auc": round(roc_auc_score(test["y"], test_scores), 3), 20 "test_policy": test_stats, 21 "required_slice_metrics": required_slice_results, 22 "required_slices_pass": required_slices_pass, 23 "release_status": "candidate_for_shadow" if required_slices_pass else "blocked", 24} 25payload = json.dumps(receipt, sort_keys=True) 26print(json.dumps(receipt, indent=2, sort_keys=True)) 27print("receipt sha256:", sha256(payload.encode()).hexdigest()[:12])
Output
1{ 2 "artifact": "sla-risk-candidate-v1", 3 "feature_contract": "point-in-time-snapshot-v1", 4 "model": { 5 "family": "gradient_boosted_trees", 6 "learning_rate": 0.05, 7 "max_depth": 2, 8 "rounds": 50 9 }, 10 "policy": { 11 "costs": { 12 "false_alarm": 8, 13 "priority_miss": 150, 14 "standard_miss": 60 15 }, 16 "sla_risk_threshold": 0.15 17 }, 18 "release_status": "candidate_for_shadow", 19 "required_slice_metrics": [ 20 { 21 "confusion": { 22 "fn": 2, 23 "fp": 9, 24 "tn": 29, 25 "tp": 34 26 }, 27 "minimum_positives": 10, 28 "minimum_recall": 0.9, 29 "name": "priority", 30 "passed": true, 31 "positives": 36, 32 "recall": 0.944, 33 "rows": 74 34 }, 35 { 36 "confusion": { 37 "fn": 3, 38 "fp": 18, 39 "tn": 21, 40 "tp": 28 41 }, 42 "minimum_positives": 10, 43 "minimum_recall": 0.85, 44 "name": "large_model", 45 "passed": true, 46 "positives": 31, 47 "recall": 0.903, 48 "rows": 70 49 } 50 ], 51 "required_slices_pass": true, 52 "split_manifest": { 53 "test": "May", 54 "train": "Jan-Mar", 55 "validation": "April" 56 }, 57 "test_auc": 0.882, 58 "test_policy": { 59 "cost": 728, 60 "fn": 5, 61 "fp": 31, 62 "priority_misses": 2, 63 "threshold": 0.15, 64 "tn": 54, 65 "tp": 50 66 } 67} 68receipt sha256: fd121184d2ff

The receipt binds feature semantics, fitted-tree choices, threshold policy, split dates, measured holdout policy, and support-aware slice evidence. Shadow traffic and production monitoring still come next. Don't mutate the live policy in place when retraining changes those values.

Explain the candidate without looking back

Practice

  1. Change drift=0.18 for May to drift=0.80. Rebuild the fixture from the first cell and rerun the lab. Compare test AUC, cost, and slice gates.
  2. Add a false-alarm cost of 30 instead of 8. Predict whether selected threshold should move up or down, then verify it.
  3. Raise max_depth from 2 to 5 in both fitted models. Compare best round and April loss. Explain why more complex trees need fresh evidence.
  4. Add a required slice for standard jobs with minimum recall 0.75. Decide whether candidate still passes.
  5. Remove threshold from receipt. Explain why rollback is no longer reproducible.
  6. Raise minimum_positives to 40. Confirm required slices fail closed when holdout evidence is too thin.

What strong answers show

EvidenceWhat a strong answer shows
baseline disciplinecompares boosted trees with a declared operational baseline on future holdout data
boosting mechanicsdistinguishes regression residual corrections from classification loss gradients
early stoppingrecords monitored validation month, metric, stage cap, and selected round
decision policyconverts risk scores into thresholded actions with explicit costs
slice safetyevaluates support-aware workloads, workload classes, and job classes before release

When promotion breaks

SymptomCauseFix
Great validation result, poor next monthrandom or stale splitevaluate on later jobs
Training keeps improving while April loss worsenstoo many boosting roundsstop on later validation loss and record selected round
Retraining changes interventions unexpectedlythreshold wasn't versioned with modelpublish one scoring bundle
Average recall passes while premium jobs failno required-slice gategate priority and critical job classes
Slice recall looks perfect on one SLA-miss jobslice gate ignored supportrequire minimum positive cases or report insufficient evidence
Complete the lesson

Mastery Check

Answer every question, then check your score. Score above 75% to mark this lesson complete.

1.An SLA-risk classifier has labeled jobs from January through May. To avoid leakage, features must reflect only values known at each job's decision time, model choices must be frozen before final holdout scoring, and linked rows from the same incident should not be split across holdouts. Which split plan provides valid promotion evidence?
2.After training a boosted classifier, validation AUC is higher than the scan-age rule. What comparison still has to be made before treating the booster as useful for interventions?
3.Suppose a regression toy example has actual delays [2, 8, 20] and first predictions [6, 6, 6], so its squared-error residuals are [-4, 2, 14]. An SLA-risk classifier instead uses binary log loss and starts three rows at probability 0.50 with labels [0, 1, 1]. What correction target direction does the classifier use at that initial stage?
4.A round search trains 160 boosting stages with fixed depth and learning rate. April validation log loss is 0.6805 at round 1, 0.5643 at round 50, and 0.5922 at round 160. May has not been opened yet. What should be recorded for the frozen candidate before final test scoring?
5.April threshold search for the frozen booster reports costs: 0.15 -> 1316, 0.20 -> 1884, 0.25 -> 1912, 0.30 -> 2038, 0.40 -> 2604. The policy minimizes declared cost and breaks ties toward the lower threshold. What should be frozen before opening May?
6.The frozen candidate has acceptable aggregate test cost. A required large-model slice has 9 SLA-miss jobs, catches all 9, and the gate requires recall at least 0.85 with at least 10 positive cases. What should the gate report?
7.A team saves only model.skops for an SLA-risk model and later needs to reproduce why a candidate was allowed into shadow traffic. Which missing bundle evidence is most important for reproducible operations?

7 questions remaining.

Next Step
Continue to Ranking and Recommendation Systems

You can now promote a tabular risk model only with operational evidence. Next, predict an ordering of documents, where exposure and feedback change the data you later train on.

PreviousBatch and Streaming Feature Pipelines
Share this article
XFacebookLinkedInBlueskyRedditHacker NewsEmail
References

Greedy Function Approximation: A Gradient Boosting Machine

Friedman, J. H. · 2001 · The Annals of Statistics

XGBoost: A Scalable Tree Boosting System.

Chen, T. & Guestrin, C. · 2016 · KDD 2016

Model persistence

scikit-learn developers · 2026 · scikit-learn User Guide

Discussion

Questions and insights from fellow learners.

Discussion loads when you reach this section.