Debugging ML Models in Python: 5 Real Failure Cases

Debugging ML models is a different sport from debugging normal code. Your Python never throws an exception, the pipeline runs green, and yet the model is quietly wrong. This tutorial walks through five real production failures the way a senior reviewer would, each one shown breaking on tested code and then fixed: training loss that diverges, a 99% accuracy that is a lie, a model that shines offline and falls apart online, label leakage caught after deploy, and a loss that turns to NaN.

“The code ran fine. That is exactly the problem. A model can be perfectly executable and completely wrong at the same time.”

Every ML code review, eventually

Last Updated: July 2026 | Tested on: Python 3.14.6, scikit-learn 1.9.0, numpy 2.4.6 | Difficulty: Advanced | Reading Time: 21 minutes

📋 Prerequisites:

Most ML bugs never announce themselves. There is no red stack trace, no failing assertion, just a number that is confidently wrong. That is why experienced practitioners approach debugging ML models in a fixed order instead of poking at the neural network first. The order matters because the cheapest bugs to fix, and the most common ones, live in the data and the split long before they live in the model. Everything below was run on Python 3.14.6 with scikit-learn 1.9.0 (the latest stable at the time of writing), so every number you see is the real output from the scripts, not a screenshot.

The Debugging Order: Data, Splits, Model, Serving

Imagine a restaurant where a dish keeps coming back to the kitchen. A good head chef does not immediately blame the cook. First they check the ingredients, then the recipe card, then the cooking, and only last the plating on the way out. Debugging a model works the same way, and jumping straight to the model is the rookie move. Bad data ruins any model, a leaky split fakes any score, and a serving mismatch wastes a perfect model at the last step. So you check in that order, top to bottom, and stop at the first thing that is broken.

Model behaving badlywork through these in order1. DATA dirty?NaN, inf, bad rangesfix: drop or clip, schemagate2. SPLIT leaky?target hides in a featurefix: drop leaky cols, splitfirst3. TRAINING diverging?loss explodes or goes NaNfix: lower LR, scale inputs4. METRIC lying?99% on imbalanced datafix: balanced acc, recall5. SERVING skewed?preprocessing mismatchfix: one shared pipelineAll five pass:trust it, then monitor driftThe ML Debugging Decision Tree: Data First, Then Splits, Model, Serving

Follow the tree from the top. Ask whether the data is clean before anything else, because NaN and impossible values poison everything downstream. Then ask whether the split is honest, because a leaked column makes even a broken model look brilliant. Only after those pass do you look at training stability, then whether the metric you are reading actually means what you think, and finally whether serving preprocesses inputs the same way training did. The five cases below are those five checkpoints, in order.

Case 1: Training Loss Diverges

A junior engineer named Aviraj messages you at midnight: “my loss is going up, not down.” This is the most common training bug there is, and nine times out of ten the learning rate is too high. Picture rolling a ball down a valley to find the bottom. A sensible step size walks it down smoothly. Too big a step and the ball overshoots the bottom, lands higher up the far wall, overshoots again, and flies out of the valley entirely. That is a diverging loss. Here is the same tiny linear model trained twice, once with a reckless rate and once with a sane one.

📄 case1_diverge.py: the same model, two learning rates

import numpy as np

rng = np.random.default_rng(0)
# A tiny linear regression: y = 3x + 2, learned by gradient descent.
X = rng.normal(0, 1, (200, 1))
y = 3.0 * X[:, 0] + 2.0 + rng.normal(0, 0.1, 200)

def train(lr, steps=8):
    w, b = 0.0, 0.0
    losses = []
    for _ in range(steps):
        pred = w * X[:, 0] + b
        err = pred - y
        loss = np.mean(err ** 2)
        losses.append(loss)
        grad_w = 2 * np.mean(err * X[:, 0])
        grad_b = 2 * np.mean(err)
        w -= lr * grad_w
        b -= lr * grad_b
    return losses

print("step | lr=1.5 (too high) | lr=0.1 (sane)")
print("-----|-------------------|--------------")
hot = train(1.5)
ok = train(0.1)
for i, (h, o) in enumerate(zip(hot, ok)):
    hs = f"{h:.3e}" if np.isfinite(h) else "nan/inf"
    print(f"  {i:>2} | {hs:>17} | {o:>12.4f}")

▶ Output

step | lr=1.5 (too high) | lr=0.1 (sane)
-----|-------------------|--------------
   0 |         1.244e+01 |      12.4360
   1 |         4.456e+01 |       8.1106
   2 |         1.624e+02 |       5.2931
   3 |         6.014e+02 |       3.4570
   4 |         2.259e+03 |       2.2600
   5 |         8.593e+03 |       1.4794
   6 |         3.305e+04 |       0.9700
   7 |         1.283e+05 |       0.6375

What happened here: Both runs start at the exact same loss of 12.4. Then they part ways completely. The reckless rate multiplies the loss by roughly four every single step, from 12 to 128,000 and climbing, because each update overshoots and lands further out than before. The sane rate glides down toward zero. The fix is almost always one of three things: lower the learning rate, scale your input features so no single one dominates the gradient, or clip the gradients to a maximum size. Try the smaller rate first, it costs nothing and solves this the vast majority of the time.

Case 2: A 99% Accuracy That Is a Lie

A data scientist named Anvi proudly reports a fraud detector with 99% accuracy. Before celebrating, ask one question: what fraction of the data is actually fraud? If fraud is 1% of transactions, a model that says “not fraud” to literally everything scores 99% while catching zero criminals. Think of a smoke alarm that never goes off. It is “correct” 99% of the days when there is no fire, and utterly useless on the one day that matters. Accuracy on imbalanced data is that broken alarm.

📄 case2_imbalance.py: accuracy versus honest metrics

import numpy as np
from sklearn.dummy import DummyClassifier
from sklearn.metrics import accuracy_score, balanced_accuracy_score, recall_score, confusion_matrix

rng = np.random.default_rng(7)
# 10,000 transactions, only 1% are fraud (the positive class we care about).
n = 10_000
y = (rng.random(n) < 0.01).astype(int)
print(f"fraud rate: {y.mean():.2%}  ({y.sum()} of {n})")

# The "model" that cheats: always predict 'not fraud'.
lazy = DummyClassifier(strategy="most_frequent").fit(np.zeros((n, 1)), y)
pred = lazy.predict(np.zeros((n, 1)))

print(f"\naccuracy           : {accuracy_score(y, pred):.4f}")
print(f"balanced accuracy  : {balanced_accuracy_score(y, pred):.4f}")
print(f"recall on fraud    : {recall_score(y, pred, zero_division=0):.4f}")
tn, fp, fn, tp = confusion_matrix(y, pred).ravel()
print(f"\nconfusion matrix: caught {tp} frauds, missed {fn}")

▶ Output

fraud rate: 0.98%  (98 of 10000)

accuracy           : 0.9902
balanced accuracy  : 0.5000
recall on fraud    : 0.0000

confusion matrix: caught 0 frauds, missed 98

What happened here: The accuracy reads a glorious 0.9902, and it is completely worthless. Balanced accuracy, which averages the hit rate on each class separately, tells the truth at exactly 0.5000, the score of random guessing. Recall on the fraud class is a flat zero: the model caught 0 of 98 frauds. This is the same imbalance trap from the Kaggle project tutorial. On any imbalanced problem, throw accuracy out and read balanced accuracy, recall, precision, and the confusion matrix instead. The confusion matrix in particular cannot lie to you, it just shows the raw counts.

Case 3: Great Offline, Broken Online

This one ruins careers because it passes every test you wrote. The model scores 79% in your notebook, you ship it, and in production it performs like a coin flip. The culprit is train-serving skew: the preprocessing at serve time does not match the preprocessing at train time. A classic version is scaling. You fit a scaler on training data, train on the scaled numbers, then the serving code forgets to apply that scaler and feeds raw values straight to a model that has never seen raw values. Think of a recipe written in grams that someone cooks reading the numbers as ounces. Same numbers, wrong meaning, ruined dish.

📄 case3_skew.py: the serving code forgot the scaler

import numpy as np
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

X, y = make_classification(n_samples=4000, n_features=8, n_informative=5,
                           random_state=1, scale=100.0)  # features live on a large scale
X = X + 500.0   # and sit far from zero, e.g. raw prices in rupees
Xtr, Xte, ytr, yte = train_test_split(X, y, test_size=0.3, random_state=1)

scaler = StandardScaler().fit(Xtr)
model = LogisticRegression(max_iter=1000).fit(scaler.transform(Xtr), ytr)

# Offline eval: preprocess the test set the SAME way as training. Looks great.
offline = accuracy_score(yte, model.predict(scaler.transform(Xte)))

# Serving bug: the API forgot the scaler and fed raw features to the model.
serving_buggy = accuracy_score(yte, model.predict(Xte))

# The fix: serve through the exact same scaler used at train time.
serving_fixed = accuracy_score(yte, model.predict(scaler.transform(Xte)))

print(f"offline accuracy (scaled)        : {offline:.4f}")
print(f"online accuracy (raw, skewed)    : {serving_buggy:.4f}")
print(f"online accuracy (scaler applied) : {serving_fixed:.4f}")

▶ Output

offline accuracy (scaled)        : 0.7900
online accuracy (raw, skewed)    : 0.5017
online accuracy (scaler applied) : 0.7900

What happened here: Offline the model hits 0.7900. The moment serving forgets the scaler and passes raw values, accuracy collapses to 0.5017, indistinguishable from guessing, even though it is the exact same trained model on the exact same test rows. Re-apply the scaler and it snaps right back to 0.7900. The permanent fix is to never let preprocessing and the model drift apart: wrap them together in a single scikit-learn Pipeline and serialise that whole object, so the transform and the model always travel as one unit. If training and serving live in different codebases, the golden rule is that they must share one preprocessing function, not two copies that slowly diverge.

Case 4: Label Leakage Found After Deploy

Label leakage is the bug that feels like a gift and turns out to be a curse. Your cross-validation accuracy is suspiciously high, everyone is thrilled, and then the model is useless the day it goes live. What happened is that a feature secretly contained the answer. Say Aditi builds a churn model and one column, account_closed_date, only ever gets filled in after a customer has already churned. During training that column is a perfect crystal ball. In production, at the moment you need a prediction, that column is empty, because the customer has not churned yet. The model was leaning on information it will never have at prediction time.

📄 case4_leakage.py: a column that leaks the target

import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import cross_val_score

rng = np.random.default_rng(3)
n = 3000
# A churn dataset. 'tenure' and 'monthly_spend' are honest signals.
tenure = rng.integers(1, 60, n)
monthly_spend = rng.normal(800, 200, n)
churn = ((tenure < 12) & (monthly_spend < 850) | (rng.random(n) < 0.15)).astype(int)

# The trap: 'account_closed_date' only gets filled AFTER a customer churns.
# It leaks the answer. In training data it is a near-perfect proxy for the label.
account_closed = np.where(churn == 1, rng.integers(1, 30, n), 0)

df = pd.DataFrame({"tenure": tenure, "monthly_spend": monthly_spend,
                   "account_closed_date": account_closed, "churn": churn})

leaky_cols = ["tenure", "monthly_spend", "account_closed_date"]
clean_cols = ["tenure", "monthly_spend"]
model = RandomForestClassifier(n_estimators=100, random_state=0)

leaky = cross_val_score(model, df[leaky_cols], df["churn"], cv=5).mean()
clean = cross_val_score(model, df[clean_cols], df["churn"], cv=5).mean()

print(f"CV accuracy WITH account_closed_date : {leaky:.4f}   <-- too good to be true")
print(f"CV accuracy WITHOUT the leaky column : {clean:.4f}   <-- the honest number")

model.fit(df[leaky_cols], df["churn"])
imp = sorted(zip(leaky_cols, model.feature_importances_), key=lambda t: -t[1])
print("\nfeature importance (the smoking gun):")
for name, val in imp:
    print(f"  {name:<20} {val:.3f}")

▶ Output

CV accuracy WITH account_closed_date : 1.0000   <-- too good to be true
CV accuracy WITHOUT the leaky column : 0.8197   <-- the honest number

feature importance (the smoking gun):
  account_closed_date  0.814
  tenure               0.129
  monthly_spend        0.057

What happened here: With the leaky column in, cross-validation returns a flawless 1.0000. That perfect score is the alarm bell, not the trophy. Drop the column and the honest accuracy is 0.8197, which is what production will actually deliver. The feature importance is the smoking gun: account_closed_date carries 0.814 of the model’s decision, dwarfing the real signals. Two habits catch this before it ships. First, treat any near-perfect score as guilty until proven innocent. Second, for every top feature ask one question: “would I actually know this value at the exact moment I need a prediction?” If the answer is no, it is a leak, and it goes.

Case 5: NaN Loss From Bad Rows

You start training and the loss prints nan on the first step. Everything grinds to a halt because in floating point, once a NaN appears it spreads to every calculation it touches, like a drop of ink in water. There are two usual sources. A missing value in the data, an empty cell that pandas reads as NaN, or a numeric overflow where a value so large gets squared that it blows past the biggest number a float can hold and becomes infinity. A real dataset exported from a messy spreadsheet often has both. Watch how one missing age and one absurd salary poison the entire loss.

📄 case5_nan.py: two poison pills in a dirty CSV

import numpy as np
import pandas as pd

rng = np.random.default_rng(11)
n = 1000
df = pd.DataFrame({
    "age": rng.normal(35, 8, n),
    "salary": rng.normal(60000, 15000, n),
})
y = (df["salary"] > 60000).astype(float).values

# Two poison pills that sneak in from a dirty CSV:
df.loc[10, "age"] = np.nan          # a missing value
df.loc[20, "salary"] = 1e308        # an absurd outlier (data-entry error)

def mse_step(X, y):
    w = np.array([0.01, 0.01])
    pred = X @ w
    return float(np.mean((pred - y) ** 2))

X = df[["age", "salary"]].values
print(f"loss on raw data     : {mse_step(X, y)}")
print(f"  NaN rows in X       : {int(np.isnan(X).any(axis=1).sum())}")
print(f"  inf/huge values     : {int((np.abs(X) > 1e12).sum())}")

# The gate: drop NaN rows, clip absurd values, then train.
clean = df.copy()
clean = clean.dropna()
clean["salary"] = clean["salary"].clip(upper=500000)
yc = y[clean.index]
Xc = clean[["age", "salary"]].values
print(f"\nrows after cleaning  : {len(clean)}  (dropped {n - len(clean)})")
print(f"loss on clean data   : {mse_step(Xc, yc):.4f}")

▶ Output

case5_nan.py:19: RuntimeWarning: overflow encountered in square
  return float(np.mean((pred - y) ** 2))
loss on raw data     : nan
  NaN rows in X       : 1
  inf/huge values     : 1

rows after cleaning  : 999  (dropped 1)
loss on clean data   : 405678.1497

What happened here: The raw loss is nan, and numpy even hands you the clue for free: “overflow encountered in square”, pointing straight at the giant salary being squared past the float ceiling. The counts confirm exactly one NaN row and one absurd value. After dropping the missing row and clipping salaries to a sane ceiling, the loss becomes a real finite number again. Note that number is large only because the features are unscaled, which is the very next thing you would fix, but it is finite, and that is the point.

The lesson: before you ever call fit, run df.isna().sum() and df.describe(), so bad rows get caught at the door instead of inside your loss function.

The Diagnostic Toolkit

Across all five cases, debugging ML models comes down to a handful of tools that do most of the work. Keep these four in reach and you will diagnose most model bugs quickly.

  • Learning curves: plot training and validation loss against epochs or data size. Diverging up means the learning rate is too high, a wide gap between the two means overfitting, both flat and high means underfitting.
  • Per-slice metrics: never trust a single overall number. Break accuracy down by segment (new users versus old, region by region) so a model that is great on average but terrible for one group cannot hide.
  • Prediction histograms: plot the distribution of predicted probabilities. If every prediction clusters at 0.5, or they all pile at one extreme, the model is not really deciding anything.
  • An input schema gate: validate every incoming batch against expected types and ranges before it reaches the model, and reject the bad rows loudly.

That last one is worth code, because it is your best defence against Case 5 and Case 3 in production. Libraries like pandera or Great Expectations do this for you, but the idea is simple enough to see in plain Python: declare what clean input looks like, then check each batch against it.

📄 case6_schema.py: a validation gate for incoming data

import pandas as pd

# A validation gate: describe what CLEAN input must look like, then check it.
# (pandera does this for you; here it is plain Python so you see the idea.)
SCHEMA = {
    "age":    {"dtype": "float", "min": 0, "max": 120,       "nullable": False},
    "salary": {"dtype": "float", "min": 0, "max": 1_000_000, "nullable": False},
}

def validate(df):
    problems = []
    for col, rule in SCHEMA.items():
        if col not in df.columns:
            problems.append(f"{col}: missing column"); continue
        s = df[col]
        if not rule["nullable"] and s.isna().any():
            problems.append(f"{col}: {int(s.isna().sum())} null values")
        below = (s < rule["min"]).sum()
        above = (s > rule["max"]).sum()
        if below: problems.append(f"{col}: {int(below)} values below {rule['min']}")
        if above: problems.append(f"{col}: {int(above)} values above {rule['max']}")
    return problems

batch = pd.DataFrame({"age": [34.0, -3.0, 41.0, None],
                      "salary": [55000.0, 62000.0, 9e9, 48000.0]})
issues = validate(batch)
print("schema check on incoming batch:")
if issues:
    for p in issues:
        print(f"  REJECT -> {p}")
else:
    print("  all rows pass")

▶ Output

schema check on incoming batch:
  REJECT -> age: 1 null values
  REJECT -> age: 1 values below 0
  REJECT -> salary: 1 values above 1000000

What happened here: The gate caught three problems in a four-row batch: a null age, a negative age of minus three which is physically impossible, and a salary of nine billion. Each is a row you never want reaching your model. Put a gate like this at the front of both your training pipeline and your serving endpoint, and the messy real world stops leaking into your maths. This is the same instinct as validating a web form before you save it, applied to model inputs.

Reproducibility: Killing Works on My Notebook

Half of debugging ML models is being able to reproduce the bug at all. A colleague named Anvay gets 91% accuracy, you run the “same” notebook and get 86%, and now you are debugging the gap between two machines instead of the actual model. Three disciplines close that gap and make every result you report trustworthy.

  • Seed everything. Set a fixed random seed for numpy, for your framework, and for the data split. Notice every script in this post used a fixed seed via default_rng or random_state, which is exactly why the numbers you see match the numbers I got.
  • Version the data, not just the code. A model is the code plus the exact data it trained on. “The customers table” is not a version. A dated snapshot or a hash of the file is, so you can always rebuild the precise dataset a model learned from.
  • Capture the environment. Pin your library versions in a requirements.txt or lockfile. The difference between scikit-learn 1.7 and 1.9 can quietly change a default and shift your metric. Record what ran.

The post-mortem on almost every “works on my notebook” mystery lands on one of those three: an unseeded random step, a dataset that changed underneath someone, or a library that was a different version. Nail all three and your results become boringly repeatable, which is precisely what you want. This is the foundation the model drift tutorial builds on when it freezes a reference dataset to compare against later.

The 15-Point Pre-Deploy Sanity Checklist

Run through this list before any model goes to production. It is ordered the same way you debug: data, split, model, metric, serving. Print it, stick it next to your monitor, and do not skip a line.

  1. No NaN or infinite values remain in the training features.
  2. Every feature sits inside a sane, documented range.
  3. A schema gate validates inputs at both train and serve time.
  4. The train, validation, and test sets do not share any rows.
  5. Every feature is available at prediction time (no leakage).
  6. Any near-perfect score has been investigated and explained.
  7. Preprocessing was fit on the training set only, never the full data.
  8. The learning curve shows loss going down and then leveling off.
  9. The metric fits the problem (not raw accuracy on imbalanced data).
  10. Per-slice metrics were checked, not just the overall number.
  11. The model beats a trivial baseline by a meaningful margin.
  12. Serving uses the exact same preprocessing object as training.
  13. Random seeds are fixed and the run is reproducible.
  14. Data version and library versions are recorded for this model.
  15. Prediction logging is on, ready for drift monitoring after launch.

Common Mistakes

⚠️ Common Mistakes:
  • Blaming the model first. The model is the last suspect, not the first. Check the data and the split before you touch a single hyperparameter.
  • Trusting a single accuracy number. One overall metric hides imbalance and hides a bad slice. Always read the confusion matrix and per-group metrics.
  • Celebrating a perfect score. A CV accuracy of 1.0 is almost always leakage, not genius. Treat it as a bug until you prove otherwise.
  • Fitting preprocessing on all the data. Fitting a scaler or encoder before the split leaks test information into training and inflates your score.
  • Two copies of the preprocessing. When training and serving each have their own preprocessing code, they drift apart and you get skew. Ship one shared pipeline.

Best Practices

  • Debug in a fixed order. Data, then splits, then model, then serving. Stop at the first broken checkpoint instead of guessing.
  • Bundle preprocessing and model together. A single serialised Pipeline makes train-serving skew almost impossible by construction.
  • Interrogate every top feature. For each important feature, confirm you would truly have its value at prediction time. That one question kills most leakage.
  • Prefer honest metrics. Balanced accuracy, recall, precision, and PR-AUC on imbalanced problems, never bare accuracy.
  • Make everything reproducible. Fixed seeds, versioned data, pinned libraries. A bug you cannot reproduce is a bug you cannot fix.

Conclusion

You just worked through five failures that no stack trace would ever show you: a loss diverging from a hot learning rate, a 99% accuracy that caught zero fraud, a model that scored 79% offline and 50% online from a forgotten scaler, a perfect 1.0 that was pure leakage, and a NaN loss from one bad row. The thread tying them together is the debugging order, data before splits before model before serving, and a habit of distrusting numbers that look too good.

Master that and you stop being the person who ships a broken model with a beautiful metric. Debugging ML models is an evergreen skill because the failure modes repeat: the library names will change, but diverging loss, imbalance, skew, leakage, and NaN will outlive every framework. Once your model is honest and deployed, the next job is keeping it honest, which is exactly the model drift tutorial. Want the full path? Browse the complete Python + AI/ML tutorial series home.

Frequently Asked Questions

What order should I debug an ML model in?

Go top down: data first, then the split, then the model, then serving. Bad data ruins any model and a leaked split fakes any score, so those are cheaper and more common bugs than anything in the model itself. Check each checkpoint in turn and stop at the first one that is broken. Jumping straight to tuning the model is the most common way to waste a day.

My cross-validation accuracy is 100%. Is that good?

Almost never. A perfect or near-perfect score is the single strongest signal of label leakage, where a feature secretly carries the answer. Check feature importance for one column that dominates, and ask whether you would actually have that value at prediction time. If not, remove it and the honest score appears.

Why does my training loss go up instead of down?

The learning rate is too high in the vast majority of cases. Each gradient step overshoots the minimum and lands further away, so the loss grows every step and can reach infinity or NaN. Lower the learning rate first, then scale your input features, then consider gradient clipping. These fixes cost nothing and solve most divergence.

My model is great offline but bad in production. Why?

The usual cause is train-serving skew: serving preprocesses inputs differently than training did, for example forgetting to apply the scaler that training used. The model then receives values it never saw. Fix it by bundling preprocessing and the model into one pipeline object that both training and serving share, so they cannot drift apart.

What causes a NaN loss and how do I fix it?

Usually a NaN or infinite value in the data, or a numeric overflow where a huge value is squared past the float limit. NaN then spreads to every downstream calculation. Before training, run df.isna().sum() and df.describe(), drop or impute missing rows, and clip absurd outliers. A schema validation gate at the input stops these at the door.

Interview Questions on Debugging ML Models

Scenario questions, not trivia: this is the form this topic takes in a real interview.

Q: A model has 98% accuracy but the business says it is useless. What do you check first?

Class balance. If the positive class is rare, a model that always predicts the majority scores high accuracy while catching none of the cases that matter. I would pull the confusion matrix, then read balanced accuracy, recall, and precision on the minority class. Accuracy is the wrong metric for imbalanced problems, and the confusion matrix shows the raw truth of what is being caught and missed.

Q: Your cross-validation score is 0.99 but production performance is poor. What is the likely cause?

Label leakage. A feature in training carries information that will not exist at prediction time, so cross-validation sees the answer and production does not. I would inspect feature importance for a single column that dominates, and for each strong feature ask whether its value is genuinely available at the moment of prediction. Removing the leaky column reveals the real score.

Q: How do you tell data drift apart from train-serving skew?

Drift is a change over time: the world moves and the live data slowly stops matching the training data. Skew is a mismatch at a single point in time between how training and serving process the same input, for example a scaler applied in one and not the other. Skew is usually there from day one and shows an instant offline-online gap, while drift appears gradually after launch. Different clocks, different fixes.

Q: Training loss becomes NaN after a few steps. Walk me through diagnosing it.

First I check the data for NaN or infinite values and impossible magnitudes, since one bad row poisons the whole loss. Then I look at the learning rate, because too high a rate makes the loss explode toward infinity, which becomes NaN. So the diagnosis is: scan and clean the inputs, add a schema gate, and if the data is clean, lower the learning rate and scale the features. numpy’s overflow warning usually points straight at the offending operation.

Q: What makes an ML result reproducible?

Three things: a fixed random seed for every random step including the data split, a versioned snapshot of the exact training data, and pinned library versions. A model is the code plus the data plus the environment. If any of those three floats, two people running the “same” notebook get different numbers, and you end up debugging the machine instead of the model.

Go deeper: when you outgrow this post, the official Python documentation is the next stop.

Previous: Model Drift in Python: Detect and Fix Decaying ML Models

Next: ML: Best Practices, the Ethics and Catches Nobody Warns You About

Series Home: Python + AI/ML Tutorial Series

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *