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

    ML Interview Prep · Lesson 9 · ML breadth

    Breadth: classic ML, evaluation and A/B testing

    Intermediate 85 min 5 interactive figures 5 videos Colab notebook

    Before this lesson: a first course in machine learning and in statistics (hypothesis tests, the normal distribution). For the code of AUC and logistic regression, see lesson 06. Depth on these topics is in a first ML course and in the Probability and Statistics track; canary releases and peeking in production are in MLOps lesson 06. Time: about 85 minutes, plus the notebook.


    Why this matters #

    Breadth questions come in phone screens, in "ML knowledge" rounds and in data-science loops. They test whether you give a short, correct, structured answer quickly. Common failures are an answer that runs for five minutes, a wrong basic definition (precision vs recall), or no number at all.

    A good breadth answer has four parts and takes 30 seconds to 2 minutes:

    1. one sentence that defines the thing;
    2. the formula or the mechanism;
    3. one trade-off, or when to use it;
    4. one number or one example.

    The same tools matter in ML for science. Evaluation design decides whether a weather or medical model is tested honestly (spatial, temporal and patient-level leakage). Calibration decides whether a probabilistic forecast can be trusted. And "is model B better than model A?" is a hypothesis test on a fixed test set.

    1. Predict first #

    2. The picture #

    2.1 Error has three sources #

    Fix one input xx. The prediction error there comes from three sources:

    1. noise in the label, which no model can remove;
    2. bias: the average model (over many training sets) is offset from the truth;
    3. variance: the fitted model moves when you draw a new training set.

    A constant predictor has large bias and almost no variance. A 1-nearest-neighbour predictor has small bias and large variance. Most choices in classic ML (model size, regularization, the number of neighbours, ensembles) move error between bias and variance.

    Interactive figure: loading…

    Move the degree slider and watch the 20 faint fits, each trained on a new random training set of 15 points. The thick orange curve is their average, and the dashed curve is the true function. At degree 0–1 the average is far from the truth (bias). At degree 9–10 the average is close to the truth, but the single fits swing wildly (variance). The lower chart shows bias², variance and the noise for every degree; their sum is the expected test error, a U-shaped curve. The average training error only falls as the degree grows.

    2.2 L1 gives zeros, L2 does not #

    Regularization adds a penalty on the weights to the loss. With an L2 (ridge) penalty λ∥w∥22\lambda\|w\|_2^2 the weights shrink towards zero but stay nonzero. With an L1 (lasso) penalty λ∥w∥1\lambda\|w\|_1 many weights become exactly zero. The picture: minimize the loss inside a ball. The L1 ball is a diamond with corners on the axes, and the smallest loss contour often touches it at a corner, where one coordinate is 0.

    Interactive figure: loading…

    Drag the centre of the ellipses (the unregularized solution) and shrink the radius. The L1 solution (diamond) often sits on a corner, so one weight is exactly 0. The L2 solution (disk) almost never lands on an axis. Ask yourself: why does the corner win so often?

    2.3 A classifier is a ranking plus a threshold #

    A classifier gives each example a score. Three different questions follow, and each has its own metrics:

    • Does the score rank positives above negatives? (ROC-AUC, average precision.)
    • At the chosen threshold, how many alarms are right, and how many positives are found? (precision, recall, F1F_1.)
    • Is the score a probability: of the cases scored 0.8, are 80% positive? (calibration, log-loss, Brier score.)
    Interactive figure: loading…

    Move the prevalence slider from 50% down to 0.5% (the fraud preset) and keep the threshold. The ROC curve and its AUC do not move: TPR and FPR are each computed inside one class. The PR curve, the precision and the number of false alarms per caught positive change a lot, because precision compares the two classes in absolute numbers. Then move the threshold and watch precision and recall trade against each other. The areas right of the threshold are the true positives and the false positives.

    2.4 An A/B test is two noisy coins #

    In an A/B test, each user converts with some probability pp. The rate of one arm with nn users has standard error p(1−p)/n\sqrt{p(1-p)/n}. The difference of two independent arms has standard error 2p(1−p)/n\sqrt{2p(1-p)/n}. To see a true difference δ\delta reliably, δ\delta must be about 2.8 standard errors: 1.96 for the test at α=0.05\alpha = 0.05, plus 0.84 for power 0.8. Solve for nn:

    n≈2⋅2.82 p(1−p)δ2≈16 p(1−p)δ2.n \approx \frac{2 \cdot 2.8^2\, p(1-p)}{\delta^2} \approx \frac{16\, p(1-p)}{\delta^2}.

    Small effects need many users, because nn grows like 1/δ21/\delta^2: half the effect needs four times the users.

    Interactive figure: loading…

    Find the number of users that gives 80% power. The blue curve is the distribution of the observed difference when there is no effect; the orange curve, when the true lift is the MDE. The test rejects outside the dashed lines. Power is the orange area outside them. Move nn until the power reads 0.80, then halve the MDE and see how far nn must move.

    Quick check

    Your model has training error 2% and validation error 15%. Which change is most likely to reduce the validation error?

    Quick check

    In the orthonormal case the lasso solution is wj=sign⁡(w^j) (∣w^j∣−λ)+w_j = \operatorname{sign}(\hat w_j)\,(|\hat w_j| - \lambda)_+. With λ=0.5\lambda = 0.5 and least-squares weights w^=(2.0, 0.3, −0.8)\hat w = (2.0,\ 0.3,\ -0.8), what is ww?

    3. Definitions and main results #

    3.1 Bias and variance #

    Setting. Let y=f(x)+εy = f(x) + \varepsilon with E[ε]=0\mathbb{E}[\varepsilon] = 0 and Var⁡(ε)=σ2\operatorname{Var}(\varepsilon) = \sigma^2. A model f^D\hat f_D is fitted on a random training set DD. The new noise ε\varepsilon at the test point is independent of DD. Let fˉ(x)=ED[f^D(x)]\bar f(x) = \mathbb{E}_D[\hat f_D(x)] be the average fit over training sets.

    Theorem (bias–variance decomposition, squared loss). At a fixed input xx,

    E[(y−f^D(x))2]=σ2⏟noise+(f(x)−fˉ(x))2⏟bias2+ED[(f^D(x)−fˉ(x))2]⏟variance.\mathbb{E}\big[(y - \hat f_D(x))^2\big] = \underbrace{\sigma^2}_{\text{noise}} + \underbrace{\big(f(x) - \bar f(x)\big)^2}_{\text{bias}^2} + \underbrace{\mathbb{E}_D\big[(\hat f_D(x) - \bar f(x))^2\big]}_{\text{variance}} .

    Why this is true, in one line. We split the error into three pieces that are "uncorrelated", so the squares add like the sides of a right triangle.

    Proof.

    1. Add and subtract f(x)f(x) and fˉ(x)\bar f(x): y−f^D(x)=ε⏟a+(f(x)−fˉ(x))⏟b+(fˉ(x)−f^D(x))⏟cy - \hat f_D(x) = \underbrace{\varepsilon}_{a} + \underbrace{(f(x) - \bar f(x))}_{b} + \underbrace{(\bar f(x) - \hat f_D(x))}_{c}.
    2. Square: (a+b+c)2=a2+b2+c2+2ab+2ac+2bc(a + b + c)^2 = a^2 + b^2 + c^2 + 2ab + 2ac + 2bc. Take the expectation over DD and ε\varepsilon.
    3. E[ab]=b E[ε]=0\mathbb{E}[ab] = b\,\mathbb{E}[\varepsilon] = 0, because bb is a constant.
    4. E[ac]=E[ε] E[c]=0\mathbb{E}[ac] = \mathbb{E}[\varepsilon]\,\mathbb{E}[c] = 0, because ε\varepsilon is independent of DD.
    5. E[bc]=b ED[fˉ(x)−f^D(x)]=b (fˉ(x)−fˉ(x))=0\mathbb{E}[bc] = b\,\mathbb{E}_D[\bar f(x) - \hat f_D(x)] = b\,(\bar f(x) - \bar f(x)) = 0.
    6. What remains is E[a2]+b2+E[c2]=σ2+bias2+variance\mathbb{E}[a^2] + b^2 + \mathbb{E}[c^2] = \sigma^2 + \text{bias}^2 + \text{variance}. ■\blacksquare

    Two small examples.

    • kk-nearest neighbours (regression). Suppose the kk neighbours of xx have independent labels with noise variance σ2\sigma^2. The prediction is the mean of kk labels, so its variance is σ2/k\sigma^2/k. Larger kk lowers the variance, but uses neighbours farther from xx, whose ff values differ from f(x)f(x): the bias grows.
    • Constant model f^D(x)=yˉ\hat f_D(x) = \bar y (the training mean). Its variance is small (about Var⁡(y)/n\operatorname{Var}(y)/n), but its bias at xx is f(x)−E[y]f(x) - \mathbb{E}[y], large wherever ff is far from its average.
    Change Bias Variance
    more flexible model (deeper tree, more features, smaller kk in kk-NN) down up
    stronger regularization (larger λ\lambda, early stopping) up down
    more training data same down
    average many bagged models about the same down, to a floor (section 3.3)
    more boosting rounds down up, eventually

    Diagnosis from learning curves. High bias: the training error is high and the validation error is close to it; use a more flexible model or better features. High variance: the training error is low and the gap to validation is large; get more data, regularize, or average models. A large gap can also mean that training and validation data differ (shift or leakage), so check that too.

    Beyond squared loss, and double descent

    The clean additive decomposition holds for squared loss. For the 0–1 loss of classification the terms do not add up this way, although "bias" and "variance" are still useful words. Very large models can show double descent: the test error rises to a peak where the model can just interpolate the training data, and then falls again as the model grows further. Explicit or implicit regularization (for example, the minimum-norm solution that gradient descent finds) lowers the variance beyond that peak. See the deep learning lesson on generalization.

    3.2 L1 vs L2 regularization #

    • Ridge (L2): minimize ∥y−Xw∥2+λ∥w∥22\|y - Xw\|^2 + \lambda\|w\|_2^2. The solution is w=(X⊤X+λI)−1X⊤yw = (X^\top X + \lambda I)^{-1} X^\top y, and the matrix is invertible for every λ>0\lambda > 0. Ridge shrinks all weights towards 0 but keeps them nonzero. With correlated features it shares the weight among them.
    • Lasso (L1): minimize 12∥y−Xw∥2+λ∥w∥1\tfrac12\|y - Xw\|^2 + \lambda\|w\|_1. In the simplest case, an orthonormal design (X⊤X=IX^\top X = I, least-squares solution w^=X⊤y\hat w = X^\top y), it acts on each coordinate alone:
    wj=sign⁡(w^j) (∣w^j∣−λ)+(soft thresholding).w_j = \operatorname{sign}(\hat w_j)\,\big(|\hat w_j| - \lambda\big)_+ \qquad (\textbf{soft thresholding}).

    Every ∣w^j∣≤λ|\hat w_j| \le \lambda becomes exactly 0. Ridge in the same case, with penalty λ2∥w∥2\tfrac{\lambda}{2}\|w\|^2, gives wj=w^j/(1+λ)w_j = \hat w_j/(1 + \lambda): never exactly 0.

    • Why zeros. The slope of ∣w∣|w| near 0 is ±1\pm 1: it does not vanish. So a small weight must "pay" λ\lambda per unit, and if the loss gains less than that, the weight stays at 0. The slope of w2w^2 near 0 is 2w→02w \to 0, so a tiny nonzero weight is almost free.
    • Bayesian (MAP) view. With Gaussian noise of variance σ2\sigma^2, a Gaussian prior wj∼N(0,τ2)w_j \sim \mathcal N(0, \tau^2) gives ridge (with λ=σ2/τ2\lambda = \sigma^2/\tau^2 for the penalty λ2∥w∥2\tfrac{\lambda}{2}\|w\|^2). A Laplace prior p(wj)∝e−∣wj∣/bp(w_j) \propto e^{-|w_j|/b} gives the lasso (with λ=σ2/b\lambda = \sigma^2/b). The Laplace density has a sharp peak at 0, which is where the exact zeros come from.
    • In practice. Standardize the features first (the penalty is not scale invariant). Do not penalize the intercept. With a group of correlated features the lasso picks one of them almost at random; the elastic net (penalty λ1∥w∥1+λ2∥w∥22\lambda_1\|w\|_1 + \lambda_2\|w\|_2^2) keeps the group. Choose λ\lambda by cross-validation.

    3.3 Trees, bagging and boosting #

    Decision trees (CART). A tree splits greedily. At each node it picks the feature and the threshold with the largest decrease of weighted impurity. For classification with class fractions pkp_k in a node:

    Gini=1−∑kpk2,entropy=−∑kpklog⁡pk.\text{Gini} = 1 - \sum_k p_k^2, \qquad \text{entropy} = -\sum_k p_k \log p_k .

    For regression the impurity is the sum of squared errors. Trees need no feature scaling, handle mixed feature types and interactions, and are easy to read. They have high variance, and their piecewise-constant predictions cannot extrapolate beyond the training range.

    Proposition (variance of an average). Let f1,…,fMf_1, \dots, f_M each have variance σ2\sigma^2 and pairwise correlation ρ\rho. Then

    Var⁡(1M∑m=1Mfm)=ρσ2+1−ρM σ2.\operatorname{Var}\Big(\frac1M \sum_{m=1}^{M} f_m\Big) = \rho\sigma^2 + \frac{1-\rho}{M}\,\sigma^2 .

    Proof.

    1. Var⁡(∑mfm)=∑mVar⁡(fm)+∑m≠m′Cov⁡(fm,fm′)=Mσ2+M(M−1)ρσ2\operatorname{Var}\big(\sum_m f_m\big) = \sum_m \operatorname{Var}(f_m) + \sum_{m \ne m'} \operatorname{Cov}(f_m, f_{m'}) = M\sigma^2 + M(M-1)\rho\sigma^2.
    2. Divide by M2M^2: σ2/M+(M−1)ρσ2/M=ρσ2+(1−ρ)σ2/M\sigma^2/M + (M-1)\rho\sigma^2/M = \rho\sigma^2 + (1-\rho)\sigma^2/M. ■\blacksquare

    More trees remove only the second term; the floor ρσ2\rho\sigma^2 stays. Bagging trains deep trees on bootstrap samples (draw nn points with replacement). A random forest also picks each split from a random subset of the features (often d\sqrt d of the dd features for classification); this lowers ρ\rho. Each point is left out of a bootstrap sample with probability (1−1/n)n→e−1≈0.368(1 - 1/n)^n \to e^{-1} \approx 0.368. So the trees that did not see a point give a nearly free test estimate for it: the out-of-bag (OOB) error.

    Gradient boosting is gradient descent in function space. Start with the best constant F0F_0. At each round mm:

    1. compute the pseudo-residuals ri=−∂L(yi,F(xi))/∂F(xi)r_i = -\partial L(y_i, F(x_i)) / \partial F(x_i), the negative gradient of the loss with respect to the current prediction. For squared loss L=12(y−F)2L = \tfrac12 (y - F)^2 this is ri=yi−F(xi)r_i = y_i - F(x_i), the ordinary residual;
    2. fit a small tree hmh_m to the pairs (xi,ri)(x_i, r_i);
    3. set each leaf value by a line search on the loss;
    4. update F←F+ν hmF \leftarrow F + \nu\, h_m with a learning rate (shrinkage) ν∈(0,1]\nu \in (0, 1], often 0.05–0.1.

    Why "gradient descent": if we could change each prediction F(xi)F(x_i) freely, the steepest-descent step would be F(xi)←F(xi)+νriF(x_i) \leftarrow F(x_i) + \nu r_i. A tree cannot fit every rir_i exactly, so it fits the best approximation of this step that generalizes to new xx.

    XGBoost's leaf weight

    XGBoost takes a second-order (Newton) step. With GG and HH the sums of the first and second derivatives of the loss over the examples in a leaf, and an L2 penalty λ\lambda on the leaf weights, the best leaf weight is w∗=−G/(H+λ)w^* = -G/(H + \lambda). For squared loss HH is the number of examples in the leaf, so w∗w^* is a shrunk mean residual. LightGBM and CatBoost are other popular implementations with different tree-growing and categorical-feature strategies.

    Bagging / random forest Boosting
    training parallel, independent trees on bootstrap samples sequential; each tree fits the errors of the current ensemble
    base learner deep trees: low bias, high variance shallow trees (depth 3–8): high bias, low variance
    mainly reduces variance bias
    more trees the error converges; no extra overfitting overfits eventually; use early stopping
    tuning few knobs, robust learning rate, rounds, depth, regularization
    typical use strong baseline with an OOB estimate best accuracy on tabular data

    3.4 Metrics #

    With true and false positives and negatives TP,FP,FN,TNTP, FP, FN, TN:

    Metric Formula In words
    precision TP/(TP+FP)TP/(TP + FP) how many alarms are right
    recall (TPR, sensitivity) TP/(TP+FN)TP/(TP + FN) how many positives are found
    F1F_1 2TP/(2TP+FP+FN)2TP/(2TP + FP + FN) harmonic mean of precision and recall
    FPR FP/(FP+TN)FP/(FP + TN) how many negatives raise an alarm
    accuracy (TP+TN)/N(TP + TN)/N misleading when one class is rare
    • ROC-AUC is the area under the curve of TPR against FPR over all thresholds. It equals P(s+>s−)+12P(s+=s−)P(s^+ > s^-) + \tfrac12 P(s^+ = s^-) for the score s+s^+ of a random positive and s−s^- of a random negative. So it measures ranking only. It does not change under any increasing transform of the scores, and it does not depend on the class ratio, because TPR and FPR are each computed inside one class.
    • PR curve and average precision (AP). Precision mixes the two classes, so it depends on the class ratio. A random ranking has AP about equal to the prevalence. With rare positives, report AP and the precision at the operating threshold.
    • Proper scores and calibration. Log-loss −[ylog⁡p+(1−y)log⁡(1−p)]-[y \log p + (1-y)\log(1-p)] and the Brier score (p−y)2(p - y)^2 are minimized in expectation by the true probability. A reliability diagram plots the observed frequency of positives against the predicted probability, in bins; a calibrated model lies on the diagonal. Fix a miscalibrated model on held-out data: temperature scaling (divide the logits by one T>0T > 0; standard for neural networks), Platt scaling (a logistic regression on the score), or isotonic regression.
    • Multi-class: macro averaging gives each class the same weight; micro averaging pools all decisions, so large classes dominate.
    • Regression: MSE is minimized by the conditional mean and punishes outliers strongly; MAE is minimized by the conditional median and is robust. MAPE, the mean of ∣y−y^∣/∣y∣|y - \hat y|/|y|, is undefined at y=0y = 0 and punishes over-prediction more than under-prediction.

    Proposition (threshold from costs). A false alarm costs cFPc_{FP} and a missed positive costs cFNc_{FN}. If p=P(y=1∣x)p = P(y = 1 \mid x) is calibrated, flagging has lower expected cost exactly when

    p>cFPcFP+cFN.p > \frac{c_{FP}}{c_{FP} + c_{FN}} .

    Proof. Flagging costs (1−p) cFP(1-p)\,c_{FP} in expectation (a false alarm when y=0y = 0). Not flagging costs p cFNp\,c_{FN} (a miss when y=1y = 1). Flag when p cFN>(1−p) cFPp\,c_{FN} > (1-p)\,c_{FP}, that is p (cFN+cFP)>cFPp\,(c_{FN} + c_{FP}) > c_{FP}. ■\blacksquare

    For example, if a missed fraud costs 20 times a false alarm, flag when p>1/21≈0.048p > 1/21 \approx 0.048.

    3.5 Evaluation design and leakage #

    • Three sets. Train to fit, validation to choose, test to report, used once. For a model choice and an honest estimate from little data, use nested cross-validation.

    • kk-fold cross-validation. Stratify by class for classification. Split by group when examples are not independent: one patient, one user or one weather station in one fold only. For time series, train on the past and test on the future, with a gap. For spatial fields, use blocks larger than the correlation length.

    • Leakage is information in the training data or features that is not available at prediction time:

      • target leakage: a feature recorded after the outcome (for example "refund issued" to predict fraud);
      • preprocessing on all data: a scaler, PCA, feature selection or target encoding fitted before the split;
      • duplicates or near-duplicates across splits;
      • a random split of time series or grouped data.

      Fix: fit every preprocessing step inside the training fold (a pipeline), and split by time or group when the data has that structure.

    • Class imbalance. Keep the natural class ratio in validation and test. Class weights or resampling change the effective prior, so the predicted probabilities are no longer calibrated; recalibrate, or keep the data as it is and move the threshold.

    • Offline vs online. An offline metric is a proxy for the product goal. The online A/B test is the final judge.

    3.6 Probability questions #

    Interviews often ask one short probability question. Three types cover most of them.

    Bayes' rule with a base rate. A disease has prevalence 0.5%. A test has sensitivity 99% (P(+∣D)=0.99P(+ \mid D) = 0.99) and specificity 99% (P(−∣no D)=0.99P(- \mid \text{no } D) = 0.99). A person tests positive. What is P(D∣+)P(D \mid +)?

    P(D∣+)=P(+∣D) P(D)P(+∣D) P(D)+P(+∣no D) P(no D)=0.99⋅0.0050.99⋅0.005+0.01⋅0.995=0.004950.0149≈0.33.P(D \mid +) = \frac{P(+ \mid D)\,P(D)}{P(+ \mid D)\,P(D) + P(+ \mid \text{no } D)\,P(\text{no } D)} = \frac{0.99 \cdot 0.005}{0.99 \cdot 0.005 + 0.01 \cdot 0.995} = \frac{0.00495}{0.0149} \approx 0.33 .

    The natural-frequency check makes it easy to say aloud: of 100,000 people, 500 are sick and 495 of them test positive. Of the 99,500 healthy people, 1% (995) also test positive. So 495/(495+995)=33%495/(495 + 995) = 33\% of the positives are sick. The false positives win because the healthy group is 200 times larger. This is the same effect as low precision under class imbalance.

    Linearity of expectation. E[X+Y]=E[X]+E[Y]\mathbb{E}[X + Y] = \mathbb{E}[X] + \mathbb{E}[Y] always, even for dependent variables. Example: shuffle nn cards numbered 1,…,n1, \dots, n. The expected number of cards in their own position is 1. Let Xi=1X_i = 1 if card ii is in position ii; then E[Xi]=1/n\mathbb{E}[X_i] = 1/n, and E[∑iXi]=n⋅1/n=1\mathbb{E}[\sum_i X_i] = n \cdot 1/n = 1, for every nn. The XiX_i are dependent, and it does not matter.

    A first-step equation. How many fair coin flips, on average, until two heads in a row? Let E0E_0 be the expected number of further flips from the start, and E1E_1 the number after one head. Condition on the next flip:

    • E1=1+12⋅0+12E0E_1 = 1 + \tfrac12 \cdot 0 + \tfrac12 E_0 (a head ends the game; a tail starts over);
    • E0=1+12E1+12E0E_0 = 1 + \tfrac12 E_1 + \tfrac12 E_0.

    Substitute the first into the second: E0=1+12+14E0+12E0E_0 = 1 + \tfrac12 + \tfrac14 E_0 + \tfrac12 E_0, so 14E0=32\tfrac14 E_0 = \tfrac32 and E0=6E_0 = 6.

    How to answer aloud. Name the events. Write the formula with symbols first. Plug in the numbers. Then check with natural frequencies or an extreme case (for example, "with prevalence 50% the answer would be 99%").

    Quick check

    A test has sensitivity 90% and specificity 95%. The prevalence is 1%. About what fraction of the people who test positive have the condition?

    3.7 Hypothesis tests and A/B testing #

    Words. The null hypothesis H0H_0 says "no difference". A type I error rejects a true H0H_0; its probability is the level α\alpha. A type II error keeps a false H0H_0; its probability is β\beta, and the power is 1−β1 - \beta. The p-value is the probability, computed under H0H_0, of a test statistic at least as extreme as the one observed. It is not the probability that H0H_0 is true. The minimum detectable effect (MDE) δ\delta is the smallest effect that is worth detecting. It is a business choice, not the effect you hope for.

    Two-proportion z-test. With xkx_k conversions out of nkn_k users in arm kk, rates p^k=xk/nk\hat p_k = x_k/n_k and pooled rate pˉ=(x1+x2)/(n1+n2)\bar p = (x_1 + x_2)/(n_1 + n_2):

    z=p^2−p^1pˉ(1−pˉ) (1/n1+1/n2).z = \frac{\hat p_2 - \hat p_1}{\sqrt{\bar p(1 - \bar p)\,(1/n_1 + 1/n_2)}} .

    Reject H0H_0 at level α\alpha when ∣z∣>z1−α/2|z| > z_{1-\alpha/2} (1.96 for α=0.05\alpha = 0.05). For a 2×22 \times 2 table this is the same test as the χ2\chi^2 test without continuity correction: χ2=z2\chi^2 = z^2.

    Theorem (sample size per arm). Let the metric have per-user variance σ12\sigma_1^2 in control and σ22\sigma_2^2 in treatment, with nn users per arm and true difference δ>0\delta > 0. With the normal approximation, a two-sided test at level α\alpha has power 1−β1 - \beta when

    n=(z1−α/2+z1−β)2 (σ12+σ22)δ2,n = \frac{(z_{1-\alpha/2} + z_{1-\beta})^2\,(\sigma_1^2 + \sigma_2^2)}{\delta^2},

    where zqz_q is the qq-quantile of the standard normal distribution.

    Proof.

    1. The difference of the two means, Δ^\hat\Delta, has standard error se=(σ12+σ22)/n\text{se} = \sqrt{(\sigma_1^2 + \sigma_2^2)/n} (independent arms; central limit theorem).
    2. The test rejects when ∣Δ^∣>z1−α/2 se|\hat\Delta| > z_{1-\alpha/2}\,\text{se}.
    3. Under the alternative, Δ^≈N(δ,se2)\hat\Delta \approx \mathcal N(\delta, \text{se}^2). Ignore the tiny chance of rejecting in the wrong direction. Then power≈P(Δ^>z1−α/2 se)=Φ(δ/se−z1−α/2)\text{power} \approx P(\hat\Delta > z_{1-\alpha/2}\,\text{se}) = \Phi(\delta/\text{se} - z_{1-\alpha/2}), with Φ\Phi the standard normal CDF.
    4. Power =1−β= 1 - \beta means δ/se−z1−α/2=z1−β\delta/\text{se} - z_{1-\alpha/2} = z_{1-\beta}, so δ=(z1−α/2+z1−β) se\delta = (z_{1-\alpha/2} + z_{1-\beta})\,\text{se}.
    5. Substitute se\text{se} from step 1, square, and solve for nn. ■\blacksquare

    Rule of thumb. With σ1=σ2=σ\sigma_1 = \sigma_2 = \sigma, α=0.05\alpha = 0.05 and power 0.8: 2(1.960+0.842)2=15.72(1.960 + 0.842)^2 = 15.7, so n≈16 σ2/δ2n \approx 16\,\sigma^2/\delta^2 per arm. For a conversion rate pp, σ2=p(1−p)\sigma^2 = p(1-p).

    What the formula says.

    • n∝1/δ2n \propto 1/\delta^2: half the MDE needs four times the users.
    • n∝σ2n \propto \sigma^2: a metric with less variance needs fewer users (conversion instead of revenue, or variance reduction with CUPED below).
    • An unequal split with a fraction ff in treatment needs about 1/(4f(1−f))1/(4f(1-f)) times as many users in total as a 50/50 split: 2.8 times for 90/10.

    Design checklist.

    1. A hypothesis and one primary metric (the overall evaluation criterion), plus guardrail metrics such as latency, errors and revenue.
    2. The unit of randomization, usually the user.
    3. Baseline, MDE, α\alpha and power → nn per arm.
    4. Duration: nn divided by the eligible users per arm per day, rounded up to full weeks.
    5. Checks: an A/A test (two identical arms; it should reject about 5% of the time) and a sample-ratio check.
    6. The analysis plan and the decision rule, fixed before the start.
    7. Report the effect with a confidence interval, and ask whether it is practically large enough, not only significant.

    Pitfalls and their fixes.

    • Peeking. Checking every day and stopping at the first p<0.05p < 0.05 inflates the type I error: about 0.08 with 2 looks, 0.14 with 5, 0.19 with 10, and 0.25 with 20 equally spaced looks. Fix the sample size in advance, or use a sequential design built for many looks (alpha spending, always-valid p-values).
    • Sample-ratio mismatch (SRM). The observed split differs from the design more than chance allows. Test the counts with a χ2\chi^2 goodness-of-fit test and raise an alarm at about p<0.001p < 0.001. An SRM means a bug in assignment, logging or filtering; do not trust the result until it is found.
    • Novelty, primacy and day-of-week effects. Users react to change itself. Run full weeks, plot the effect by day, and compare new with returning users.
    • Many metrics or variants. With 20 independent metrics at α=0.05\alpha = 0.05, P(at least one false positive)=1−0.9520=0.64P(\text{at least one false positive}) = 1 - 0.95^{20} = 0.64. Choose the primary metric in advance; correct the others (Holm, or Benjamini–Hochberg for the false discovery rate).
    • Interference. The analysis assumes that a user's outcome does not depend on the assignment of other users (SUTVA, the stable unit treatment value assumption). Social networks and two-sided marketplaces break it. Randomize clusters or markets, or use switchback designs (switch the whole system over time).
    • Unit of randomization ≠ unit of analysis. If you randomize users but compute the click rate per page view, the naive variance is too small, because the views of one user are correlated. Analyze one value per user, or use the delta method or a user-level bootstrap.
    • Changing the split during the test (for example 1% → 50%) can make the pooled totals show the reverse of every daily result (Simpson's paradox). Analyze the period with a fixed split.
    Interactive figure: loading…

    Press "Run 2,000 A/A tests" with 1 look, then with 10 and 20 looks. Both arms are identical, so every rejection is a false positive. A path turns orange when it crosses ±1.96\pm 1.96 at any look, and the experimenter stops there. The lower chart compares the simulated false-positive rate with the planned 5%.

    Quick check

    A 50/50 test shows 50,600 users in control and 49,400 in treatment. The χ2\chi^2 test of the counts gives p=0.0002p = 0.0002. What is the right next step?

    Variance reduction with CUPED

    Replace the outcome YY by Y−θ(X−Xˉ)Y - \theta(X - \bar X), where XX is the same metric measured before the experiment and θ=Cov⁡(X,Y)/Var⁡(X)\theta = \operatorname{Cov}(X, Y)/\operatorname{Var}(X). The variance falls by the factor 1−ρ21 - \rho^2, with ρ\rho the correlation of XX and YY: ρ=0.7\rho = 0.7 gives 0.510.51, so about half the users are needed. The estimate of the treatment effect stays unbiased, because XX is measured before randomization and so has the same distribution in both arms.

    Answers to section 1. (1) About 60,000. The formula gives 57,760 per arm (57,763 with the pooled variance under H0H_0); the rule of thumb gives 16⋅0.09/0.0052=57,60016 \cdot 0.09/0.005^2 = 57{,}600. (2) Ask for the precision at the operating threshold (or the AP). ROC-AUC ignores the class ratio. Suppose the threshold gives TPR 0.80 at FPR 0.05. Per 1,000,000 transactions there are 5,000 frauds and 995,000 normal ones: 4,000 frauds are caught, and 0.05⋅995,000=49,7500.05 \cdot 995{,}000 = 49{,}750 normal transactions raise false alarms. Precision is 4,000/53,750=7.4%4{,}000/53{,}750 = 7.4\%: about 12 false alarms for every fraud caught.

    SQL patterns for data rounds

    Some loops add a SQL question on experiment logs. The logical order of a query is FROM/JOIN → WHERE → GROUP BY → HAVING → window functions → SELECT → DISTINCT → ORDER BY → LIMIT. Most bugs put a step in the wrong place.

    • Joins. LEFT JOIN keeps every left row and fills NULL when nothing matches. A condition on the right table belongs in the ON clause: in WHERE it removes the NULL rows and turns the left join into an inner join. Anti-join (rows without a match): NOT EXISTS (...).
    • NULL. x = NULL is never true; use IS NULL. COUNT(*) counts rows, COUNT(col) counts non-NULL values. x NOT IN (subquery) returns no rows if the subquery contains a NULL.
    • Groups. WHERE filters rows before grouping; HAVING filters groups after it.
    • Window functions compute a value per row without collapsing rows: ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY ts). RANK() gives 1, 1, 3 for a tie and DENSE_RANK() gives 1, 1, 2. Top-NN per group: ROW_NUMBER() in a subquery, then WHERE rn <= N.

    Conversion rate per variant, with the user as the unit, from tables assignments(user_id, variant, assigned_at) and events(user_id, event, ts):

    WITH per_user AS (
      SELECT a.user_id, a.variant, COUNT(e.user_id) AS purchases
      FROM assignments AS a
      LEFT JOIN events AS e
        ON  e.user_id = a.user_id
        AND e.event = 'purchase'
        AND e.ts >= a.assigned_at          -- only purchases after assignment
      GROUP BY a.user_id, a.variant
    )
    SELECT variant, COUNT(*) AS users,
           ROUND(AVG(CASE WHEN purchases > 0 THEN 1.0 ELSE 0.0 END), 3) AS conv_rate
    FROM per_user
    GROUP BY variant;
    

    On a small test table (users 1–3 in A, 4–6 in B; users 1, 4 and 5 buy after assignment) this gives A: 3 users, rate 0.333 and B: 3 users, rate 0.667. If you move the two purchase conditions to WHERE, the users without purchases disappear and both rates become 1.0.

    4. Worked example #

    A. Metrics on ten examples. Four positives with scores 0.95, 0.85, 0.70, 0.45; six negatives with scores 0.80, 0.60, 0.40, 0.30, 0.20, 0.10.

    1. Threshold 0.5. The predicted positives are the scores 0.95, 0.85, 0.80, 0.70, 0.60. Of these, 3 are real positives. So TP=3TP = 3, FP=2FP = 2, FN=1FN = 1 (the 0.45), TN=4TN = 4.
    2. Precision =3/5=0.60= 3/5 = 0.60. Recall =3/4=0.75= 3/4 = 0.75. F1=2⋅3/(2⋅3+2+1)=6/9=0.667F_1 = 2 \cdot 3/(2 \cdot 3 + 2 + 1) = 6/9 = 0.667. Accuracy =7/10= 7/10.
    3. ROC-AUC by counting pairs. There are 4⋅6=244 \cdot 6 = 24 (positive, negative) pairs. The positive 0.95 beats all 6 negatives; 0.85 beats 6 (0.80 is lower); 0.70 beats 5 (not 0.80); 0.45 beats 4 (0.40, 0.30, 0.20, 0.10). Total 6+6+5+4=216 + 6 + 5 + 4 = 21, so AUC =21/24=0.875= 21/24 = 0.875.
    4. Average precision. Sort by score: 0.95 (+), 0.85 (+), 0.80 (−), 0.70 (+), 0.60 (−), 0.45 (+), …. The positives arrive at ranks 1, 2, 4 and 6. The precision at those ranks is 1/11/1, 2/22/2, 3/43/4, 4/64/6. Each positive adds 1/41/4 of recall, so AP =14(1+1+0.75+0.667)=0.854= \tfrac14 (1 + 1 + 0.75 + 0.667) = 0.854.
    5. Now add six more negatives with the same scores (the prevalence falls from 40% to 25%). Recall stays 0.750.75 and ROC-AUC stays 0.8750.875: both are computed inside one class. Precision falls to 3/(3+4)=0.433/(3 + 4) = 0.43, and AP falls to 0.7750.775.

    B. An A/B test from plan to decision. A new checkout page. Baseline conversion p1=10%p_1 = 10\%, MDE δ=0.5\delta = 0.5 points (a 5% relative lift), α=0.05\alpha = 0.05 two-sided, power 0.8.

    1. Sample size: n=(1.960+0.842)2 (0.10⋅0.90+0.105⋅0.895)/0.0052=7.849⋅0.18398/0.000025=57,760n = (1.960 + 0.842)^2\,(0.10 \cdot 0.90 + 0.105 \cdot 0.895)/0.005^2 = 7.849 \cdot 0.18398/0.000025 = 57{,}760 per arm.
    2. Duration: 20,000 eligible users per day, split 50/50, give 10,000 per arm per day. 57,760/10,000=5.857{,}760/10{,}000 = 5.8 days. Round up to 7 days (one full week), so about 70,000 per arm.
    3. Result: control has 7,024 conversions of 70,240 users (10.00%10.00\%); treatment has 7,360 of 69,760 (10.55%10.55\%).
    4. SRM check first. The expected count per arm is 70,000. χ2=(2402+2402)/70,000=1.65\chi^2 = (240^2 + 240^2)/70{,}000 = 1.65, p=0.20p = 0.20: no alarm. (With 70,700 vs 69,300 it would be χ2=14.0\chi^2 = 14.0, p=0.0002p = 0.0002: stop and debug.)
    5. Test. Pooled pˉ=14,384/140,000=0.1027\bar p = 14{,}384/140{,}000 = 0.1027. Then z=0.0055/0.1027⋅0.8973⋅(1/70,240+1/69,760)=3.39z = 0.0055/\sqrt{0.1027 \cdot 0.8973 \cdot (1/70{,}240 + 1/69{,}760)} = 3.39, and p=0.0007p = 0.0007.
    6. 95% confidence interval of the difference, with the unpooled standard error 0.162 points: 0.55±1.96⋅0.162=[0.23, 0.87]0.55 \pm 1.96 \cdot 0.162 = [0.23,\ 0.87] points. The effect is significant, and the whole interval is above zero. Whether a lift of 0.2–0.9 points pays for the change is a product decision.
    Quick check

    The plan of example B needs 57,760 users per arm for an MDE of 0.5 points. The product team now wants to detect 0.25 points. About how many users per arm?

    5. In code #

    Libraries check the numbers of section 4. The notebook has these cells and the exercises.

    import numpy as np
    from scipy import stats
    from sklearn.metrics import (average_precision_score, confusion_matrix,
                                 precision_recall_fscore_support, roc_auc_score)
    
    # (A) metrics on ten examples
    y = np.array([1, 1, 1, 1, 0, 0, 0, 0, 0, 0])
    s = np.array([0.95, 0.85, 0.70, 0.45, 0.80, 0.60, 0.40, 0.30, 0.20, 0.10])
    pred = (s >= 0.5).astype(int)
    print(confusion_matrix(y, pred).ravel())                   # tn fp fn tp: [4 2 1 3]
    prf = precision_recall_fscore_support(y, pred, average="binary")[:3]
    print(np.round(prf, 3))                                    # [0.6   0.75  0.667]
    print(roc_auc_score(y, s), round(average_precision_score(y, s), 3))   # 0.875 0.854
    y2, s2 = np.r_[y, y[4:]], np.r_[s, s[4:]]                  # six more negatives, same scores
    p2, r2 = precision_recall_fscore_support(y2, (s2 >= 0.5).astype(int), average="binary")[:2]
    print(round(p2, 3), r2, roc_auc_score(y2, s2), round(average_precision_score(y2, s2), 3))
    # 0.429 0.75 0.875 0.775
    
    # (B) sample size, SRM check, z-test and confidence interval
    za, zb = stats.norm.ppf(0.975), stats.norm.ppf(0.80)
    n = (za + zb) ** 2 * (0.10 * 0.90 + 0.105 * 0.895) / 0.005 ** 2
    print(round(n))                                            # 57760
    print(round(stats.chisquare([70240, 69760]).pvalue, 3))    # 0.2: no sample-ratio mismatch
    print(round(stats.chisquare([70700, 69300]).pvalue, 5))    # 0.00018: mismatch, debug first
    xa, na, xb, nb = 7024, 70240, 7360, 69760
    pa, pb, pool = xa / na, xb / nb, (xa + xb) / (na + nb)
    z = (pb - pa) / np.sqrt(pool * (1 - pool) * (1 / na + 1 / nb))
    print(round(z, 2), round(2 * stats.norm.sf(abs(z)), 5))   # 3.39 0.00069
    se = np.sqrt(pa * (1 - pa) / na + pb * (1 - pb) / nb)
    print(np.round(100 * np.array([pb - pa - za * se, pb - pa + za * se]), 2))   # [0.23 0.87]
    
    # (C) simulation: power at n = 57,760, and an A/A test
    rng = np.random.default_rng(0)
    def rejection_rate(p_a, p_b, n, reps=20_000):
        a, b = rng.binomial(n, p_a, reps), rng.binomial(n, p_b, reps)
        pool = (a + b) / (2 * n)
        z = (b - a) / n / np.sqrt(pool * (1 - pool) * 2 / n)
        return np.mean(np.abs(z) > za)
    print(rejection_rate(0.10, 0.105, 57_760))   # 0.796 (about 0.80; Monte Carlo error 0.003)
    print(rejection_rate(0.10, 0.100, 57_760))   # 0.049 (about 0.05: the A/A test)
    

    6. Where it shows up #

    • ML practice. Model selection is bias–variance in action. Offline metrics choose a candidate; an online A/B test decides, and offline and online results often disagree. Gradient-boosted trees (XGBoost, LightGBM, CatBoost) are still the default for tabular data.
    • Rare events. Fraud, spam, defects and disease screening all have low prevalence. Precision at the operating point and the base-rate effect of section 3.6 decide whether the model is useful.
    • ML for science. Random splits of gridded weather fields or seismic records leak through spatial and temporal correlation; use blocks in space and time, and keep all records of one station, patient or event in one fold. Probabilistic forecasts need calibration (reliability diagrams). Comparing two models on the same test cases is a paired test: bootstrap over the cases and report an interval.
    • Product teams. Every launch decision for a ranking, recommendation or pricing model goes through an A/B test with the design checklist of section 3.7.

    7. Common mistakes #

    • "The p-value is the probability that H0H_0 is true." It is the probability of data at least this extreme, computed under H0H_0.
    • Accuracy or ROC-AUC alone on rare events. Report precision and recall at the operating point, or AP.
    • Preprocessing on all data before cross-validation. Scaling, feature selection and target encoding must be fitted inside each training fold.
    • Random splits of grouped or time-ordered data. Split by group or by time.
    • Stopping an A/B test at the first p<0.05p < 0.05, or ignoring a sample-ratio mismatch.
    • Analyzing per page view while randomizing per user. The naive variance is too small.
    • "Random forests cannot overfit." More trees do not add overfitting, but deep trees on noisy data still have variance, and the floor ρσ2\rho\sigma^2 stays. "Boosting reduces variance" is also wrong as a main claim: boosting mainly reduces bias.
    • Confusing sensitivity with P(D∣+)P(D \mid +). Always bring in the base rate.

    8. Check yourself #

    1. 100 trees, each with variance 1 and pairwise correlation 0.3. What is the variance of their average, and how do you lower it further?
      Answer0.3+0.7/100=0.3070.3 + 0.7/100 = 0.307. More trees can only remove the 0.0070.007. To go lower, decrease ρ\rho (fewer features per split, more randomness) or the variance of each tree.
    2. Baseline conversion 5%, relative MDE 10%, α=0.05\alpha = 0.05, power 0.8. How many users per arm (rule of thumb)? And for a relative MDE of 5%?
      Answerδ=0.05⋅0.10=0.005\delta = 0.05 \cdot 0.10 = 0.005, so n≈16⋅0.05⋅0.95/0.0052=30,400n \approx 16 \cdot 0.05 \cdot 0.95/0.005^2 = 30{,}400 per arm. Half the MDE needs 4 times as many: 121,600121{,}600.
    3. In worked example 4A, which of recall, precision, ROC-AUC and AP change when the test set gets six more negatives with the same scores?
      AnswerRecall (0.75) and ROC-AUC (0.875) do not change: each is computed inside one class. Precision (0.60→0.430.60 \to 0.43) and AP (0.854→0.7750.854 \to 0.775) fall, because they depend on the class ratio.
    4. A missed defect costs 50 and a false alarm costs 5. Your model is calibrated. Above which probability do you flag a part?
      AnswercFP/(cFP+cFN)=5/55≈0.091c_{FP}/(c_{FP} + c_{FN}) = 5/55 \approx 0.091. Flag when p>0.091p > 0.091.
    5. A colleague checks the A/B test every day for 10 days and stops when p<0.05p < 0.05. What is the false-positive rate, and what are two fixes?
      AnswerAbout 0.19 instead of 0.05 for 10 equally spaced looks. Fix the sample size and analyze once; or use a sequential test with alpha spending or always-valid p-values.
    6. You randomize by user, and the metric is clicks per page view. Why is the usual two-proportion test wrong here?
      AnswerThe page views of one user are correlated, so they are not independent trials. The test treats each view as independent and underestimates the variance, which gives too many false positives. Compute one value per user (clicks/views per user, or totals with the delta method) or bootstrap over users.
    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. State the bias–variance decomposition for squared loss at a point xx.

    2. Why does the lasso give exact zeros and ridge does not?

    3. Variance of the average of MM models, each with variance σ2\sigma^2 and pairwise correlation ρ\rho?

    4. What does each tree of gradient boosting fit?

    5. Define precision and recall.

    6. Why can ROC-AUC look excellent on a fraud model that is useless in practice?

    7. Rule-of-thumb sample size per arm for a conversion rate pp and absolute MDE δ\delta (α=0.05\alpha = 0.05, power 0.8)?

    8. What does peeking do to an A/B test?

    9. How do you detect a sample-ratio mismatch, and what does it mean?

    10. Prevalence 0.5%, sensitivity 99%, specificity 99%. What is P(D∣+)P(D \mid +)?

    Open the review →

    9. Interview angle #

    Recognize the question from the wording.

    The question says Start with
    "my model overfits / underfits", "train 2%, val 15%" bias vs variance; the learning-curve diagnosis; one fix per direction
    "L1 or L2?", "feature selection" soft thresholding, the diamond corner, the priors, the elastic net for correlated groups
    "bagging vs boosting", "why does a random forest work" ρσ2+(1−ρ)σ2/M\rho\sigma^2 + (1-\rho)\sigma^2/M; the comparison table
    "99% accurate", "fraud", "rare disease" base rate, confusion matrix, precision at the operating point, AP
    "is the model calibrated?" reliability diagram, log-loss or Brier, temperature or isotonic scaling on held-out data
    "how do you split the data?" groups, time, space; where leakage enters; preprocessing inside the fold
    "a test is 99% accurate, the person tests positive" Bayes' rule, then natural frequencies
    "design an experiment", "how long should the test run?" the 7-step checklist; n≈16 p(1−p)/δ2n \approx 16\,p(1-p)/\delta^2; full weeks
    "the result is significant on day 3" peeking; fixed horizon or sequential design

    Typical questions and short model answers.

    1. "Explain the bias–variance trade-off." — "At a fixed input, the expected squared error splits into noise, bias squared and variance. Bias is how far the average model is from the truth; variance is how much the model changes with a new training set. Flexible models have low bias and high variance. Regularization, more data and averaging lower the variance. I diagnose it from learning curves: a large train–validation gap means variance; high error on both means bias."
    2. "Bagging vs boosting?" — "Bagging trains deep trees in parallel on bootstrap samples and averages them. The average of MM models with correlation ρ\rho has variance ρσ2+(1−ρ)σ2/M\rho\sigma^2 + (1-\rho)\sigma^2/M, so bagging lowers variance, and random forests lower ρ\rho with random feature subsets. Boosting trains shallow trees in sequence; each tree fits the negative gradient of the loss at the current predictions. It lowers bias and needs early stopping. On tabular data, boosting usually wins on accuracy."
    3. "ROC-AUC or PR-AUC for fraud?" — "ROC-AUC measures ranking and ignores the class ratio. At 0.5% fraud, a threshold with 80% recall and 5% FPR gives a precision of only 7%, about 12 false alarms per caught fraud, with an excellent ROC-AUC. So I report average precision and the precision and recall at the operating threshold, chosen from the costs of the two errors."
    4. "How many users do we need for this A/B test?" — "I need the baseline rate, the minimum effect worth detecting, the level and the power. Then n=(z1−α/2+z1−β)2⋅2p(1−p)/δ2n = (z_{1-\alpha/2} + z_{1-\beta})^2 \cdot 2p(1-p)/\delta^2, about 16 p(1−p)/δ216\,p(1-p)/\delta^2. For 10% baseline and a 0.5-point lift that is about 58,000 per arm. I divide by the daily traffic per arm and round up to full weeks."
    5. "The PM checked the dashboard on day 3 and it is significant. Can we ship?" — "Not yet. If we look every day and stop at the first significant result, the false-positive rate is much higher than 5%, about 19% with 10 looks. We fixed the sample size in advance, so we wait. If early stopping matters, we use a sequential test with alpha spending. I also check the sample ratio and the guardrail metrics first."
    6. "A test is 99% sensitive and 99% specific; the disease has 0.5% prevalence. Positive result: probability of disease?" — "About one third. Of 100,000 people, 500 are sick and 495 test positive; of 99,500 healthy people, 995 test positive. So 495 of 1,490 positives are sick."

    Typical follow-ups. "What if the metric is revenue, not conversion?" (higher variance: more users, CUPED, or a capped metric). "What if users talk to each other?" (interference: cluster or market randomization, switchbacks). "Why not test 20 metrics?" (1−0.9520=64%1 - 0.95^{20} = 64\% chance of a false positive; one primary metric, corrections for the rest). "How do you choose kk in kk-fold?" (5 or 10; more folds lower bias of the estimate but cost more and have correlated folds).

    Mock prompt (A/B design). "A new ranking model raises offline NDCG by 2%. Design the online test." A strong answer contains: one primary metric (for example, sessions with a purchase per user) and guardrails (latency, revenue, errors); user-level randomization; the power calculation with a baseline and an MDE, and the duration in full weeks; an A/A test and an SRM check; no peeking, or a sequential design; a check for novelty effects; the decision rule fixed in advance; and a note on interference if the product is a marketplace.

    10. Practice #

    The notebook for this lesson runs in Colab. Each exercise has a check cell and a folded solution:

    1. bias_variance: estimate bias², variance and noise for polynomial fits by simulation, and check that they add up to the test MSE.
    2. precision_recall_at and cost_threshold: precision and recall at a threshold, and the threshold that minimizes the expected cost.
    3. sample_size_per_arm: the formula of section 3.7, checked against a simulation of the power.
    4. two_proportion_ztest: the z-test, checked against scipy.stats.chi2_contingency.
    5. srm_check: the sample-ratio check with scipy.stats.chisquare.

    Timed practice (25 minutes each, talking aloud): "explain the bias–variance trade-off with a picture"; "derive the sample-size formula"; "design an A/B test for a new search ranking"; "our model is 99% accurate on fraud; what do you ask?"; "implement ROC-AUC from scratch"; "expected number of coin flips until HH"; Chip Huyen's questions on evaluation and A/B testing (see below).

    Say it aloud drill. Start a 2-minute timer for each question of section 9. Answer with the four parts of "Why this matters" (definition, mechanism, trade-off, number). Record yourself, then compare with sections 3–4. Repeat the ones where you ran over time or had no number.

    11. Watch and read #

    The trade-off with one simple picture; compare it with the widget of section 2.1.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Trees fitted to residuals, with a learning rate: the squared-loss case of section 3.3.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    How the ROC curve is built from thresholds; near the end, why precision helps with rare positives.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Effect size, variance and the number of samples: the ideas behind the sample-size formula.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play
    Trustworthy experiments from a leading practitioner: the overall evaluation criterion, sample-ratio mismatch and common pitfalls.
    Open on YouTube · the player loads from youtube-nocookie.com when you press play