Type at least 2 letters. Enter opens the first result, Esc closes.

    MLOps · Lesson 2 · Reproducible runs and tested models

    Testing ML systems: unit, data, behavioural, slice and regression tests

    Intermediate 60 min 4 interactive figures 3 videos Colab notebook

    Before this lesson: lesson 01 (data contracts, reproducible runs); evaluation metrics and data leakage; logistic regression; the binomial distribution (common distributions) and hypothesis testing. Time: about 60 minutes, plus the notebook.


    Why this matters #

    A model can pass every code test and still be wrong. A feature computed with a future timestamp, a model that uses the reviewer's name instead of the review text, or a new version that is better on average but worse in the mountains: none of these raises an error. The tests of this lesson find them. They become the release gate in CI (lesson 03) and in the model registry (lesson 06). LLM eval suites (lesson 09) use the same ideas. "How would you test this model?" is also a standard interview question.

    1. Predict first #

    2. The picture #

    Two kinds of correctness. Code correctness is exact. A function gets an input and must return a known output, so the test is assert f(x) == y. Learned behaviour is statistical. You cannot write down the right output for every input. So you test properties (relations between inputs and outputs) with tolerances, and rates (the share of cases that fail) over many cases.

    The simplest model. Take a logistic model of the probability of rain in the next hour,

    p(x)=σ(whh+wTT+b),p(x) = \sigma(w_h h + w_T T + b),

    where hh is the relative humidity, TT is the temperature, σ(z)=1/(1+e−z)\sigma(z) = 1/(1 + e^{-z}) is the sigmoid, and wh,wT,bw_h, w_T, b are learned. A directional test says: "more humidity must not lower the probability of rain". The sigmoid is increasing, so pp grows with hh exactly when wh≥0w_h \ge 0. For this model the property holds for every input if one number has the right sign. One check proves a global property.

    The general case. For a neural network ff, the same test computes f(x+δeh)−f(x)f(x + \delta e_h) - f(x) at the inputs you chose. Here ehe_h is the unit vector of the humidity feature and δ>0\delta > 0 is a step. The test says nothing about inputs you did not choose. This is the central limit of behavioural testing: a test samples the input space. It can find a violation (a counterexample). It cannot prove that the property holds everywhere.

    Interactive figure: loading…

    Draw a few suites of 20 random cases, then show where the test fails. The map is the model's probability of rain (darker blue = higher). Each case is a short arrow from (h,T)(h, T) to (h+5,T)(h + 5, T); it fails (magenta) when the probability drops by more than 0.01. The "network" has a small dip near h≈78%h \approx 78\%, T≈24T \approx 24 °C that covers about 2.4% of the inputs. A suite of 20 cases finds it only about 38% of the time. Switch to "logistic": there the sign of whw_h settles the question for all inputs.

    This picture decides where each test runs. Cheap, exact tests (code and data) run on every commit. Slower, statistical tests (behaviour and slices) run before a model is promoted to production.

    Quick check

    A logistic rain model has wh=0.05w_h = 0.05 and wT=−0.3w_T = -0.3. How many test cases do you need to show that more humidity never lowers its probability of rain?

    3. Definitions and main results #

    Test types #

    The examples are for the running example of this track: a rain nowcast that gives the probability of rain in the next hour at a weather station.

    Type What it checks Example When it runs
    code unit one function, exact output the Kelvin-to-Celsius feature function; the loss is minimal for a perfect forecast; output shapes every commit, seconds
    data the data contract and leakage the contract of lesson 01; no station in two splits; no feature timestamp after the label time; no duplicates between train and test every commit on a sample; before every training run on the full data
    model, before training the training code can learn the loss falls on a tiny batch; the model can overfit one batch; outputs are probabilities in [0,1][0, 1] every commit, about a minute
    model, behavioural learned capabilities MFT, INV and DIR tests (below) before promotion
    slice and regression candidate against production, overall and per slice accuracy per region, season and station type; no slice drops by more than δ\delta before promotion (the release gate)
    infrastructure the system around the model the model loads in the serving image; online and offline predictions agree on logged requests; latency and memory budgets; rollback works CI after the image build; canary

    A data test is a test of the data, not of code: it runs the checks of a data contract and looks for leakage. A pre-training test (for example "the model can overfit one batch of 8 examples") checks that the training code can learn at all. If a small network cannot reach 100% training accuracy on 8 points, the code has a bug: wrong labels, a learning rate of zero, or a gradient that does not reach the weights.

    Behavioural tests (CheckList) #

    Let f(x)∈[0,1]f(x) \in [0, 1] be the model's score, T(x)T(x) a set of perturbed copies of an input xx, and τ≥0\tau \ge 0 a tolerance. CheckList (Ribeiro et al., 2020) defines three test types.

    • Minimum functionality test (MFT): simple inputs xx with known labels yy, aimed at one capability. A case fails if the thresholded score differs from yy. It is a unit test for a capability. Example: "The food was not good." must be negative.
    • Invariance test (INV): TT is label-preserving (the right label does not change). A case fails if max⁡x′∈T(x)∣f(x′)−f(x)∣>τ\max_{x' \in T(x)} |f(x') - f(x)| > \tau. Example: replace the reviewer's name.
    • Directional expectation test (DIR): TT has a known effect direction d∈{+1,−1}d \in \{+1, -1\}. A case fails if d (f(x′)−f(x))<−τd\,(f(x') - f(x)) < -\tau for some x′∈T(x)x' \in T(x). Example: appending "The staff was rude." must not raise the positive score (d=−1d = -1).

    The failure rate of a test is (failing cases) / (tested cases). INV and DIR need no labels: they compare predictions with predictions. So they also run on unlabeled production inputs. MFT comes from software unit tests. INV and DIR come from metamorphic testing: you do not know the right output, but you know how the output must relate to the output of a changed input.

    How to design them, in five steps.

    1. List the capabilities the model must have. For sentiment: vocabulary, negation, named entities. For a weather model: the response to humidity, to pressure, to the time of day.
    2. Write each one as a relation between inputs and outputs: a fixed label (MFT), equality (INV) or an order (DIR).
    3. Write a template or a perturbation function that generates many cases.
    4. Check with domain knowledge that the perturbation really preserves the label or the direction. Swapping city names preserves the label of a restaurant review. For a weather model it does not: the location carries information.
    5. Choose τ\tau, a maximum failure rate, and whether a failure blocks a merge or only reports.

    What they can show: a concrete counterexample; a shortcut (a feature that predicts the label in the training data for a wrong reason: the score moves when only a name changes); a missing capability (negation). They treat the model as a black box, so they also compare models trained on different data. What they cannot show: that the property holds for all inputs; accuracy or calibration; failures that nobody thought of. And a test is only as valid as its perturbation.

    Quick check

    A sentiment model uses a bag of words: it counts each word and ignores the order. It has 95% test accuracy. What does it give for "the food was good, not bad" and "the food was bad, not good"?

    Quick check

    A rain model gets an input with the station's pressure lowered by 5 hPa (falling pressure usually brings rain). What kind of test is "the probability of rain must not go down"?

    Result: what a clean run shows (the rule of three) #

    Theorem (rule of three). If nn independent test cases all pass, the one-sided 95% upper confidence bound on the failure rate pp is

    pu=1−0.051/n≈3n.p_u = 1 - 0.05^{1/n} \approx \frac{3}{n}.

    Why this is true, first in words. If the model failed often, a clean run of nn cases would be very unlikely. The bound pup_u is the failure rate at which a clean run becomes too unlikely (less than 5%) to believe.

    Proof.

    1. If the true failure rate is pp and the cases are independent, then P(0 failures in n)=(1−p)nP(\text{0 failures in } n) = (1 - p)^n.
    2. The 95% upper bound pup_u is the largest pp for which this outcome still has probability at least 0.05: (1−pu)n=0.05(1 - p_u)^n = 0.05.
    3. Solve: 1−pu=0.051/n1 - p_u = 0.05^{1/n}, so pu=1−0.051/np_u = 1 - 0.05^{1/n}.
    4. Take logs: ln⁡(1−pu)=ln⁡(0.05)/n\ln(1 - p_u) = \ln(0.05)/n. For small pup_u, ln⁡(1−pu)≈−pu\ln(1 - p_u) \approx -p_u, so pu≈−ln⁡(0.05)/n=2.996/n≈3/np_u \approx -\ln(0.05)/n = 2.996/n \approx 3/n. ■\blacksquare

    So 300 passing cases show p≲1%p \lesssim 1\% (exactly 0.994%0.994\%), and 3,000 show p≲0.1%p \lesssim 0.1\%. The bound is about the distribution that generated the test cases. Cases from one template are not a sample of production inputs.

    Interactive figure: loading…

    Move nn and the true failure rate pp, then run 1,000 suites. The violet curve is the exact bound 1−0.051/n1 - 0.05^{1/n}; the dashed line is 3/n3/n. The two agree for n≥30n \ge 30. When the magenta line of pp lies above the bound, fewer than 5% of suites pass (blue squares). With n=300n = 300 and p=1%p = 1\%, about 4.9% of suites still pass: this is the 95% level at work.

    The exact (Clopper–Pearson) bound

    The rule of three is the special case k=0k = 0 of the exact one-sided Clopper–Pearson bound for a binomial proportion: with kk failures in nn cases, the 95% upper bound is the 0.95 quantile of Beta(k+1,n−k)\text{Beta}(k + 1, n - k). For k=0k = 0, the Beta$(1, n)$ distribution has CDF 1−(1−x)n1 - (1 - x)^n, so its 0.95 quantile solves (1−x)n=0.05(1 - x)^n = 0.05: the same pup_u. In SciPy: stats.beta.ppf(0.95, 1, n).

    Quick check

    An invariance suite of 1,000 independent cases has no failure. What can you claim, with 95% confidence?

    Slice and regression tests #

    A slice is a subset of rows defined by metadata: region, season, station type, sensor model, a rare class. A regression test scores the candidate model and the production model on the same frozen test set, overall and per slice. It flags a slice when the slice has at least nmin⁡n_{\min} rows and its metric drops by more than δ\delta. The ML Test Score paper (test Model 6) suggests two kinds of threshold: absolute ("error on slice xx below 5%") and incremental ("error on slice xx up by less than 1% against the previous model").

    Small slices are noisy. An accuracy aa measured on nn rows has standard error

    SE=a(1−a)/n,\mathrm{SE} = \sqrt{a(1 - a)/n},

    which is 0.0430.043 for a=0.75a = 0.75 and n=100n = 100. A drop of 3 points on 100 rows is less than one standard error.

    Pair the comparison. Both models are scored on the same rows, so most rows give the same result for both: easy rows are right for both, hard rows are wrong for both. These rows carry no information about the difference. McNemar's test uses only the rows where the models disagree, the discordant pairs: bb = old right and new wrong, cc = old wrong and new right. The drop in accuracy is (b−c)/n(b - c)/n. If the two models are equally accurate, each discordant row goes either way with probability 1/21/2, so c∼Binomial(b+c,1/2)c \sim \text{Binomial}(b + c, 1/2). The exact two-sided p-value is 2⋅P(X≤min⁡(b,c))2 \cdot P(X \le \min(b, c)) with X∼Binomial(b+c,1/2)X \sim \text{Binomial}(b + c, 1/2).

    Interactive figure: loading…

    Start with the worked example, then try the other presets. Blue bars are the old model, orange bars the new one; the aqua dashed line is the old alpine accuracy minus δ=0.02\delta = 0.02. Overall the new model wins, while the alpine slice loses 10 points. Move the slider "disagree (b + c)/n": the drop stays the same, but McNemar's p-value changes. Fewer disagreements mean a clearer verdict.

    Why pairing gives a smaller standard error

    Let Xi=1X_i = 1 if the old model is right on row ii, and Yi=1Y_i = 1 if the new one is. The drop is the mean of Di=Xi−YiD_i = X_i - Y_i. Then Var⁡(Di)=Var⁡(Xi)+Var⁡(Yi)−2Cov⁡(Xi,Yi)\operatorname{Var}(D_i) = \operatorname{Var}(X_i) + \operatorname{Var}(Y_i) - 2\operatorname{Cov}(X_i, Y_i). An unpaired test assumes Cov⁡=0\operatorname{Cov} = 0. But the two models are right on the same easy rows, so the covariance is large and positive, and the paired variance is much smaller. For the alpine slice of section 4, the paired standard error of the drop is ((b+c)/n−((b−c)/n)2)/n=0.041\sqrt{((b + c)/n - ((b - c)/n)^2)/n} = 0.041, while the unpaired one is 0.0610.061.

    The ML Test Score #

    Breck et al. (2017) list 28 tests in four sections of seven:

    # Features and data Model development ML infrastructure Monitoring
    1 feature expectations are captured in a schema model specs are reviewed and submitted training is reproducible dependency changes result in notification
    2 all features are beneficial offline and online metrics correlate model specs are unit tested data invariants hold for inputs
    3 no feature's cost is too much all hyperparameters have been tuned the ML pipeline is integration tested training and serving are not skewed
    4 features adhere to meta-level requirements the impact of model staleness is known model quality is validated before serving models are not too stale
    5 the data pipeline has appropriate privacy controls a simpler model is not better the model is debuggable models are numerically stable
    6 new features can be added quickly model quality is sufficient on important data slices models are canaried before serving computing performance has not regressed
    7 all input feature code is tested the model is tested for considerations of inclusion serving models can be rolled back prediction quality has not regressed

    Scoring. Each test gets 0.5 points if it is run manually with documented and shared results, and 1 point if a system runs it automatically and repeatedly. Sum the points per section. The final score is the minimum of the four section sums. The authors chose the minimum because all four areas matter: a team must cover each one to raise the score. Reading the final score (Table V of the paper):

    Score Meaning
    0 more of a research project than a production system
    (0,1](0, 1] possible serious holes in reliability
    (1,2](1, 2] a first pass at basic productionisation; more investment may be needed
    (2,3](2, 3] reasonably tested; more tests may be automated
    (3,5](3, 5] strong automated testing and monitoring, fit for mission-critical systems
    above 5 exceptional automated testing and monitoring

    Using it to find missing tests. Score each section. The lowest section is the bottleneck: work there first. Inside it, choose the tests whose failure would cost the most, and turn manual tests into automated ones (+0.5 each).

    Interactive figure: loading…

    Click tests to change their state and watch the final score. Each test cycles through not done, manual (0.5) and automated (1). The section with the lowest sum has a magenta frame: it alone sets the score. Raise any other section and nothing changes. Compare the final score with the mean.

    Quick check

    A system has the section sums data 3.5, model 2.0, infrastructure 4.0, monitoring 1.5. What is its ML Test Score, and where should the team invest first?

    Answers to section 1. (1) Yes. Section 4 shows a model that gains 1.7 points overall and loses 10 points on alpine stations. The ML Test Score paper gives a stronger example: global accuracy up by 1%, accuracy for one country down by 50%. (2) About 1%. The rule of three gives 1−0.051/300=0.994%≈3/3001 - 0.05^{1/300} = 0.994\% \approx 3/300. A failure rate below 1% is still possible after 300 clean cases.

    4. Worked example #

    (a) A slice regression. A rain classifier is tested on 900 lowland and 100 alpine cases.

    slice rows old accuracy new accuracy
    lowland 900 0.90 (810 correct) 0.93 (837 correct)
    alpine 100 0.80 (80 correct) 0.70 (70 correct)
    all 1,000 (810+80)/1000=0.890(810 + 80)/1000 = 0.890 (837+70)/1000=0.907(837 + 70)/1000 = 0.907

    Overall: +1.7 points. On alpine stations: −10 points. With nmin⁡=30n_{\min} = 30 and δ=0.02\delta = 0.02, the slice test blocks the release.

    Is the alpine drop real or noise? Build the paired table on the 100 alpine cases:

    new right new wrong
    old right 66 b=14b = 14
    old wrong c=4c = 4 16

    Check the margins: old 66+14=8066 + 14 = 80 ✓, new 66+4=7066 + 4 = 70 ✓, total 66+14+4+16=10066 + 14 + 4 + 16 = 100 ✓.

    1. Discordant rows: b+c=18b + c = 18. The drop is (b−c)/n=10/100=0.10(b - c)/n = 10/100 = 0.10 ✓.
    2. Under "equally accurate", c∼Binomial(18,1/2)c \sim \text{Binomial}(18, 1/2).
    3. P(X≤4)=((180)+(181)+(182)+(183)+(184))/218=(1+18+153+816+3060)/262144=4048/262144=0.0154P(X \le 4) = \big(\binom{18}{0} + \binom{18}{1} + \binom{18}{2} + \binom{18}{3} + \binom{18}{4}\big)/2^{18} = (1 + 18 + 153 + 816 + 3060)/262144 = 4048/262144 = 0.0154.
    4. Two-sided: p=2×0.0154=0.031p = 2 \times 0.0154 = 0.031. The drop is unlikely to be noise.

    An unpaired comparison of 0.80 and 0.70 gives SE=0.8⋅0.2/100+0.7⋅0.3/100=0.061\mathrm{SE} = \sqrt{0.8 \cdot 0.2/100 + 0.7 \cdot 0.3/100} = 0.061 and only z=0.10/0.061=1.64z = 0.10/0.061 = 1.64, because it ignores that 82 of the 100 rows agree.

    (b) An ML Test Score. A nowcasting system has:

    • data: schema checks, feature-code unit tests and privacy controls automated (3×13 \times 1); a feature-importance study and a feature-cost review done by hand (2×0.52 \times 0.5) → 4.0;
    • model: spec review and slice metrics automated (2×12 \times 1); tuning and a simple-baseline comparison done by hand (2×0.52 \times 0.5) → 3.0;
    • infrastructure: model specs unit tested (1); a manual canary and a documented rollback (2×0.52 \times 0.5) → 2.0;
    • monitoring: model staleness checked by hand each week (0.5) → 0.5.

    Final score: min⁡(4.0,3.0,2.0,0.5)=0.5\min(4.0, 3.0, 2.0, 0.5) = 0.5, "possible serious holes in reliability". Monitoring is the bottleneck. Automate Monitor 2 (run the lesson 01 contract on serving inputs) and Monitor 3 (recompute features offline for logged requests and compare). Monitoring becomes 0.5+1+1=2.50.5 + 1 + 1 = 2.5, and the final score becomes min⁡(4.0,3.0,2.0,2.5)=2.0\min(4.0, 3.0, 2.0, 2.5) = 2.0. Now infrastructure is the bottleneck: automate the canary (Infra 6) and the rollback (Infra 7).

    (c) INV and DIR tests for a ground-motion model. In seismology, a ground-motion model predicts how strongly the ground shakes at a site during an earthquake. Here it predicts the peak ground acceleration (PGA) from the magnitude MM, the distance RR to the source, and VS30V_{S30}, the average shear-wave velocity of the top 30 m of soil (low VS30V_{S30} = soft soil).

    • DIR: R→1.1RR \to 1.1R at fixed MM and VS30V_{S30} must not increase PGA (shaking weakens with distance).
    • DIR: M→M+0.5M \to M + 0.5 at fixed RR and VS30V_{S30} must not decrease PGA.
    • INV: the record id, the station code (if it is not a model input) and the order of the records must not change any prediction.
    • Not a valid DIR test: "softer soil (lower VS30V_{S30}) must raise PGA". It holds for weak shaking, but soft soil responds nonlinearly and can reduce PGA in strong shaking. This test would report correct physics as a failure.

    5. In code #

    The notebook builds the test harness. Here NumPy, SciPy and scikit-learn check the claims of this lesson.

    import numpy as np
    from scipy import stats
    from sklearn.feature_extraction.text import CountVectorizer
    from sklearn.neural_network import MLPClassifier
    
    # 1. A bag of words cannot see word order
    texts = ["the food was good, not bad", "the food was bad, not good"]
    X = CountVectorizer().fit_transform(texts).toarray()
    print((X[0] == X[1]).all())     # True: equal inputs, so equal scores for ANY weights
    
    # 2. Rule of three: 0 failures in n cases -> 95% upper bound on the failure rate
    n = 300
    print(round(1 - 0.05 ** (1 / n), 5), round(stats.beta.ppf(0.95, 1, n), 5), 3 / n)
    # 0.00994 0.00994 0.01
    
    # 3. The slice example of section 4: overall gain, slice loss, paired test
    old_overall = (900 * 0.90 + 100 * 0.80) / 1000
    new_overall = (900 * 0.93 + 100 * 0.70) / 1000
    print(round(old_overall, 3), round(new_overall, 3))     # 0.89 0.907
    print(round(stats.binomtest(4, 18, 0.5).pvalue, 3))     # 0.031 (McNemar, exact)
    
    # 4. A pre-training test: a small network must fit one tiny batch
    rng = np.random.default_rng(0)
    X8, y8 = rng.normal(size=(8, 5)), np.array([0, 1, 0, 1, 1, 0, 1, 0])
    mlp = MLPClassifier(hidden_layer_sizes=(32,), solver="lbfgs", max_iter=500, random_state=0)
    print(mlp.fit(X8, y8).score(X8, y8))                    # 1.0
    

    stats.beta.ppf(0.95, 1, n) is the exact one-sided bound for 0 failures; it equals 1−0.051/n1 - 0.05^{1/n}. The overfit test is fast, seeded and has a wide margin, so it is not flaky. If it fails, the training code has a bug.

    6. Where it shows up #

    • ML for science. Physical-consistency checks are behavioural tests: non-negative precipitation (an output range), conservation of mass or energy within a tolerance, invariance or equivariance under rotation or reflection where the physics has that symmetry (INV), and monotone responses such as "more rainfall gives more runoff" (DIR). Scoring a weather model per region, season and lead time is slice testing.
    • Industry. A fraud model is sliced by country, payment method and merchant type; a recommender by new and old users. CheckList found many failures in commercial sentiment models that had high test accuracy (Ribeiro et al., 2020).
    • Production. Slice and behavioural tests become the promotion gate in CI (lesson 03) and in the registry (lesson 06). The monitoring section of the ML Test Score is lesson 07 and lesson 08. LLM eval suites (lesson 09) are behavioural tests, sometimes with a model as the judge.

    7. Common mistakes #

    • Asserting exact float outputs of a trained model. They change with seeds, hardware and library versions. Assert properties with tolerances.
    • Shipping on the aggregate metric alone. Check slices against production.
    • A perturbation that changes the label. City names for a weather model, soil type for strong shaking. Check every perturbation with domain knowledge.
    • Reading a passing behavioural suite as proof. It bounds a failure rate on the tested distribution only.
    • Blocking on tiny slices. Use nmin⁡n_{\min}, a margin δ\delta and a paired test.
    • Comparing two models unpaired on the same rows. The unpaired test ignores that most rows agree, so it is too weak.
    • Unseeded or slow model tests on every commit. Seed them, keep them tiny, and move long ones to a nightly or release job.
    • Averaging the ML Test Score sections. The score is the minimum.

    8. Check yourself #

    1. What is the difference between an invariance test and a directional expectation test?
      AnswerINV applies a label-preserving perturbation and requires the score not to move by more than τ\tau. DIR applies a perturbation with a known effect direction and requires the score not to move the other way (by more than τ\tau). Neither needs labels.
    2. A slice of 40 rows drops by 3 accuracy points. Do you block the release?
      AnswerNot on this alone. At accuracy near 0.75 the standard error on 40 rows is 0.75⋅0.25/40=0.068\sqrt{0.75 \cdot 0.25/40} = 0.068, so 3 points is about 0.4 standard errors. Report it; for a decision, use a paired test (McNemar) with a margin, or collect more rows. Set nmin⁡n_{\min} so that the slices you gate on can show the drops you care about.
    3. Which test type catches each problem? (a) a feature computed with a timestamp after the label time; (b) the serving image has another NumPy version and computes a feature differently; (c) the model ignores negation.
      Answer(a) A data (leakage) test. (b) An infrastructure test: training-serving consistency, comparing online and offline features or predictions on logged requests. (c) A behavioural test, an MFT on negation.
    4. How many independent passing cases do you need to claim a failure rate below 0.5% with 95% confidence?
      AnswerSolve 1−0.051/n≤0.0051 - 0.05^{1/n} \le 0.005: n≥ln⁡0.05/ln⁡0.995=597.6n \ge \ln 0.05 / \ln 0.995 = 597.6, so 598 cases. The rule of three gives 3/0.005=6003/0.005 = 600.
    5. On a slice, the old model is right and the new one wrong on 3 rows; the reverse happens on 9 rows. Is the new model worse on this slice?
      AnswerNo, it is better: c=9>b=3c = 9 > b = 3, so the new model gains (9−3)/n(9 - 3)/n. McNemar: 2P(X≤3)2 P(X \le 3) for X∼Bin(12,1/2)X \sim \text{Bin}(12, 1/2) gives p=0.146p = 0.146, so the gain is also not clearly larger than noise.
    6. Why is "the score must not change when we replace the station id" a bad INV test for a rain model that takes the station id as an input?
      AnswerThe station id carries real information (altitude, local climate), so changing it can and should change the prediction. The perturbation is not label-preserving. INV tests on ids are valid only for ids that are not model inputs, such as a record id.
    Recall cardsAnswer in your head first, then reveal. Add the cards to your review deck to see them again just before you would forget.
    1. What is a minimum functionality test (MFT)?

    2. What is an invariance test (INV)?

    3. What is a directional expectation test (DIR)?

    4. State the rule of three and its first proof step.

    5. Compute the rule-of-three bound for 1,000 passing cases.

    6. Why can a behavioural test suite not prove that a property holds?

    7. Which rows does McNemar's test use?

    8. What is the standard error of an accuracy a=0.75a = 0.75 measured on n=100n = 100 rows?

    9. How is the final ML Test Score computed, and why?

    10. Which test type catches a feature computed with a timestamp after the label time?

    Open the review →

    9. Interview angle #

    • "How would you test an ML model before production?" Walk the table: code unit tests, data tests (contract and leakage), pre-training tests (overfit one batch), behavioural tests for the capabilities that matter, slice regression against production, infrastructure tests (the model loads, online = offline); then a canary.
    • "The new model is better on the test set. Why not ship it?" A gain can hide a slice regression or a shortcut. Check slices with a paired test, run behavioural tests, check for leakage and for training-serving consistency.
    • "How do you keep ML tests in CI from being flaky?" Seeds, tolerances, tiny data, statistical thresholds with margins, and slow suites outside the commit path.
    • "Your behavioural suite passes. What does that tell you?" The failure rate on the tested case distribution is below about 3/n3/n with 95% confidence. Nothing more.

    10. Practice #

    The notebook for this lesson runs in Colab. It has five exercises, each with a check cell and a folded solution:

    1. Write check_invariance and check_directional, and catch a planted name shortcut in a bag-of-words review model whose test accuracy looks better than a clean model's.
    2. Write an MFT on negation that the bag-of-words model fails, and explain why in one sentence.
    3. Write slice_regressions, which flags slices with n≥nmin⁡n \ge n_{\min} and a drop larger than δ\delta, and adds McNemar's p-value.
    4. Write rule_of_three_bound(n, conf) and check it with a simulation and the exact binomial.
    5. Write ml_test_score(table) with the minimum rule and find the bottleneck section.

    11. Watch and read #

    Starts at "Testing ML systems": tests for data, training and models, testing in production, and the ML Test Score.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    The first author explains MFT, INV and DIR tests and the failures they found in commercial models.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    A hands-on lab: linting, pytest and doctests, and a memorization test (the overfit-one-batch idea) in a real repository; the second half is about PyTorch performance.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play