Unit 2A · Supervised Learning · Course Outcomes CO2, CO5
Six algorithms, one dataset, four boundaries
Supervised learning is one sentence: you have inputs paired with the right answers, and you want a rule that generalises to inputs you have not seen. Every algorithm in this unit is a different guess about what kind of rule to look for. This file runs all of them on the same eight students so the differences are visible rather than described.
Section 2.0
Eight students, two measurements
What separates the ones who passed?
Hours of focused practice per week on one axis, assignments submitted on the other. Four students failed, four passed. Nothing else is known about them.
Read the plot before reading any algorithm. The four failures sit toward the bottom left and the four passes toward the top right, and there is a clear empty corridor between them. Every method in this unit is an attempt to describe that corridor, and they disagree about its shape.
Sections 2.3.1 and 2.3.3
Draw one straight line
Which line, out of the infinitely many that work?
Logistic regression and support vector machines both answer with a line, and on this data both end up at hours + assignments = 6. They get there for completely different stated reasons — one maximises the likelihood of the labels, the other maximises the width of the empty corridor.
That they agree here is not a coincidence, and section 2.3.3 shows why. It is one of the more satisfying results in the unit.
Section 2.3.2
Ask the nearest students
Why fit anything at all?
k-nearest neighbours never builds a model. To classify a new student it finds the k most similar ones already on record and takes a vote. The boundary is whatever falls out of that rule, and it is not a line.
Cheap to train, expensive to predict, and completely at the mercy of how you measure similarity — change the units of one axis and the answer changes. Section 2.3.2 shows exactly that happening.
Section 2.3.4
Ask two yes/no questions
What if the rule has to be explainable?
A decision tree can only cut parallel to the axes, so it approximates the diagonal corridor with a staircase. Two questions are enough here: is practice at most 2.5 hours, and if not, are assignments at most 2.5?
The result is a rule you can say out loud, which is why trees survive in settings where a decision must be justified. The cost is that a diagonal boundary needs infinitely many steps to represent exactly.
What you need before this chapter
All of Unit 1, and four things from it in particular. The normal equations AᵀAθ = Aᵀy from 1.1, because 2.2 is the same solution with an extra column. The gradient descent update from 1.4, because it trains everything from 2.3.1 onward. Entropy and information gain from 1.5, because 2.3.4 is those formulas in a loop. And the bias–variance decomposition from 1.6, because 2.1 is about measuring it and 2.3.5 is about beating it.
Log loss from 1.3 and the sigmoid from 1.8 are used from section 2.3.1 without re-deriving them. If either is unfamiliar, read those two sections first — they are short.
This unit comes in two files
The syllabus gives Unit 2 thirteen hours, roughly twice any other unit, so it is split. This file is the algorithms: how each model is defined, trained, and hand-computed. The companion file is how you judge the result and what you owe the people affected by it.
| File | Sections | What it answers |
|---|---|---|
| 2A — this file | 2.1, 2.2, 2.3 | Which model, and how does it actually compute a prediction? |
| 2B — companion | 2.4, 2.5, 2.6 | Is it any good, what if labels are scarce, and is it fair to deploy? |
The dataset this file uses
Eight students. Two features, one continuous target, one binary label. Small enough that every algorithm below can be run entirely by hand, and structured so that the algorithms genuinely disagree.
| Student | Practice hours x₁ | Assignments x₂ | x₁+x₂ | Attended lab | Marks /20 | Result |
|---|---|---|---|---|---|---|
| A | 1 | 3 | 4 | yes | 9 | fail |
| B | 2 | 1 | 3 | no | 8 | fail |
| C | 2 | 3 | 5 | yes | 11 | fail |
| D | 3 | 2 | 5 | yes | 10 | fail |
| E | 3 | 4 | 7 | yes | 14 | pass |
| F | 4 | 3 | 7 | no | 12 | pass |
| G | 3 | 5 | 8 | yes | 17 | pass |
| H | 6 | 3 | 9 | no | 15 | pass |
The label is defined as marks ≥ 12, so the classification problem and the regression problem are two views of the same thing. That matters more than it sounds: section 2.2 predicts the marks and section 2.3 predicts the label, and comparing them is how you decide which question your own problem is really asking.
The sums every section reuses
Both means are exactly 3, which is what makes centring painless.
Four students deserve names you will keep seeing. C and D are the two failures closest to passing; E and F are the two passes closest to failing. Those four decide the support vector machine on their own, and every other student is irrelevant to it.
Architecture, Model Selection and Hyperparameters
Before any algorithm: what exactly is being chosen, who chooses it, and how you find out whether the choice was good without lying to yourself.
The question
Unit 1 ended with a warning: the model that fits your training data best is usually not the model you want. So you cannot pick a model by looking at how well it fits. What can you look at instead?
This section is the protocol that answers that. It is the least glamorous material in the unit and the most frequently botched, including in published work. Every algorithm after this one depends on it, because each arrives with knobs that somebody has to set.
The intuition
Think about revising for an exam using a book of practice questions that has answers in the back.
If you check the answer immediately after each question, you will feel confident, because you are measuring your ability to read an answer you have just seen. That is training error. To find out what you actually know, you must cover some questions, attempt them cold, and only then check. Those covered questions are a validation set.
Now the subtle part. Suppose you use those covered questions to decide which revision technique works best — flashcards or rewriting notes. After trying six techniques and picking the winner, that set of questions has been used to make a decision, so your score on it is optimistic too. You need a third batch, untouched, to get an honest number. That third batch is the test set, and the reason people get poor results in practice is almost always that they only had two.
The formal treatment
Two kinds of number live inside every model, and confusing them is the root of most of the trouble.
| Parameter | Hyperparameter | |
|---|---|---|
| Set by | The training algorithm, from data | You, before training starts |
| Chosen to | Minimise training loss | Minimise validation loss |
| Examples | w₀, w₁, w₂ of a regression; a tree's split thresholds; an SVM's α values | λ, learning rate η, k in k-NN, tree depth, SVM's C and kernel, number of trees |
| Count on the spine | 3 for the regression of 2.2 | 1 for ridge, 1 for k-NN, 2 or more for an SVM |
The word architecture covers the choices that are structural rather than numeric: which model family, which features, how features are encoded, whether the target is a number or a class. Architecture decisions sit above hyperparameters and are usually made by argument rather than by search.
The three-way split, and why it is not enough here
Applied to the spine, a 60/20/20 split of eight rows gives five, two and two. A two-row validation set can only report accuracies of 0, 0.5 or 1, so it cannot distinguish between models that differ by less than half. This is not a contrived problem: it is the normal situation for medical, industrial and educational datasets, and it is why cross-validation exists.
k-fold cross-validation
Stratified k-fold keeps the class proportions of each fold equal to the whole, which matters whenever a class is scarce — an unstratified fold can easily contain no positive cases at all, making its score meaningless. Leave-one-out cross-validation is the extreme k = n: maximum training data per fit, n fits, and a high-variance estimate because the n training sets are nearly identical to each other.
Depth — what the standard deviation across folds is telling you
Two models can share a mean CV accuracy of 0.85 while one scores 0.85, 0.85, 0.85, 0.85 and the other 1.0, 1.0, 0.7, 0.7. The means are useless on their own. Large fold-to-fold spread means the estimate itself is unreliable, usually because folds are small, and it is a reason to distrust a ranking, not to prefer the model with the luckier folds.
A common and defensible rule: prefer the simplest model whose mean CV score is within one standard error of the best. On small data this rule will often keep you from chasing noise, and it is the same instinct as the regularization of 1.7 applied to model selection rather than to parameters.
Searching the hyperparameter space
| Strategy | How it works | When it is right |
|---|---|---|
| Grid search | Every combination of a list of values per hyperparameter | Two or three hyperparameters with known plausible ranges. Exhaustive and reproducible. |
| Random search | Sample combinations from distributions over the ranges | Four or more hyperparameters, or when you suspect only one or two of them matter. Usually beats grid search per unit of compute. |
| Bayesian / sequential | Model the validation score as a function of the hyperparameters and sample where the model is optimistic | Each fit is expensive — large networks, long training runs. |
Search on a logarithmic scale for anything that spans orders of magnitude, which covers λ, C, η and kernel widths. Trying λ ∈ {1, 2, 3, 4} explores almost nothing; λ ∈ {0.01, 0.1, 1, 10, 100} explores five decades with fewer fits.
The worked example
Worked 2.1a — cross-validation to choose k for k-nearest neighbours
every fold by handk using stratified 4-fold cross-validation, then check the answer with leave-one-out. Distances tie often on this data, so fix the rule first: when several training rows tie at the k-th distance, every tied row votes.Pair each failure with a pass so every fold holds one of each: fold 1 {A, E}, fold 2 {B, F}, fold 3 {C, G}, fold 4 {D, H}. Each fold is scored by a model that has seen only the other six students.
Compare squared distances throughout. Ranking by d² is identical to ranking by d, and on integer coordinates d² is always a whole number.
Training set {A, C, D, E, G, H}. Classify B(2,1) and F(4,3).
Fold 2 therefore scores 1 out of 2. A deadlock counts as an error, because a classifier that cannot answer has not answered.
Student F is worth remembering. It is one of the two passes sitting hardest against the boundary, its single nearest neighbour is the failure D, and it deadlocks again at k = 1 and k = 3 under leave-one-out. Every scheme below loses exactly one row, and it is nearly always F.
| k | fold 1 {A,E} | fold 2 {B,F} | fold 3 {C,G} | fold 4 {D,H} | mean | std dev |
|---|---|---|---|---|---|---|
| 1 | 1.00 | 0.50 | 1.00 | 1.00 | 0.8750 | 0.2165 |
| 3 | 1.00 | 0.50 | 1.00 | 1.00 | 0.8750 | 0.2165 |
| 5 | 0.50 | 1.00 | 1.00 | 1.00 | 0.8750 | 0.2165 |
| k | correct | LOOCV accuracy | which row is lost, and why |
|---|---|---|---|
| 1 | 7 / 8 | 0.8750 | F deadlocks: D and E are both at d² = 2, one of each class |
| 3 | 7 / 8 | 0.8750 | F deadlocks again, now 2–2 among C, D, E, H |
| 5 | 6 / 8 | 0.7500 | E and G are outvoted — a five-row neighbourhood reaches across the boundary |
Leave-one-out mildly prefers
k = 1 or k = 3 (0.8750) over k = 5 (0.7500).The honest conclusion is not a value of
k but a statement about the experiment: eight rows cannot distinguish these models. The standard deviation of 0.2165 was announcing that before the means were even compared.This is the normal outcome on small data and it is not a failure of method. What you do with it is choose on other grounds — prefer k = 3 over k = 1 because a single neighbour is the highest-variance choice available, and say in writing that the data did not decide. Reporting "cross-validation selected k = 3" would imply evidence that does not exist.
Worked 2.1b — how many model fits does a search cost?
budget arithmeticC ∈ {0.1, 1, 10, 100} and γ ∈ {0.01, 0.1, 1} with 5-fold cross-validation. How many fits? What if you add a third hyperparameter with five values? And how does random search compare?Grid search is exponential in the number of hyperparameters. Three knobs is usually the practical ceiling.
Suppose the top 5% of the hyperparameter space would be good enough. Each random draw independently lands in that region with probability 0.05, so the chance of missing it every time is 0.95ᵀ for T draws.
| draws T | 0.95ᵀ = P(miss) | P(hit) |
|---|---|---|
| 20 | 0.3585 | 0.6415 |
| 30 | 0.2146 | 0.7854 |
| 60 | 0.0461 | 0.9539 |
| 100 | 0.0059 | 0.9941 |
Random: 60 draws finds a top-5% setting with probability 0.9539, and the number does not change when you add a fourth hyperparameter.
That independence from dimension is the whole argument for random search. Grid search also wastes its budget: with 12 grid points, only 4 distinct values of
C are ever tried, whereas 12 random draws try 12.The visualization
Where each row goes, fold by fold
interactive — step the foldsThe pitfalls
Where marks are lost
- Tuning on the test set. The single most common error in the field. Once you have compared two models on the test set and kept the winner, the number you report is a validation number wearing a test set's name.
- Scaling before splitting. Computing a mean and standard deviation over the whole dataset and then splitting leaks information from validation into training. Fit the scaler on the training fold only, then apply it to the others. The same rule covers imputation, feature selection and target encoding.
- Unstratified folds on imbalanced data. With 5% positives and five folds, a fold can easily contain zero positives, making its recall undefined and its accuracy meaningless.
- Random folds on grouped or time-ordered data. If the same patient, user or document appears in several rows, random splitting puts near-duplicates on both sides and the score becomes fantasy. Use grouped splits; for time series, always train on the past and validate on the future.
- Reading a mean CV score without its spread. See the depth box. The spread is half the result.
- Searching a linear grid for a log-scaled hyperparameter.
λ ∈ {1,2,3,4}covers less ground than{0.01, 1, 100}does with fewer fits. - Treating a hyperparameter as a parameter. Choosing
korλto minimise training error always returns the most flexible setting:k = 1andλ = 0both give zero training error and neither is a choice.
Practice
P2.1.1 (direct) — A dataset has 500 rows. You use 5-fold cross-validation to compare 3 models, each with 8 hyperparameter settings. How many model fits, and how many rows train each fit?
Fits: 3 models × 8 settings × 5 folds = 120. Each fit trains on 4 of the 5 folds, so 500 × 4/5 = 400 rows, and validates on 100.
Then one final fit on all 500 rows using the winning configuration, before the single test-set evaluation — 121 fits in total. That last refit is standard and often forgotten: cross-validation is used to choose, and the shipped model is retrained on everything the choice was allowed to see.
P2.1.2 (variation) — Repeat Worked 2.1a with the unstratified folds {A,B}, {C,D}, {E,F}, {G,H} at k = 1 and k = 3. Compare with the stratified result and say what caused the difference.
At k = 3 the unstratified estimate collapses to chance while the stratified one holds at 0.8750, on identical data with an identical model. The cause is the class balance of each training set. Holding out {C,D} leaves six rows that are 2 failures against 4 passes, so any three-row neighbourhood is more likely to be majority-pass simply by prevalence — and the rows being validated are the two failures. Holding out {E,F} does the mirror image.
Two lessons. Unstratified folds do not merely add noise, they add bias, and the bias points against whichever class the fold over-represents. And k = 3 is more exposed than k = 1, because a vote among more neighbours draws harder on the training prevalence. This is why stratification is the default rather than a refinement, and it is also a preview of the class-imbalance problem that Unit 2B is largely about.
P2.1.3 (interpretation) — A colleague reports: "I ran 5-fold CV on 40 configurations, picked the best (CV accuracy 0.91), and it got 0.88 on the test set. The small drop proves it generalises." Assess this claim.
The drop is expected, not reassuring, and the reasoning has the logic backwards. Taking the maximum over 40 CV scores is an optimistically biased estimate of the winner's true accuracy, because part of what made it the maximum was luck in the fold assignment. The test score of 0.88 is the honest number; the 0.91 was inflated by the selection.
What the pair does tell you is that the inflation was small, roughly 3 points, which suggests the folds were large enough that the selection did not overfit badly. What it does not tell you is that 0.88 is good, or that this model beats the runner-up — the runner-up might well test higher. And the one thing your colleague must not now do is try a 41st configuration and report its test score.
P2.1.4 (synthesis) — Using section 1.6, explain what cross-validation is estimating, which of the three terms of the bias–variance decomposition it can and cannot see, and why leave-one-out has higher variance than 5-fold despite training on more data.
Cross-validation estimates expected test error, which by 1.6 is bias² + variance + σ². It sees only the sum. It cannot separate the three terms, because doing so requires many independent training sets drawn from the true distribution and knowledge of the true function — neither of which you have. The decomposition tells you what to do about a large CV error; it is the train-versus-validation gap, not CV itself, that hints at which term dominates.
Leave-one-out's variance has two sources. Its n training sets overlap in n−2 rows, so the n scores are strongly positively correlated and averaging them reduces variance far less than averaging independent scores would — the same ρ + (1−ρ)/B effect that limits ensembles in 2.3.5. And each individual score is a single 0-or-1 outcome, the noisiest possible measurement. Five-fold trades a little extra bias, from training on 80% rather than (n−1)/n of the data, for markedly less variance, which is why it is the usual default.
Linear Regression and Regularization
Unit 1 fitted a line to one feature. Two features change nothing about the algebra and everything about how you are allowed to interpret the answer.
The question
You now have two measurements per student instead of one. Practice hours predicted marks reasonably; assignments predicted them better. Does using both beat using either, and by how much? And once the model has two coefficients, what does one of them actually mean?
That second question is where regression stops being arithmetic. A coefficient in a multiple regression does not mean what a coefficient in a simple regression means, and reading it wrongly is how confident, competent people reach false conclusions from correct fits.
The intuition
With one feature you fit a line through a scatter plot. With two you fit a flat sheet — a plane — through a cloud of points floating above the floor. The plane is still described by "where it sits" plus "how steeply it tilts", except now it tilts in two independent directions, so there are two slopes instead of one.
The interpretation of each slope is the part worth slowing down for. w₁ is not "how much marks rise with practice hours". It is "how much marks rise with practice hours among students whose assignment count is the same". The plane tilts along one axis while the other is held still. If practice and assignments were strongly related to each other, there might be very few real students at any given assignment count who differ in practice hours — and then the model is answering a question the data barely contains.
Here the two features are only weakly related, correlation 0.1581, so the model is on comfortable ground. That is a fact you check, not one you assume.
The formal treatment
The model, in the notation Unit 1 established:
Nothing new. "Linear" has always meant linear in the parameters, never in the features, which is why adding x² or log x or x₁x₂ as extra columns keeps the problem linear and keeps the closed form available.
The centred form, which is what you use by hand
Subtracting the means from every feature and from the target removes the intercept from the system, shrinking a 3×3 solve to a 2×2 one. The intercept is recovered at the end from the fact that the fitted surface passes through the point of means.
Reading the fit: R² and its adjusted cousin
Plain R² rises even when you add a column of random numbers, because the extra parameter can always absorb a little noise. Adjusted R² charges for each parameter, so it falls when a feature earns less than it costs. On eight rows the correction is severe — (n−1)/(n−d−1) = 7/5 for two features — and that severity is appropriate.
Depth — why R² is not additive across features
On the spine, practice hours alone reach R² = 0.3676 and assignments alone reach 0.7118. Adding those gives 1.0794, which is impossible. Together they reach 0.9412.
The two features share some of their explanatory power, because they are correlated with each other. That shared portion is counted once by the joint model and twice by the sum of the separate models. Only when features are exactly uncorrelated — S₁₂ = 0 — does the joint R² equal the sum, and only then do the two coefficients equal what two separate simple regressions would give. Everything difficult about interpreting multiple regression follows from that one condition rarely holding.
Multicollinearity, formally
If one feature is an exact linear combination of others, the columns of A are dependent, AᵀA is singular, and infinitely many parameter vectors achieve the identical minimum — the flat-floored trough of 1.2. Near-dependence is worse in practice than exact dependence, because the fit still runs and returns huge coefficients of opposite sign that swing wildly with a small change in the data. Variance inflation factor is the standard diagnostic: regress feature j on all the others, and VIFⱼ = 1/(1 − R²ⱼ). A VIF above about 10 is the usual warning line.
Regularization, applied
Unit 1.7 introduced ridge and lasso on a single coefficient. With two, the difference between them becomes visible for the first time, because now there is something to select between.
Adding λ to the diagonal is why ridge is also called Tikhonov regularization: it makes a singular matrix invertible, so ridge has a unique answer even under exact multicollinearity, where ordinary least squares has none. That is a stronger selling point than the shrinkage.
The worked example
Worked 2.2a — fit the plane, exactly
closed form, two featuresmarks ≈ w₀ + w₁·hours + w₂·assignments to the eight students. Report the coefficients, the residuals, and MSE, R² and adjusted R².Everything the fit needs. Both feature means are 3 and the mark mean is 12, which is why this stays arithmetic.
| Student | x₁ | x₂ | marks | predicted | r = ŷ − y | r² |
|---|---|---|---|---|---|---|
| A | 1 | 3 | 9 | 10 | +1 | 1 |
| B | 2 | 1 | 8 | 7 | −1 | 1 |
| C | 2 | 3 | 11 | 11 | 0 | 0 |
| D | 3 | 2 | 10 | 10 | 0 | 0 |
| E | 3 | 4 | 14 | 14 | 0 | 0 |
| F | 4 | 3 | 12 | 13 | +1 | 1 |
| G | 3 | 5 | 17 | 16 | −1 | 1 |
| H | 6 | 3 | 15 | 15 | 0 | 0 |
| Σ | 24 | 24 | 96 | 96 | 0 | 4 |
Only four students are missed at all, each by exactly one mark. Residuals sum to zero, as the intercept guarantees.
Baseline for comparison: predicting 12 for everyone gives MSE = 68/8 = 8.5, so the model cuts squared error by a factor of 17.
Say the model out loud, because that is the deliverable. A student with no practice and no assignments is predicted 3 marks out of 20. Each additional hour of weekly practice is worth 1 mark. Each additional assignment submitted is worth 2 marks — twice as much, per unit, and the pass threshold is 12.
The advice that falls out is that one more assignment beats one more hour of practice. That advice is only sound if the two features are genuinely separable in the data, which is exactly what the correlation of 0.1581 was checked for.
Worked 2.2b — one feature, two features, and why the pieces do not add up
interpretationEvery coefficient shrank when its partner joined. Hours went from 1.2500 to 1.0000, assignments from 2.2000 to 2.0000. The simple regression on hours was quietly taking credit for the assignments that hard-working students also tend to submit. Controlling for assignments removes that borrowed credit.
The R² values do not add. 0.3676 + 0.7118 = 1.0794, which exceeds 1. See the depth box: the shared portion is double-counted by the sum.
The joint model is much better than either. MSE falls from 2.4500 to 0.5000 — nearly a factor of five — because the two features carry largely different information. Had they been near-duplicates, the second would have bought almost nothing while still costing a parameter.
A regression coefficient is a property of the whole model, not of the feature. Quoting
w₁ = 1 as "the effect of practice" without naming what was held fixed is the single most common misreading of a regression table.Worked 2.2c — ridge and lasso on two coefficients
the paths divergeλ = 0 upward. Which coefficient does lasso eliminate first, and at what λ?Add λ to each diagonal entry and re-solve. At λ = 4:
| λ | w₁ hours | w₂ assignments | w₀ | train MSE | ‖w‖² | objective |
|---|---|---|---|---|---|---|
| 0 | 1.0000 | 2.0000 | 3.0000 | 0.5000 | 5.0000 | 0.5000 |
| 1 | 0.9617 | 1.8251 | 3.6393 | 0.5445 | 4.2561 | 1.0765 |
| 2 | 0.9245 | 1.6792 | 4.1887 | 0.6521 | 3.6746 | 1.5708 |
| 4 | 0.8551 | 1.4493 | 5.0870 | 0.9610 | 2.8315 | 2.3768 |
| 10 | 0.6899 | 1.0310 | 6.8372 | 2.0162 | 1.5390 | 3.9399 |
| 40 | 0.3419 | 0.4263 | 9.6953 | 4.9795 | 0.2987 | 6.4728 |
| 100 | 0.1690 | 0.1969 | 10.9022 | 6.6941 | 0.0673 | 7.5359 |
Notice what the intercept does. As the slopes are squeezed toward zero, w₀ climbs toward the mark mean of 12, because the model degenerates into predicting the average. The intercept is unpenalised precisely so that it can absorb this.
| λ | w₁ hours | w₂ assignments | w₀ | non-zero |
|---|---|---|---|---|
| 0 | 1.0000 | 2.0000 | 3.0000 | 2 |
| 5 | 0.8718 | 1.7756 | 4.0577 | 2 |
| 10 | 0.7436 | 1.5513 | 5.1154 | 2 |
| 20 | 0.4872 | 1.1026 | 7.2308 | 2 |
| 40 | 0.0000 | 0.2000 | 11.4000 | 1 |
| 60 | 0.0000 | 0.0000 | 12.0000 | 0 |
Between λ = 20 and λ = 40 the hours coefficient hits exactly zero and stays there; by λ = 60 both are gone and the model is the constant 12. Ridge never reaches zero at any finite λ — at 100 it is still reporting 0.1690 and 0.1969.
R² = 0.3676 against 0.7118.Ridge shrinks both and eliminates neither. Ridge divides, lasso subtracts, and only subtraction can reach zero.
So the question you are asking chooses the penalty: "which features matter" needs lasso; "give me a stable model using all of them" needs ridge.
One warning about that first sentence. Lasso's choice among correlated features is unstable: with two near-duplicate features it keeps one essentially arbitrarily, and a small change to the data can flip which. Elastic net — penalising αΣ|w| + (1−α)Σw² — exists to keep the selection while stabilising it, and it is what you reach for when lasso's answer will not sit still.
The visualization
The fitted plane, and what regularization does to it
interactive — slide λ, switch penaltyFeature space — contours of predicted marks
Coefficient path
The left panel draws lines of equal predicted marks. Because the model is a plane, those lines are straight and parallel, and their direction is set by the ratio w₁ : w₂ while their spacing is set by the overall magnitude. Watch what regularization changes: ridge keeps the direction almost fixed and widens the spacing, lasso rotates the lines until they become vertical at the moment w₁ hits zero. That rotation is feature selection, seen geometrically.
The pitfalls
Where marks are lost
- Reading a coefficient without its condition.
w₂ = 2means two marks per assignment at fixed practice hours. Drop the condition and the sentence is false, as Worked 2.2b demonstrates numerically. - Comparing raw coefficients across features on different scales. If assignments were recorded as a percentage instead of a count,
w₂would be 100× smaller and nothing about the model would have changed. Standardise before comparing magnitudes, or comparewⱼ × sd(xⱼ). - Penalising the intercept. Shrinking
w₀toward zero pulls predictions toward zero rather than toward the mean, which is never what you want. Exclude it, and note that this is also why you centre. - Regularizing unstandardised features. The penalty
Σwⱼ²charges each coefficient equally, so a feature measured in large units gets a naturally small coefficient and is barely penalised. Standardise first or the choice of units silently decides which feature survives. - Choosing
λby training error. Training MSE increases monotonically withλ— look at the ridge table. Minimising it always returnsλ = 0. Use validation, per 2.1. - Treating a lasso zero as proof of irrelevance. It means that feature was not worth its penalty given the others present at this
λ. Changeλ, or drop a correlated partner, and it can return. - Adding features because R² went up. It always goes up. Adjusted R² or validation error are the honest tests, and on the spine the correction factor is 7/5.
Practice
P2.2.1 (direct) — Fit y = w₀ + w₁x₁ + w₂x₂ to five rows with n = 5, Σx₁ = 15, Σx₂ = 10, Σy = 40, Σx₁² = 55, Σx₂² = 30, Σx₁x₂ = 34, Σx₁y = 134, Σx₂y = 88.
Model: ŷ = 3.5714 + 1.2857x₁ + 0.2857x₂. Check by the point of means: 3.5714 + 1.2857(3) + 0.2857(2) = 8.0000 = ȳ. Note the feature correlation here is 4/√100 = 0.4, appreciably stronger than the spine's 0.1581, so these two coefficients are more entangled than the spine's are.
P2.2.2 (variation) — Return to the spine and add a third feature x₃ = x₁ + x₂, "total effort". What happens to the fit, to the coefficients, and to ridge?
The design matrix now has columns [1, x₁, x₂, x₁+x₂], and the fourth is the sum of the second and third. Its rank is 3 while d + 1 = 4, so AᵀA is 4×4 with determinant exactly 0 and no inverse. Ordinary least squares has no unique answer.
The predictions are unchanged: any (w₁, w₂, w₃) with w₁ + w₃ = 1 and w₂ + w₃ = 2 reproduces the identical plane, so (1, 2, 0), (0, 1, 1) and (−5, −4, 6) all fit exactly as well. The fit survives; the interpretation does not.
Ridge repairs this. Adding λ to the diagonal makes the matrix invertible for any λ > 0, and among the infinitely many equally good solutions it selects the one of smallest ‖w‖² — which spreads the effect across the correlated columns rather than concentrating it arbitrarily. That is Tikhonov regularization doing the job it was invented for, and it is why ridge is the default first response to multicollinearity.
P2.2.3 (interpretation) — A housing model reports R² = 0.86 and, among its coefficients, "number of bedrooms" is −12,400. Floor area is also a feature. Explain the negative sign without assuming the fit is wrong.
The fit is probably fine and the coefficient probably means what it says: among houses of the same floor area, one with more bedrooms sells for less. That is plausible — it describes the same square footage chopped into more, smaller rooms, and buyers pay for space.
What has gone wrong is only the reading. Bedrooms and floor area are strongly correlated, so the marginal relationship between bedrooms and price is positive while the partial relationship, holding area fixed, is negative. Both are true statements about different questions. Regress price on bedrooms alone and the sign will flip.
Three things to do before quoting either number. Check the VIF for bedrooms; if it is 10 or more, the coefficient is estimated from very little independent variation and its value is unstable even if its sign is right. Consider a more interpretable parameterisation, such as floor area plus average room size. And never present a partial coefficient to a non-technical audience without stating what was held fixed, because "an extra bedroom costs you twelve thousand" is how a correct model becomes a false headline.
P2.2.4 (synthesis) — The spine's label is marks ≥ 12. Use the fitted plane to classify all eight students, compare with the true labels, and say what this tells you about using regression for classification.
Predict a pass when 3 + x₁ + 2x₂ ≥ 12, that is x₁ + 2x₂ ≥ 9. Evaluating: A 1+6=7 fail, B 2+2=4 fail, C 2+6=8 fail, D 3+4=7 fail, E 3+8=11 pass, F 4+6=10 pass, G 3+10=13 pass, H 6+6=12 pass. All eight correct.
So on this data the regression route works, and it even gives a boundary — x₁ + 2x₂ = 9 — that differs from the x₁ + x₂ = 6 that 2.3.1 and 2.3.3 will find. Different because it was fitted to a different target: the marks, not the labels.
Three reasons not to make a habit of it. Least squares punishes a prediction of 19 for a passing student as heavily as a prediction of 5, although only one of those is an error — squared loss on a thresholded target charges for being too right. The output is not a probability and cannot be calibrated into one; nothing keeps it inside [0,1]. And a single extreme mark drags the boundary, because 1.3 showed how far one outlier moves a squared-error fit, whereas the classifiers of 2.3 would barely notice. Predicting the number is the better choice when you need the number; when you need the class, use a loss designed for classes.
Logistic Regression
A linear model that outputs a probability, trained by the loss that probability deserves. The default classifier, and the one every other model is measured against.
The question
Section 2.2 predicted a mark and then thresholded it, and P2.2.4 showed why that is uncomfortable: squared loss charges you for predicting 19 when the student scored 14, although both are passes. What you want instead is a model whose output is the probability of passing, and a loss that only cares about how much probability it put on the right answer.
The intuition
Keep the linear part. Compute a weighted sum of the features, exactly as in regression, and call it a score: high score means likely to pass, low score means likely to fail. The score is unbounded, running from minus infinity to plus infinity, and a probability must sit between 0 and 1, so squash it.
The sigmoid from 1.8 does the squashing. A score of 0 becomes a probability of 0.5 — complete indecision — and the probability approaches 1 or 0 as the score grows large in either direction. So logistic regression is two steps: a straight line in the feature space that says which side you are on, and a squash that turns "how far from the line" into "how confident".
That is the whole model. The decision boundary is a straight line because the score is linear; the confidence varies smoothly because the sigmoid is smooth. Everything else in this section is about how the line's coefficients are found and what they mean.
The formal treatment
The name comes from inverting the sigmoid. Solving p = σ(z) for z gives the log-odds, or logit:
That multiplicative reading is how logistic coefficients are reported in medicine, epidemiology and credit scoring. An odds ratio of e^(wⱼ) is a statement you can quote; the coefficient itself is not.
The loss, and why not squared error
Compare that with the MSE gradient of 1.4, (2/n)Σ(ŷᵢ − yᵢ)xᵢⱼ. Identical in form: error times feature, averaged. The cancellation that produces it is the sigmoid-and-log-loss identity from 1.8, and it is the reason log loss is the natural partner for a sigmoid output rather than merely a convenient one.
There is no closed form. J is convex in θ, so by 1.2 gradient descent finds the global optimum, but the stationarity equations are transcendental and must be solved iteratively.
Depth — on separable data the optimum does not exist
The eight students are perfectly separable. Take any boundary that classifies all eight correctly and double every weight: every correct probability moves closer to 1, so the log loss strictly decreases. Double again and it decreases again. There is no finite minimiser — the loss approaches 0 as ‖w‖ → ∞, and unregularised gradient descent will run forever with the weights growing without bound.
What converges is the direction. Running gradient descent on the spine for 2000 steps gives ‖w‖ = 8.80 and a log loss of 0.000994, yet the boundary has settled at x₁ + x₂ = 5.9993 and stops moving. That limiting direction is exactly the maximum-margin hyperplane of 2.3.3. It is a genuine theorem, not a coincidence of this dataset: unregularised logistic regression on separable data converges in direction to the hard-margin SVM solution.
The practical consequence is that you regularize logistic regression by default. Adding λ‖w‖² restores a finite unique optimum, and the hyperparameter is usually exposed as C = 1/(2λ), so large C means weak regularization.
Beyond two classes
Softmax regression, also called multinomial logistic regression, replaces the sigmoid with the softmax of 1.8: one score per class, exponentiate, normalise. The alternatives are one-versus-rest, training K binary classifiers and taking the largest score, and one-versus-one, training K(K−1)/2 pairwise classifiers and voting. Softmax is preferred when the classes are genuinely mutually exclusive, because it produces probabilities that sum to 1 by construction.
The worked example
Worked 2.3.1a — score all eight students and grade the model
forward passw₀ = −3, w₁ = 0.5, w₂ = 0.5. Compute each probability, the log loss, and the accuracy. State the decision boundary and the odds ratio per feature.Because both weights are 0.5, the score depends only on the total s = x₁ + x₂:
Each loss uses only the probability given to the true class: −ln p for a pass, −ln(1−p) for a failure.
| Student | s | z | p = σ(z) | true | predicted | loss |
|---|---|---|---|---|---|---|
| B | 3 | −1.50 | 0.1824 | fail | fail | 0.2014 |
| A | 4 | −1.00 | 0.2689 | fail | fail | 0.3133 |
| C | 5 | −0.50 | 0.3775 | fail | fail | 0.4741 |
| D | 5 | −0.50 | 0.3775 | fail | fail | 0.4741 |
| E | 7 | +0.50 | 0.6225 | pass | pass | 0.4741 |
| F | 7 | +0.50 | 0.6225 | pass | pass | 0.4741 |
| G | 8 | +1.00 | 0.7311 | pass | pass | 0.3133 |
| H | 9 | +1.50 | 0.8176 | pass | pass | 0.2014 |
| mean log loss | 0.3657 | |||||
The table is rearranged by s rather than alphabetically, which makes the symmetry visible: the four highlighted rows are the four students nearest the boundary, and each pays the same 0.4741. Loss is a function of distance from the boundary alone.
Equal steps in s do not give equal steps in probability: 6→8 gains 0.2311 while 8→10 gains only 0.1497. The sigmoid saturates, which is 1.8's vanishing-gradient story appearing here as diminishing returns on confidence.
Boundary
x₁ + x₂ = 6. Odds multiplier 1.6487 per unit of either feature.Perfect accuracy with a log loss well above zero: every student is on the right side, none is predicted confidently. That gap is exactly what a threshold cannot see and Unit 2B measures.
Worked 2.3.1b — two gradient descent steps by hand
centred features, η = 1θ = 0 using centred features d₁ = x₁ − 3 and d₂ = x₂ − 3, with η = 1. Show two full iterations.Unit 1.4 measured the spine's condition number at 70 and showed what that does to a single learning rate. The same problem applies here, and centring is the cheapest fix. It also makes the first gradient computable in one line.
Two things worth noticing. The intercept gradient is exactly zero because the classes are balanced — four and four — so a balanced dataset starts with no reason to shift the intercept. And J = ln 2 at the origin is not a coincidence: a model predicting 0.5 for everything has a log loss equal to the entropy of the label distribution in nats, which is 1.5's root entropy of 0.9710 bits converted to nats.
| i | d₁ | d₂ | z | p | y | p − y | (p−y)d₁ | (p−y)d₂ |
|---|---|---|---|---|---|---|---|---|
| A | −2 | 0 | −1.0000 | 0.2689 | 0 | +0.2689 | −0.5379 | 0.0000 |
| B | −1 | −2 | −1.2500 | 0.2227 | 0 | +0.2227 | −0.2227 | −0.4454 |
| C | −1 | 0 | −0.5000 | 0.3775 | 0 | +0.3775 | −0.3775 | 0.0000 |
| D | 0 | −1 | −0.3750 | 0.4073 | 0 | +0.4073 | 0.0000 | −0.4073 |
| E | 0 | +1 | +0.3750 | 0.5927 | 1 | −0.4073 | 0.0000 | −0.4073 |
| F | +1 | 0 | +0.5000 | 0.6225 | 1 | −0.3775 | −0.3775 | 0.0000 |
| G | 0 | +2 | +0.7500 | 0.6792 | 1 | −0.3208 | 0.0000 | −0.6416 |
| H | +3 | 0 | +1.5000 | 0.8176 | 1 | −0.1824 | −0.5473 | 0.0000 |
| Σ | 0 | 0 | 4 | −0.0116 | −2.0629 | −1.9017 |
Every residual pᵢ − yᵢ is still large, between 0.18 and 0.41, because the model is not yet confident about anyone. Under log loss the size of a residual is exactly how hard that student pushes on the weights, which is why the marginal students C, D, E and F contribute the most.
| step | w₁ | w₂ | ‖w‖ | J | boundary slope |
|---|---|---|---|---|---|
| 1 | 0.5000 | 0.3750 | 0.6250 | 0.3935 | −1.3333 |
| 2 | 0.7579 | 0.6127 | 0.9746 | 0.2897 | −1.2369 |
| 10 | 1.5666 | 1.4586 | 2.1405 | 0.1141 | −1.0741 |
| 200 | 3.9729 | 3.9602 | 5.6095 | 0.0095 | −1.0032 |
| 2000 | 6.2215 | 6.2202 | 8.7976 | 0.0010 | −1.0002 |
The norm keeps growing and the loss keeps falling, with no finite optimum, exactly as the depth box predicted.
Converting the 2000-step weights back to original coordinates gives the boundary
x₁ + x₂ = 5.9993 — the same line as the illustrative model above, and the same line the SVM will find in 2.3.3.The visualization
The sigmoid, the threshold, and who gets called a pass
interactive — move the thresholdTwo controls, two different lessons. The threshold slides the cut-off without touching the model, which moves accuracy and leaves log loss untouched — the threshold is a deployment decision, not a training one, and Unit 2B is about choosing it. The weight scale multiplies both weights, which leaves accuracy at 1.000 and drives log loss toward zero without moving the boundary at all. That is the divergence of the depth box, made visible.
The pitfalls
Where marks are lost
- Calling it "regression" and expecting a number. Logistic regression is a classifier. The word is historical: it regresses the log-odds.
- Reading the coefficient as a change in probability.
w₁ = 0.5adds 0.5 to the log-odds, not 0.5 top. The effect on probability depends on where you start, which is what the saturating sigmoid means. - Using MSE instead of log loss. It is not convex in the parameters of a logistic model, and a confidently wrong prediction produces a nearly zero gradient because the sigmoid has saturated — the model stops correcting exactly when it is most wrong.
- Leaving it unregularized on separable or near-separable data. Coefficients run to hundreds, standard errors become meaningless, and the model is absurdly overconfident on new data. Suspiciously huge coefficients are the diagnostic.
- Forgetting to scale features before regularizing. The penalty treats all coefficients alike, so unit choices decide which feature is shrunk. Same trap as 2.2.
- Assuming the 0.5 threshold is meaningful. It is the default, nothing more. Under class imbalance or unequal error costs it is usually the wrong choice, and 2B's threshold sweep is how you find a better one.
- Interpreting coefficients as causal. A positive coefficient on "attended lab" does not mean attending the lab would raise anyone's chances. It means lab attendance predicts passing among these students, which is a statement about the data, not about an intervention.
Practice
P2.3.1.1 (direct) — A model has z = −4 + 0.8x₁ + 1.2x₂. Compute p for a student with x₁ = 3, x₂ = 2, state the decision at threshold 0.5, and give the odds ratio for one extra assignment.
So one more assignment multiplies this student's odds of passing by 3.32, taking the odds from 2.23 to 7.39, which is p = 0.8808. The probability rose by 0.19 while the odds more than tripled — a reminder that odds ratios sound larger than the probability change they describe.
P2.3.1.2 (variation) — Take the illustrative spine model z = 0.5s − 3 and multiply both weights and the intercept by 4, giving z = 2s − 12. Recompute the accuracy and the log loss, and explain what changed.
The boundary is identical: 2s − 12 = 0 is still s = 6. Scaling the whole weight vector cannot move the boundary, because the boundary is where z = 0 and zero times anything is zero. What changed is confidence, and log loss fell by a factor of five to reward it.
This is why log loss can be driven arbitrarily low on separable data, and why accuracy cannot distinguish the two models at all. It is also the mechanism by which an unregularized fit becomes overconfident: the training loss genuinely improves, so gradient descent genuinely pursues it, and nothing in the objective knows that 0.9975 is an unjustifiable claim about a student it has seen once.
P2.3.1.3 (interpretation) — A hospital's logistic model for a rare complication reports an odds ratio of 3.5 for a risk factor, with 96% accuracy. A manager concludes the factor "makes complications three and a half times more likely". Assess both numbers.
The odds ratio. Three and a half times the odds, not the probability, and only approximately equal when the outcome is rare. If baseline risk is 1%, odds are 0.0101, tripled-and-a-half gives 0.0354, so the risk becomes 3.4% — close enough that the manager's sentence is nearly right. At a baseline of 40%, odds 0.667 become 2.333 and risk becomes 70%, which is 1.75× the probability, not 3.5×. The rarer the outcome, the better the approximation. Also, the ratio is conditional on the model's other covariates being held fixed, and it says nothing about what would happen if the factor were removed.
The accuracy. For a rare complication, 96% accuracy is compatible with a model that never predicts a complication at all — if the base rate is 4%, predicting "no complication" always scores exactly 96%. The number is uninformative without recall, precision and the confusion matrix, which is precisely the material of Unit 2B. Ask for those before asking anything else.
P2.3.1.4 (synthesis) — Using 1.2 and 1.4, explain why logistic regression is trained iteratively while linear regression has a closed form, and why that difference does not threaten reproducibility.
Setting the gradient to zero gives, for linear regression, Aᵀ(Aθ − y) = 0 — linear in θ, so it rearranges into the normal equations and is solved by linear algebra. For logistic regression it gives Aᵀ(σ(Aθ) − y) = 0, where θ appears inside a sigmoid. That is transcendental; no rearrangement isolates θ, so the root must be found numerically.
Reproducibility survives because of 1.2. The log-loss objective is convex in θ, so every local minimum is global and any correctly implemented descent from any starting point reaches the same answer. Two runs that disagree indicate a bug, a learning rate above the stability limit, or insufficient iterations — not a legitimate second optimum. That guarantee is what disappears in Unit 4, where the surface is non-convex and two runs disagreeing is normal.
The one caveat is this section's depth box: on separable data the optimum is at infinity, so "reaches the same answer" holds for the direction of w but not its length, and where you stop is set by your iteration budget rather than by the mathematics. Regularizing removes the caveat.
k-Nearest Neighbours
The model that does not fit anything. Training is free, prediction is expensive, and the entire behaviour is decided by how you chose to measure similarity.
The question
Every method so far fits parameters and then throws the data away. But if you want to know whether a new student will pass, there is an obvious alternative that requires no fitting at all: find the students most like them and see what happened.
The question is what "most like them" means, and how many of them to ask.
The intuition
You are new to a town and want to know whether a particular street floods. You could build a hydrological model, or you could ask the three nearest households.
Asking one neighbour is fast and captures local detail, but that neighbour might be wrong or unusual. Asking thirty averages out the eccentrics, but thirty households reach across several streets and you have stopped asking about your street. There is a right number and it depends on how variable the neighbours are and how quickly conditions change with distance.
The other half of the intuition is that "nearest" needed a definition and you just supplied one implicitly. Nearest in metres? In travel time? In elevation? Change the answer and different households become your neighbours. In k-NN this choice is not a detail; it is the model.
The formal treatment
This makes k-NN non-parametric — the number of things it remembers grows with the data rather than being fixed in advance — and lazy, meaning no work happens until a query arrives. It has no training loss, no gradient and no parameters. Only hyperparameters: k, the distance function, and the weighting scheme.
Distances
Comparing squared Euclidean distances gives the same ranking as comparing distances, so skip the square root when you only need an ordering. That is what every worked example below does.
Two variants worth knowing
Distance-weighted voting gives each neighbour weight 1/d or 1/d² instead of one vote each, so a neighbour at the edge of the neighbourhood counts for less. This also breaks most ties automatically, which matters because ties are common on small or integer-valued data. Radius neighbours replaces "the nearest k" with "everything within distance r", which is the better choice when density varies a lot — a fixed k in a sparse region reaches absurdly far.
Depth — k as the bias–variance dial, and the curse of dimensionality
Small k means low bias and high variance: at k = 1 the training error is exactly zero, because every training point is its own nearest neighbour, and the boundary is a jagged shape that changes completely if one point moves. Large k means high bias and low variance: at k = n the model predicts the majority class everywhere and ignores the features entirely. The effective number of parameters is roughly n/k, so k is 1.6's complexity knob wearing a different label.
The deeper problem is dimensional. In d dimensions, capturing a fraction f of the data's range in every direction requires a cube of side f^(1/d). To capture 1% of the volume needs side 0.63 in 10 dimensions and side 0.95 in 100 — in other words, your "local" neighbourhood spans 95% of the range of every feature and is not local at all. Distances also concentrate: the ratio of the farthest to the nearest neighbour tends to 1 as d grows, so "nearest" stops being meaningful. k-NN is excellent in 2 to 10 dimensions and unusable in 1000 without dimensionality reduction first, which is Unit 3.3.
The worked example
Worked 2.3.2a — classify a new student for k = 1, 3, 5 and 7
integer distances throughoutk.Every value is distinct, so there are no ties to resolve and the ranking is unambiguous.
| rank | student | d² | d | label | running pass votes | prediction at this k |
|---|---|---|---|---|---|---|
| 1 | G (3,5) | 1 | 1.0000 | pass | 1 / 1 | pass, unanimous |
| 2 | E (3,4) | 2 | 1.4142 | pass | 2 / 2 | |
| 3 | C (2,3) | 4 | 2.0000 | fail | 2 / 3 | pass, 0.667 |
| 4 | A (1,3) | 5 | 2.2361 | fail | 2 / 4 | |
| 5 | F (4,3) | 8 | 2.8284 | pass | 3 / 5 | pass, 0.600 |
| 6 | D (3,2) | 10 | 3.1623 | fail | 3 / 6 | |
| 7 | B (2,1) | 16 | 4.0000 | fail | 3 / 7 | fail, 0.429 |
| 8 | H (6,3) | 20 | 4.4721 | pass | 4 / 8 |
The prediction flips between
k = 5 and k = 7, and the vote fraction falls monotonically the whole way. That falling fraction is the model losing locality: by seven neighbours out of eight, Q is being classified by almost the entire dataset, whose majority is a 4–4 tie broken only by which student is left out.For reference, the linear models of 2.3.1 and 2.3.3 put Q at x₁ + x₂ = 7, one unit inside the pass region, and predict pass with probability 0.6225. So k-NN at k = 1, 3, 5 agrees with them and only the near-degenerate k = 7 does not.
Worked 2.3.2b — the same query, after changing one unit
why you must scaleMultiply the first feature by 60. Q becomes (120, 5), and A becomes (60, 3), and so on. No information has been added or removed.
| k | hours — neighbours | prediction | minutes — neighbours | prediction |
|---|---|---|---|---|
| 1 | G | pass | C | fail |
| 3 | G, E, C | pass 2–1 | C, B, G | fail 2–1 |
| 5 | G, E, C, A, F | pass 3–2 | C, B, G, E, A | fail 3–2 |
Squaring is what does the damage. A difference of one hour became a difference of 60 minutes, and 60² is 3600, so the practice axis now contributes thousands while the assignment axis contributes single digits. Distance has become a measurement of practice alone, and the assignment count — the feature 2.2 found to be twice as predictive — has been silenced.
Standardise: divide each feature by its standard deviation, computed on the training set only. Here sd₁ = √(16/8) = √2 = 1.4142 and sd₂ = √(10/8) = √1.25 = 1.1180, so squared distance becomes 0.5(Δx₁)² + 0.8(Δx₂)².
Standardising makes the answer invariant to units. k-NN, SVMs with any distance-based kernel, and every regularized linear model share this requirement.
Fit the scaler on the training fold only. Fitting it on all the data before splitting is the leakage pitfall from 2.1.
The visualization
The decision regions k-NN actually produces
interactive — change k and the scalingThe shaded regions are the actual predictions, evaluated on a grid. Notice they are built from straight segments — k-NN boundaries are always piecewise linear, made of perpendicular bisectors between pairs of points — but the assembled shape is nothing like a single line. At k = 1 every training point owns a polygon around itself, which is a Voronoi diagram, and training accuracy is 1.000 by construction. Switch to minutes and watch the regions become vertical stripes: the second feature has stopped existing.
The pitfalls
Where marks are lost
- Not scaling the features. Worked 2.3.2b is the whole warning. This is the single most common k-NN error and it is silent — the model runs and returns confident nonsense.
- Reading
k = 1's perfect training accuracy as skill. Every point is its own nearest neighbour at distance zero. The number is a tautology. - Choosing even
kfor two classes. Invites 2–2 deadlocks. Use oddk, or distance weighting, and state your tie rule — the spine deadlocks often enough that 2.1's worked example had to. - Forgetting that prediction is where the cost lives. Training is free but every query costs
O(nd). With a million stored rows, that is a million distance computations per prediction. KD-trees and ball trees help in low dimensions; approximate methods are needed in high ones. - Using it in high dimensions untreated. See the depth box. Beyond roughly 20 features, reduce dimensionality first or use a different model.
- Ignoring class imbalance. With 95% negatives, most neighbourhoods are majority-negative regardless of location. Distance weighting, class-weighted votes or resampling are the responses.
- Applying it to categorical features encoded as integers. One-hot encoding a three-level category makes every pair of distinct levels equidistant, which is usually what you want; encoding them as 1, 2, 3 asserts that level 1 is closer to level 2 than to level 3, which is usually false.
Practice
P2.3.2.1 (direct) — Classify a new student R = (5, 2) at k = 1 and k = 3, using the spine in its original units.
k = 1: F and H tie at d² = 2 and both are passes, so the tie is harmless — pass.
k = 3: F (pass), H (pass), D (fail) → 2–1 → pass, vote fraction 0.667.
Sanity check against the linear models: R has x₁ + x₂ = 7, on the pass side of the boundary s = 6. All three methods agree.
P2.3.2.2 (variation) — Redo Worked 2.3.2a using Manhattan distance instead of Euclidean. Does anything change?
k = 1: G, pass — unchanged. k = 3: G plus the tied pair E and C, so three voters — pass, pass, fail → pass 2–1, unchanged. k = 5: G, E, C, A and then a three-way tie at distance 4, so all of F, D and B join, giving seven voters: G, E, F pass against C, A, D, B fail → fail 3–4.
So the flip that Euclidean distance produced at k = 7 arrives at k = 5 under Manhattan. The reason is structural: on integer coordinates, Manhattan distance takes far fewer distinct values than squared Euclidean, so ties are much more common and any fixed k silently pulls in more neighbours than requested. The metric is a hyperparameter with real consequences, and on lattice-like data it also changes how often you must invoke a tie rule.
P2.3.2.3 (interpretation) — A production k-NN model with k = 5 has 88% accuracy in testing but takes 400 ms per prediction against a 50 ms budget, on 2 million stored rows and 30 features. Give three options and their trade-offs.
Reduce n. Prototype selection or clustering the training set down to representatives cuts the distance count directly and proportionally. Risk: rare classes and boundary cases are exactly what gets discarded, so accuracy loss is concentrated where accuracy matters.
Index instead of scanning. A KD-tree or ball tree gives sublinear search, but their advantage decays as dimension rises and at 30 features the gain over brute force is modest. Approximate nearest neighbour methods — locality-sensitive hashing, HNSW graphs — do work at this dimension and routinely give 10–100× speedups for a small, measurable recall loss. This is usually the right first attempt because it preserves the model.
Replace the model. Train a logistic regression, gradient-boosted trees or a small network on the same data. Prediction becomes microseconds and memory drops from gigabytes to kilobytes. You lose k-NN's ability to represent an arbitrarily complicated boundary with no assumptions, and you have to accept whatever accuracy the substitute reaches — but at 30 features a boosted-tree model very often matches or beats k-NN anyway, so measure before assuming a sacrifice.
The one option not to take is lowering k. Prediction cost is dominated by computing n distances, not by selecting k of them, so k = 1 is barely faster than k = 5 and considerably worse.
P2.3.2.4 (synthesis) — Using 1.6 and 2.1, explain why k-NN's training accuracy at k = 1 carries no information, what leave-one-out cross-validation reports instead, and why 1-NN's LOOCV score on the spine is 0.8750 rather than 1.0000.
At k = 1 the nearest stored row to any training point is that point itself, at distance zero, so it votes for its own label and is always correct. Training accuracy is 1.0000 by construction for any dataset with no contradictory duplicates. In 1.6's vocabulary this is the degree-4 polynomial again: a model with enough capacity to interpolate reports zero training error as a structural fact, not as evidence.
Leave-one-out removes exactly that degeneracy. Holding out row i deletes it from the stored set, so the vote comes from a genuinely different student and the score measures generalisation. On the spine this gives 0.8750: seven of eight rows are classified correctly and student F is lost, because with F removed its two nearest remaining neighbours are D at d² = 2 and E at d² = 2 — one failure and one pass, a deadlock.
The bias–variance reading is that k = 1 sits at the extreme low-bias, high-variance end, and F is where that variance shows: F is one of the two passes hardest against the boundary, so its single nearest neighbour is as likely to come from the wrong class as the right one. Raising k trades that variance for bias, and 2.1 found that eight rows cannot resolve which trade is better — which is itself the honest finding.
Support Vector Machines
Of all the lines that separate the data, pick the one furthest from everything. A different objective, a surprising amount of geometry, and the only model here that can ignore most of its training set.
The question
The eight students are separable, and that means infinitely many lines classify all eight correctly. Logistic regression picked one by maximising likelihood, which as its depth box showed is a choice it makes only in the limit and only reluctantly. Is there a reason to prefer one separating line over another that does not appeal to probability at all?
There is, and it is purely geometric. A line that passes within a hair of student C is technically correct on the training data and obviously fragile: the next student who looks slightly like C will fall on the wrong side. A line with room on both sides will not.
The intuition
Imagine the two classes as two towns and you are asked to build a road between them. You could hug one town's edge, but the sensible road runs down the middle of the empty land, as far from both as possible.
Now widen the road until it touches a building on each side. The road's width is the margin, and the buildings it touches are the support vectors. Everything else in either town is irrelevant — you could demolish it and the road would not move. That is the striking property of an SVM: the solution depends on a handful of points near the boundary and not at all on the rest.
On the spine four students touch the road: C and D on the failing side, E and F on the passing side. The other four could be moved anywhere in their own territory and the answer would be unchanged.
The formal treatment
Labels are written ŷ ∈ {−1, +1} in this section rather than {0, 1}, because it lets a single expression say "correct and by how much".
The functional margin can be inflated by multiplying w and b by 10 without moving the boundary, so it cannot be maximised as it stands. The fix is a normalisation, and it is the step that makes the whole derivation work: require the closest points to have functional margin exactly 1. This is the canonical form, and it costs nothing because it only fixes a scale that was arbitrary.
The dual, and where the support vectors come from
Attaching a multiplier αᵢ ≥ 0 to each constraint and eliminating w and b gives an equivalent problem in the multipliers alone:
Two things fall out. The complementary slackness condition of the KKT theorem says αᵢ[ŷᵢ(wᵀxᵢ+b) − 1] = 0 for every i, so either a point sits exactly on the margin or its multiplier is zero. Points with αᵢ = 0 contribute nothing to w — that is the demolition property, derived. And the data enters the dual only through inner products xᵢᵀxⱼ, which is the opening for kernels.
Soft margin, and what C means
Real data is not separable. Introduce a slack ξᵢ ≥ 0 per point, allowing violations, and charge for them:
So C is a regularization strength in disguise, and it runs the opposite way from λ: roughly, C ≈ 1/λ. In the dual, soft margin changes only one thing — the constraint becomes 0 ≤ αᵢ ≤ C. Points with αᵢ = C are the ones inside or across the margin.
The kernel trick
Since the dual uses only inner products, replace xᵢᵀxⱼ with a kernel K(xᵢ, xⱼ) that equals the inner product of the two points after some feature map φ. You then get a linear separator in a high-dimensional space without ever computing coordinates there.
Depth — why the margin is 2/‖w‖, in three lines
Take a point x₊ on the positive margin, so wᵀx₊ + b = 1, and a point x₋ on the negative one, so wᵀx₋ + b = −1. Subtracting gives wᵀ(x₊ − x₋) = 2. The distance between the two parallel margin lines is the length of x₊ − x₋ measured along the unit normal w/‖w‖, which is wᵀ(x₊ − x₋)/‖w‖ = 2/‖w‖. That is the entire derivation.
A useful identity for checking work: at the optimum of the hard-margin problem, the dual objective equals ½‖w‖², and since the dual objective is Σαᵢ − ½‖w‖², this means Σᵢαᵢ = ‖w‖². On the spine, Σαᵢ = 2 and ‖w‖² = 2. If those two disagree, the multipliers are wrong.
The worked example
Worked 2.3.3 — the hard-margin SVM, verified from both sides
primal and dualThe two classes are separated along s = x₁ + x₂: the failures have s = 3, 4, 5, 5 and the passes have s = 7, 7, 8, 9. So the corridor runs from s = 5 to s = 7, its middle is s = 6, and the normal direction is (1, 1).
Now impose the canonical normalisation. The closest points must have functional margin exactly 1, so we need a(5) + b = −1 and a(7) + b = +1 for w = a(1,1):
| Student | x₁+x₂ | ŷᵢ | wᵀxᵢ + b | ŷᵢ(wᵀxᵢ+b) | distance to boundary | |
|---|---|---|---|---|---|---|
| B | 3 | −1 | −3 | +3 | 2.1213 | |
| A | 4 | −1 | −2 | +2 | 1.4142 | |
| C | 5 | −1 | −1 | +1 | 0.7071 | support vector |
| D | 5 | −1 | −1 | +1 | 0.7071 | support vector |
| E | 7 | +1 | +1 | +1 | 0.7071 | support vector |
| F | 7 | +1 | +1 | +1 | 0.7071 | support vector |
| G | 8 | +1 | +2 | +2 | 1.4142 | |
| H | 9 | +1 | +3 | +3 | 2.1213 |
All eight constraints hold, four with equality. Four points sit exactly on the margin and four have slack, so no constraint is violated and the candidate is feasible.
Only the four support vectors can have non-zero multipliers. By the symmetry of the four points around the boundary, try α = α₀ for each and solve w = Σαᵢŷᵢxᵢ:
Primal objective 1.0000, dual objective 1.0000. Zero duality gap on a convex problem certifies that this is the global optimum, not merely a good candidate. This is the step that turns a plausible guess into a proof.
Using support vector C, with ŷↄ = −1 and xↄ = (2,3):
In practice b is averaged over all support vectors, since floating-point arithmetic makes them disagree slightly.
Support vectors C, D, E, F, each with
α = 0.5. Students A, B, G and H have α = 0 and could be deleted without changing the model.This is the identical boundary that unregularized logistic regression converged to in 2.3.1 — arrived at by geometry in five steps rather than by 2000 gradient steps.
One consequence worth stating plainly. Delete student H, the strongest performer in the class, and the model does not move. Move student C by a tenth of an hour and it does. An SVM is decided entirely by its most marginal cases, which is a strength when those cases are informative and a serious liability when they are mislabelled — a single wrong label near the boundary rewrites a hard-margin solution completely, and that is the main argument for using a soft margin even on data that looks separable.
The visualization
Why the widest road wins
interactive — tilt and shift the boundaryEvery angle and offset that keeps "separates all 8" at yes is a valid answer for logistic regression, a perceptron, or any method that only asks for correctness. Only one is valid for an SVM. Tilt away from 45° and watch the corridor pinch shut against C or against E — the margin falls and ½‖w‖² rises, which is the objective being minimised.
The pitfalls
Where marks are lost
- Forgetting to scale features. The margin is measured in the feature space's own units, so an unscaled feature dominates
‖w‖and dominates the solution. As critical here as in k-NN, and the RBF kernel'sγmakes it worse. - Believing large
Cmeans more regularization. It is the reverse. LargeCpunishes violations, giving a narrow margin and an overfit-prone model. SmallCis the regularized end. - Using
{0, 1}labels in the margin formulas. The expressionŷᵢ(wᵀxᵢ+b) ≥ 1requires±1; with 0 and 1 every negative constraint collapses to0 ≥ 1. - Reading an SVM's output as a probability.
wᵀx + bis a signed distance, not a probability. Getting probabilities requires a separate calibration step, and it is fitted on held-out data rather than read off the model. - Defaulting to an RBF kernel on wide data. With many features and few rows, a linear SVM usually wins and trains in a fraction of the time. RBF earns its place when the boundary is genuinely curved and
nis comfortably larger thand. - Ignoring the cost at scale. Training is roughly
O(n²)toO(n³)for kernel SVMs, which becomes impractical somewhere around a hundred thousand rows. Linear SVMs trained by stochastic methods scale far better. - Using a hard margin because the training data happens to be separable. Separability at
n = 8is close to guaranteed and says nothing. A hard margin gives one mislabelled boundary point total control of the model.
Practice
P2.3.3.1 (direct) — Find the maximum-margin separator for three points: (3,1) and (3,−1) labelled +1, and (1,0) labelled −1. Give w, b, the margin, and the multipliers.
By symmetry about the horizontal axis the boundary must be vertical, so w = (a, 0). The canonical conditions are 3a + b = +1 and a + b = −1. Subtracting gives 2a = 2, so a = 1 and b = −2.
P2.3.3.2 (variation) — A ninth student I is added at (3, 3), so s = 6, exactly on the old boundary, and labelled fail. What happens to the hard-margin solution?
The data is still separable — the failures now reach s = 6 and the passes start at s = 7 — but the corridor has narrowed from the interval (5, 7) to (6, 7). The boundary moves to the new middle, s = 6.5, and the normalisation must be redone:
One added point halved the margin and quadrupled the objective. The support vectors are now I, E and F; C and D have slack and drop out entirely. That is the fragility argument for soft margins made concrete: with C finite, the optimiser could pay a small hinge penalty to leave I inside the margin and keep most of the original width, which is almost certainly the better model. A hard margin has no such option.
P2.3.3.3 (interpretation) — A trained SVM reports 4,800 support vectors out of 5,000 training rows. What does that tell you, and what would you change?
96% of the data sits on or inside the margin, which means the model has found almost no clean separation. Two diagnoses, distinguished by the training accuracy. If training accuracy is high, C is large and γ is probably large too: the model has wrapped a tight bubble around each training point, and it will generalise badly — effectively a memorising k-NN with extra steps. If training accuracy is also poor, the kernel simply does not suit the data.
It is also a practical problem regardless of accuracy, because prediction cost scales with the number of support vectors: every query needs 4,800 kernel evaluations.
What to change, in order. Verify the features are scaled, since unscaled features are the most common cause. Reduce γ, which widens each kernel's reach and lets one support vector cover more ground. Reduce C, accepting more margin violations for a wider margin. Then re-tune the two jointly on a log grid, since they interact strongly. And compare against a linear SVM, which on many real datasets performs as well and gives a compact model.
P2.3.3.4 (synthesis) — Logistic regression and the SVM both chose x₁ + x₂ = 6. Using 1.3 and 2.3.1's depth box, explain why, and identify a situation where the two would clearly disagree.
The connection is the shape of the two losses. Hinge loss, max(0, 1 − ŷf), is exactly zero for any point with functional margin at least 1, so once the margin is achieved those points stop influencing the solution entirely. Log loss, ln(1 + e^(−ŷf)), is never exactly zero — but it decays exponentially, so a point at large margin contributes almost nothing. The two losses are close everywhere except near the boundary, and both therefore end up caring about the same handful of marginal points.
2.3.1's depth box supplies the exact statement: on separable data, unregularized logistic regression drives ‖w‖ → ∞ in the direction that maximises the minimum margin, which is by definition the hard-margin SVM solution. Agreement here is a theorem, not a coincidence.
They diverge when the data is not separable, and specifically when there are points far on the wrong side. Log loss grows linearly in the margin violation, so a badly misclassified outlier keeps pulling the boundary forever. Hinge loss also grows linearly, so a soft-margin SVM is pulled too — but its multiplier is capped at C, which places a hard ceiling on how much any single point can influence w. That cap is why SVMs are the more robust of the two to a handful of extreme mislabels. They also diverge in what they can deliver: logistic regression returns calibrated probabilities, and an SVM returns a distance that must be calibrated separately — so if the output feeds a threshold that will be tuned later, as in Unit 2B, logistic regression is the more convenient starting point.
Decision Trees
Section 1.5's information gain, applied recursively. The only model in this unit whose reasoning a person can read off directly, and the one most eager to overfit.
The question
A loan is refused, a student is flagged, a scan is prioritised. Someone will ask why, and "the weighted sum of your standardised features fell below the threshold" is not an answer anyone can act on. Is there a model whose decision is a sentence?
There is, and it is built entirely from the entropy machinery of 1.5. Section 1.5 chose one question; this section keeps going.
The intuition
Twenty questions. You want to identify something and you may only ask yes/no questions, so you ask the one that splits the possibilities most evenly — because that is the question whose answer tells you the most.
A decision tree does the same thing with a training set. At the root it searches every feature and every threshold for the question that leaves the two resulting groups as pure as possible, asks it, and then repeats independently on each group. It stops when a group is pure, too small to split, or a depth limit is reached.
The consequence of asking about one feature at a time is that every cut is parallel to an axis. A tree cannot draw a diagonal line; it approximates one with a staircase, and the finer the staircase the more the tree is memorising rather than learning.
The formal treatment
The impurity measures are 1.5's, unchanged:
Candidate thresholds for a numeric feature are the midpoints between consecutive distinct values, so a feature with m distinct values gives m − 1 candidates and the search at each node costs O(nd) after sorting. Greedy means the root split is chosen without any consideration of what it does to later splits, and the tree that results is generally not the smallest or most accurate tree available — finding that one is NP-hard.
Controlling the size
| Control | What it does | Effect on bias and variance |
|---|---|---|
max_depth | Hard cap on question count along any path | The bluntest and most effective knob |
min_samples_leaf | Refuses leaves below a size | Stops single-row leaves, which are pure noise |
min_impurity_decrease | Refuses splits that gain too little | Prunes as it grows — can stop too early |
| Cost-complexity pruning | Grow fully, then remove subtrees that cost more than they earn, minimising error + α·(number of leaves) | The principled version; α is chosen by cross-validation |
Cost-complexity pruning is worth recognising as 1.7's regularization applied to structure rather than to coefficients: an objective plus α times a complexity penalty, with α tuned on validation. Growing first and pruning afterwards beats stopping early, because a split with low gain can enable a highly informative one below it, and an early-stopping rule never finds out.
Depth — the bias in "feature importance"
A tree reports importance as the total impurity decrease attributable to each feature, weighted by the rows it affected. It is cheap and it is systematically biased toward features with many distinct values, for exactly 1.5's reason: a feature with more candidate thresholds gets more chances to look good, and in the limit a unique identifier splits every node perfectly while predicting nothing. C4.5's gain ratio divides by the split information to correct this at selection time, but the importance report usually does not.
Two better options when the number matters. Permutation importance shuffles one feature in the validation set and measures how much performance drops — slower, model-agnostic, and it answers the question you actually asked. Correlated features still cause trouble for both: two near-duplicates split the credit arbitrarily, so a genuinely important feature can report near-zero importance because its twin absorbed it.
The worked example
Worked 2.3.4 — grow the tree, every candidate split shown
entropy and Gini in parallelFour failures and four passes, so the class balance is exactly even — the point of maximum impurity.
Take x₁ ≤ 2.5. Left branch A(1,3), B(2,1), C(2,3) — all three failed. Right branch D(3,2), E(3,4), F(4,3), G(3,5), H(6,3) — one failure, four passes.
| Question | Left (fail/pass) | Hₗ | Right (fail/pass) | Hₕ | weighted H | IG | Gini gain |
|---|---|---|---|---|---|---|---|
| x₁ ≤ 1.5 | 1 / 0 | 0.0000 | 3 / 4 | 0.9852 | 0.8621 | 0.1379 | 0.0714 |
| x₁ ≤ 2.5 | 3 / 0 | 0.0000 | 1 / 4 | 0.7219 | 0.4512 | 0.5488 | 0.3000 |
| x₁ ≤ 3.5 | 4 / 2 | 0.9183 | 0 / 2 | 0.0000 | 0.6887 | 0.3113 | 0.1667 |
| x₁ ≤ 5.0 | 4 / 3 | 0.9852 | 0 / 1 | 0.0000 | 0.8621 | 0.1379 | 0.0714 |
| x₂ ≤ 1.5 | 1 / 0 | 0.0000 | 3 / 4 | 0.9852 | 0.8621 | 0.1379 | 0.0714 |
| x₂ ≤ 2.5 | 2 / 0 | 0.0000 | 2 / 4 | 0.9183 | 0.6887 | 0.3113 | 0.1667 |
| x₂ ≤ 3.5 | 4 / 2 | 0.9183 | 0 / 2 | 0.0000 | 0.6887 | 0.3113 | 0.1667 |
| x₂ ≤ 4.5 | 4 / 3 | 0.9852 | 0 / 1 | 0.0000 | 0.8621 | 0.1379 | 0.0714 |
No candidate reaches 1.0000, so no single question separates the classes — unlike Unit 1's five students, where hours ≤ 2.5 did the whole job. This tree must grow. Entropy and Gini rank all eight identically, as they usually do.
The left child is pure and becomes a leaf. The right child holds D(3,2), E(3,4), F(4,3), G(3,5), H(6,3): one failure, four passes.
| Question | Left | Right | weighted H | IG | Gini gain |
|---|---|---|---|---|---|
| x₂ ≤ 2.5 | 1 / 0 | 0 / 4 | 0.0000 | 0.7219 | 0.3200 |
| x₂ ≤ 3.5 | 1 / 2 | 0 / 2 | 0.5510 | 0.1710 | 0.0533 |
| x₂ ≤ 4.5 | 1 / 3 | 0 / 1 | 0.6490 | 0.0729 | 0.0200 |
| x₁ ≤ 3.5 | 1 / 2 | 0 / 2 | 0.5510 | 0.1710 | 0.0533 |
| x₁ ≤ 5.0 | 1 / 3 | 0 / 1 | 0.6490 | 0.0729 | 0.0200 |
x₂ ≤ 2.5 wins outright with IG = 0.7219 — the entire remaining entropy — and both children are pure. The tree is finished.
A depth-1 stump would stop at the root and misclassify D, giving 7/8 = 0.8750.
Cross-validated, both depths score 0.8750 — the extra depth buys training accuracy and nothing else.
Compare the shape with 2.3.3. The SVM drew one diagonal line, x₁ + x₂ = 6, using four support vectors. The tree drew two axis-aligned cuts and needed a leaf containing exactly one student to get D right. That single-row leaf is the tree overfitting in miniature: it is a rule derived from one observation, and min_samples_leaf = 2 would have refused it and produced the 0.8750 stump instead.
The visualization
The staircase, growing one question at a time
interactive — slide the depthDecision regions
The tree itself
The pitfalls
Where marks are lost
- Dropping the branch-size weights from the gain. The most common arithmetic error in the topic. Averaging
HₗandHₕunweighted makesx₁ ≤ 1.5look competitive on the strength of a one-row pure branch. - Growing to purity and reporting training accuracy. An unlimited tree reaches 100% on any dataset without contradictory duplicates. It is a property of the algorithm, not a result.
- Expecting a tree to draw a diagonal. Every boundary is axis-parallel. A rule like "pass if
x₁ + x₂ ≥ 6" needs an unbounded staircase, and this is a structural limit no amount of data removes. Add the sum as a feature if you need it. - Trusting a single tree's structure. Trees are high-variance: change one row and the root split can change, taking the entire tree with it. If you are interpreting the structure, check its stability across resamples first.
- Reading impurity-based feature importance as truth. See the depth box — it favours high-cardinality features and splits credit arbitrarily among correlated ones.
- Scaling the features first. Harmless but pointless: splits depend only on the ordering of values, so any monotone transformation leaves the tree identical. This is one of the few models where scaling genuinely does not matter.
- Using a tree for extrapolation. Every prediction is a leaf's training mean, so a regression tree predicts a flat value beyond the range of its training data forever. Linear models extrapolate, sometimes badly; trees refuse to.
Practice
P2.3.4.1 (direct) — A node holds 10 rows: 6 pass, 4 fail. A candidate split gives branches of (4 pass, 0 fail) and (2 pass, 4 fail). Compute the entropy of the node, the information gain, and the Gini gain.
P2.3.4.2 (variation) — Rebuild the spine's tree with min_samples_leaf = 2. What tree do you get, and what is its training accuracy?
The root split x₁ ≤ 2.5 gives branches of 3 and 5 rows, both at least 2, so it is still allowed and still the best. The left branch is pure and becomes a leaf.
On the right branch, the winning split x₂ ≤ 2.5 would produce branches of 1 and 4 rows. The one-row branch violates the constraint, so that split is forbidden. The alternatives, x₂ ≤ 3.5 and x₁ ≤ 3.5, both give branches of 3 and 2 rows with IG = 0.1710. Take x₂ ≤ 3.5: left is D, F, plus... on this branch the rows are D(3,2), E(3,4), F(4,3), G(3,5), H(6,3), so x₂ ≤ 3.5 gives left {D, F, H} = 1 fail 2 pass, predicting pass, and right {E, G} = pure pass.
So the constraint has bought nothing and cost a split. Two lessons. A minimum-leaf-size rule can force the tree into a split that is worse than not splitting at all, which is why cost-complexity pruning — grow first, then remove — is the better discipline. And on eight rows, every regularization setting collapses to roughly the same 0.8750 model, which is 2.1's finding restated: the dataset is too small to support a choice.
P2.3.4.3 (interpretation) — A depth-12 tree has training accuracy 0.998 and validation accuracy 0.71. Its top three reported feature importances are customer_id, timestamp and transaction_amount. Diagnose everything wrong here.
The gap. 0.998 against 0.71 is severe overfitting, exactly the pattern of 1.6's diagnosis table. Depth 12 permits up to 4096 leaves, so most leaves hold a handful of rows and encode noise.
customer_id as the top feature. Two problems at once. It is a high-cardinality identifier, so the depth box's bias guarantees it will look important whether or not it is. And it is almost certainly leakage: if the same customer appears in training and validation, the tree has memorised individuals rather than learning behaviour, and 2.1's warning about grouped data applies — the split should have been by customer, not by row. Drop the column.
timestamp as second. Same cardinality bias, plus a likely temporal leak. If the model will be used on future data, a raw timestamp lets the tree partition by "which batch this came from", which is unavailable and meaningless at prediction time. Replace it with derived features that will exist in future — hour of day, day of week, days since signup — and validate on a time-ordered split.
What to do. Remove the identifier and the raw timestamp, re-split by customer group and by time, cap the depth or tune cost-complexity α by cross-validation, set a minimum leaf size, and recompute importance by permutation rather than by impurity. Expect the training accuracy to fall a long way. That fall is the model becoming honest, not becoming worse.
P2.3.4.4 (synthesis) — The tree needed a single-row leaf to classify D correctly, and the SVM classified D correctly with slack to spare. Using 1.5, 1.6 and 2.3.3, explain the difference and say which model you would ship for this problem.
The difference is the hypothesis space. The SVM's boundary is x₁ + x₂ = 6, and D sits at s = 5, a full 0.7071 units clear on the correct side — D is a support vector, so it defines the boundary rather than straining against it. The tree cannot express that diagonal at all. Its axis-aligned cuts place D in the same x₁ > 2.5 region as four passes, so the only way to get D right is a second cut isolating it alone.
In 1.6's terms, the tree pays for its restricted hypothesis space with bias, and then buys the last training point back with variance — a leaf built on one observation. The SVM pays no such price because the diagonal was in its space from the start. In 1.5's terms, information gain is a greedy one-feature-at-a-time criterion and is structurally blind to a rule that depends on a sum of features, which is why the root's best gain was only 0.5488 rather than 1.
Ship the SVM, or logistic regression. The generating structure here is a threshold on total effort, and both linear models represent it exactly with a wide margin, while the tree needs a memorised special case. If interpretability were mandatory, the honest tree is the depth-1 stump — "practise more than 2.5 hours" — at 0.8750, and you would say plainly that it misses one student. What you would not do is ship the depth-2 tree and cite its 100% training accuracy, which is the specific failure this whole unit exists to prevent.
Ensembles: Bagging, Random Forests and Boosting
Many weak models beat one strong one, for a reason that comes straight out of 1.6. Two different strategies, attacking two different terms of the decomposition.
The question
Section 2.3.4 built a tree that reached 100% training accuracy and 87.5% under cross-validation, and warned that changing one row could change the root split and with it the entire tree. That instability is variance, and 1.6 supplied a way to reduce variance without touching bias: average.
So instead of trying to build one good tree, build many mediocre ones and combine them. The question is how to make them different enough for averaging to help, and whether there is a second strategy that reduces bias instead.
The intuition
Two ways to use a committee, and they are genuinely different in kind.
Ask everyone at once. Give each member a different random sample of the evidence, let them form independent opinions, and take a vote. Individual mistakes are uncorrelated, so they partly cancel; individual insight is shared, so it survives. That is bagging, and it works on high-variance members — deep trees, which are confident and unstable.
Ask them in turn. The first member gives an opinion, you note what they got wrong, and you tell the second member to concentrate on precisely those cases. The third focuses on what the first two still get wrong, and so on. Each member is weak — a single yes/no question — but the sequence corrects itself. That is boosting, and it works on high-bias members.
The distinction is worth holding onto: bagging is parallel and reduces variance, boosting is sequential and reduces bias. They are not two flavours of the same trick.
The formal treatment
Why averaging works, and where it stops
| ρ | B = 1 | B = 5 | B = 25 | B = 100 | limit |
|---|---|---|---|---|---|
| 0.0 | 1.0000 | 0.2000 | 0.0400 | 0.0100 | 0.0000 |
| 0.3 | 1.0000 | 0.4400 | 0.3280 | 0.3070 | 0.3000 |
| 0.6 | 1.0000 | 0.6800 | 0.6160 | 0.6040 | 0.6000 |
Read the ρ = 0.6 row and the whole design of random forests follows. Going from 25 trees to 100 buys almost nothing once the trees are correlated; the only way to keep improving is to decorrelate them. Everything a random forest does beyond bagging exists to push ρ down.
Bagging and out-of-bag estimation
| n | 8 | 10 | 100 | 1000 | limit |
|---|---|---|---|---|---|
| (1 − 1/n)ⁿ | 0.3436 | 0.3487 | 0.3660 | 0.3677 | 0.3679 |
So roughly a third of the data is left out of each bootstrap sample. Those rows are a free validation set for that particular model, and averaging over all B models gives the out-of-bag error — an estimate close to cross-validation's, obtained at no extra cost. It is the reason random forests are unusually convenient to tune.
Random forests
Bagging trees is not enough, because all the trees see nearly the same data and so choose nearly the same root split. Random forests add one change: at every node, consider only a random subset of m features rather than all d.
The trade is deliberate. Restricting the feature choice makes each individual tree worse — it is sometimes forced to split on a mediocre feature — but makes the trees far less alike, and the table above shows that lowering ρ is worth more than raising individual quality. This is the clearest example in the course of a component being intentionally degraded to improve the whole.
Boosting
Three facts about αₜ make it memorable. A learner with ε = 0.5 gets α = 0 and no vote, because a coin flip is worthless. A learner better than chance gets α > 0. A learner worse than chance gets α < 0, meaning its prediction is inverted and used — being reliably wrong is also information.
Gradient boosting generalises this. Instead of reweighting rows, fit each new model to the negative gradient of the loss with respect to the current predictions — which for squared error is simply the residuals. That reframing lets boosting use any differentiable loss, and it is what XGBoost, LightGBM and CatBoost implement. A learning rate ν, typically 0.01 to 0.1, shrinks each step, and more trees are then needed; the product of the two is the real capacity knob.
Stacking is the third combination scheme: train several different model families, then train a small model — usually a regularized linear one — on their cross-validated predictions. It can exploit complementary strengths that voting cannot, at the cost of a much more careful validation setup, since the meta-model must never see a base model's prediction on data that base model was trained on.
Depth — why boosting overfits, and why it took so long to admit it
Bagging cannot overfit by adding models: more trees only reduce variance, so B is a compute budget rather than a hyperparameter to tune. Boosting is different. Each round deliberately increases the ensemble's capacity to fit the training data, so training error falls monotonically toward zero and T is a genuine complexity parameter that must be validated. Gradient boosting with too many rounds overfits reliably, which is why every implementation ships with early stopping on a validation set.
AdaBoost was for years thought to resist overfitting, because its test error often kept falling after training error hit zero. The resolution is that once every point is classified correctly, further rounds keep increasing the margins — the same quantity 2.3.3 maximises — and larger margins keep improving generalisation even though accuracy cannot. It is a real effect with a real explanation, and it is not a licence to skip validating T.
The worked example
Worked 2.3.5a — one round of AdaBoost on the spine
weights by handx₁ ≤ 2.5. Compute ε₁, α₁, and every updated weight.All eight rows start at wᵢ = 1/8 = 0.125. The stump predicts fail for x₁ ≤ 2.5 and pass otherwise, so it gets A, B, C, E, F, G, H right and D wrong — D practised 3 hours but failed.
A stump that gets 7 of 8 right earns a vote weight near 1. For comparison, ε = 0.4 would earn only 0.2027, and ε = 0.5 exactly 0.
Correct rows are multiplied by e^(−α) = e^(−0.9730) = 0.3780; the wrong row by e^(+α) = 2.6458.
| Student | A | B | C | D | E | F | G | H | Σ |
|---|---|---|---|---|---|---|---|---|---|
| before | 0.125 | 0.125 | 0.125 | 0.125 | 0.125 | 0.125 | 0.125 | 0.125 | 1.000 |
| after | 0.0714 | 0.0714 | 0.0714 | 0.5000 | 0.0714 | 0.0714 | 0.0714 | 0.0714 | 1.000 |
Student D's weight jumps from 0.125 to 0.5000 — it now carries half of all the attention in the dataset.
Round 2's stump is fitted to those weights, so it will do almost anything to get D right, and it will be allowed to sacrifice some of the others to do it.
The 0.5000 is not a coincidence of these numbers. The choice of αₜ is exactly the value that makes the misclassified rows carry total weight ½ after renormalising, for any εₜ. That is the invariant to remember and the fastest way to check your arithmetic: after every AdaBoost round, the rows the last learner got wrong hold half the mass, and the next learner therefore faces a problem on which its predecessor scores exactly 50% — the worst possible score. Each round hands the next one a maximally difficult, and therefore maximally informative, reweighting.
Worked 2.3.5b — how many voters do you need?
majority vote arithmeticB members is correct?| B | 1 | 3 | 5 | 11 | 25 |
|---|---|---|---|---|---|
| P(ensemble correct) | 0.6000 | 0.6480 | 0.6826 | 0.7535 | 0.8462 |
This is the Condorcet jury theorem, and it is the whole promise of ensembling.
It requires two conditions: each voter must be better than chance, and the voters must be independent. Neither holds exactly for real models, which is what the
ρ table above is measuring.Set each voter's accuracy to 0.4 instead and the arithmetic runs the other way: the ensemble converges to 0% as B grows. An ensemble of models that are worse than chance is reliably worse than any one of them, which is why "check each base learner beats the baseline" is the first step and not a formality.
The visualization
What averaging buys, and what correlation takes back
interactive — ensemble size and correlationVariance remaining after averaging
AdaBoost weights, round by round
The left curve is the formula ρ + (1−ρ)/B. Push ρ to 0 and averaging is unboundedly effective; push it to 0.9 and 60 trees achieve almost nothing that one tree did not. Feature subsampling is the lever that moves ρ, and that is why it is the defining ingredient of a random forest rather than an optional extra.
The pitfalls
Where marks are lost
- Confusing bagging with boosting. Parallel and variance-reducing versus sequential and bias-reducing. They want opposite base learners: bagging wants deep unstable trees, boosting wants shallow stumps.
- Bagging a low-variance model. Averaging 500 linear regressions gives approximately one linear regression. There is no variance to remove, so there is nothing to gain.
- Tuning
Bfor a random forest as if it could overfit. It cannot; more trees only help, with diminishing returns. TuningTfor boosting is mandatory, because it can and does. - Forgetting the
1/Zₜin AdaBoost. Without renormalising, the weights stop summing to 1 andεₜis no longer a rate. The check is the invariant: misclassified mass should be exactly 0.5 after every round. - Reading out-of-bag error as a test score. It is a validation estimate. If you used it to choose
mor the depth, it is optimistic for the same reason 2.1 gives. - Expecting an ensemble to fix bad features or leakage. Averaging 500 models that all exploit the same leaked column gives a very confident wrong answer.
- Claiming a forest is interpretable because a tree is. Five hundred trees voting is not a rule anyone can read. Use permutation importance, partial dependence or SHAP — and note that Unit 2B treats this as an obligation rather than a nicety.
Practice
P2.3.5.1 (direct) — An AdaBoost round has weighted error εₜ = 0.25. Compute αₜ, the multipliers applied to correct and incorrect rows, and verify the misclassified mass afterwards.
Note that e^(−α) = √(ε/(1−ε)) in general, so the correct mass becomes √(ε(1−ε)) and so does the incorrect mass — identical by construction, which is precisely why the split is always 50/50. Also Zₜ = 2√(ε(1−ε)) = 0.8660 here, and since Zₜ < 1 whenever ε ≠ 0.5, the product of the Z values bounds the training error and drives it to zero exponentially. That bound is AdaBoost's convergence proof in one line.
P2.3.5.2 (variation) — Suppose round 2 of Worked 2.3.5a finds a stump that gets D right but misclassifies A and B. Compute ε₂ and α₂, and say what the two-learner ensemble predicts for D.
For student D: learner 1 said pass (h₁ = +1, wrong) with weight 0.9730; learner 2 said fail (h₂ = −1, right) with weight 0.8959. The ensemble score is 0.9730(+1) + 0.8959(−1) = +0.0771, so H(D) = sign(+0.0771) = +1 = pass — still wrong, but only just.
That near-tie is instructive. One round of correction was not quite enough to overturn a confident first learner, and D's weight will now rise again for round 3, at which point a third learner agreeing with the second will flip the verdict. Boosting converges by accumulation, not by any single round winning, and it is why T in the tens or hundreds is normal even on easy problems.
P2.3.5.3 (interpretation) — A random forest of 500 trees reports out-of-bag accuracy 0.91 while any single tree in it averages 0.78. A colleague proposes going to 5000 trees. Estimate the benefit.
Almost none. Use the variance formula. Going from 500 to 5000 changes (1−ρ)/B from (1−ρ)/500 to (1−ρ)/5000, and both are negligible beside ρ for any realistic tree correlation. At ρ = 0.3 the remaining variance moves from 0.30140 to 0.30014 — a change in the fourth decimal place, invisible against the sampling noise of the OOB estimate itself. The cost is ten times the training time and ten times the prediction latency.
What would actually help. Lower ρ by reducing m, the features considered per split, since that is the only term left that is not already exhausted — tune it on a small grid and watch OOB. Raise each tree's quality with better features. Or change model family: gradient boosting attacks bias, which a forest of already-averaged trees cannot reduce further, and on tabular data it frequently beats a random forest outright.
The general rule to take away: once the ensemble-size curve has flattened, more members is the one intervention guaranteed not to help.
P2.3.5.4 (synthesis) — Using 1.6 and 2.3.4, explain why bagging trees works so well, why bagging the spine's tree specifically would not, and which term of the decomposition boosting attacks.
A fully grown tree is close to unbiased and very high variance: it can represent almost any axis-aligned partition, so its average over training sets is near the truth, but any single tree depends heavily on which rows it saw. 1.6's decomposition says averaging leaves bias untouched and divides variance by up to B. A model with all its error in the variance term is therefore the ideal candidate, and a deep tree is the standard example of one.
On the spine it would fail, for two reasons. Bootstrap samples of eight rows contain on average only 5 distinct students, and 2.3.4 showed that the informative structure lives in a single row — student D. Any bootstrap sample missing D produces a tree with no reason to make the second split, and roughly a third of samples miss it. More fundamentally, the true boundary is diagonal, so every tree in the forest shares the same axis-aligned bias, and averaging cannot remove a bias that all members share. The forest would converge to a smoother staircase, not to the diagonal.
Boosting attacks bias. Each stump is a very high-bias model — one question, two leaves — and the sum of many weighted stumps is a far richer function than any of them. On the spine, an additive combination of stumps on x₁ and x₂ can approximate the diagonal to any accuracy, which is exactly the thing a single tree of bounded depth cannot do. The price, per this section's depth box, is that capacity now grows with T, so variance returns and T must be validated.
Bayesian Classifiers
Turn the question around: instead of learning a boundary, model what each class looks like and ask which class was more likely to have produced this student.
The question
Every classifier so far models P(class | features) directly — the boundary between the classes. There is an entirely different route. Model what the failing students look like, model what the passing students look like, and then for a new student ask which of the two descriptions fits better.
That route needs Bayes' theorem to get from "what a class looks like" back to "which class is this", and it needs one strong assumption to remain computable at all. Both are worth knowing exactly.
The intuition
You hear a four-legged animal in the next room. Is it a dog or a horse? You reason about two things at once: how likely each animal is to make that sound, and how likely each animal is to be in a house at all. A horse would make a heavier sound, but horses are rare indoors, so you conclude dog.
Those two ingredients are the likelihood and the prior, and multiplying them is Bayes' theorem. A classifier built this way is honest about the base rate in a way that a boundary-fitting model is not, which is exactly why it behaves sensibly when one class is rare.
The assumption that makes it cheap is that the clues are independent given the animal. Four legs, the sound, the smell — treat each as separate evidence and multiply. This is usually false; hearing a heavy tread and feeling the floor shake are not independent clues. Doing it anyway is what "naive" means, and the surprising thing is how often it does not matter.
The formal treatment
The problem is P(x | c). With d binary features there are 2ᵈ − 1 probabilities to estimate per class, so ten features already need over a thousand and you will never have enough data. The naive assumption collapses that count to d:
The zero-frequency problem and Laplace smoothing
If a feature value never occurs with a class in training, its estimated probability is 0, and one zero in a product of probabilities makes the whole posterior 0 — no matter how strongly every other feature votes. A single unseen combination can veto a class entirely.
Which variant for which data
| Variant | Feature type | What P(xⱼ | c) is |
|---|---|---|
| Categorical / Bernoulli | Discrete or binary | A smoothed frequency table, as above |
| Multinomial | Counts, classically word counts | Smoothed relative frequency of each token in the class |
| Gaussian | Continuous | A normal density with the class's own mean and variance for that feature |
Depth — why a wrong assumption gives right answers
Correlated features cause Naive Bayes to double-count evidence, so its posterior probabilities are pushed toward 0 and 1 and are badly calibrated — a reported 0.999 may be nothing of the sort. But the argmax is often unaffected, because both classes' scores are inflated in a broadly similar way and the ranking survives. A classifier judged on accuracy can therefore perform well while its probabilities are worthless.
This is why Naive Bayes remains a strong baseline for text, where features are thousands of sparse word indicators: the independence assumption is plainly false, yet the decision is usually right, training is a single pass over the data, and it works with very few examples per class. It is also why you should never feed a Naive Bayes probability into a downstream expected-cost calculation without calibrating it first — a distinction Unit 2B's calibration material makes precise.
One further connection worth noticing: Gaussian Naive Bayes with a shared variance across classes produces a linear boundary, the same shape logistic regression fits. The two are the generative and discriminative versions of the same model family, and the discriminative one usually wins with plenty of data while the generative one wins when data is scarce.
The worked example
Worked 2.3.6 — Naive Bayes, and why smoothing is not optional
exact fractionsPractice is high at 3 hours or more; assignments are high at 3 or more. Lab attendance is already binary.
| Student | practice | assignments | lab | class |
|---|---|---|---|---|
| A (1,3) | low | high | yes | fail |
| B (2,1) | low | low | no | fail |
| C (2,3) | low | high | yes | fail |
| D (3,2) | high | low | yes | fail |
| E (3,4) | high | high | yes | pass |
| F (4,3) | high | high | no | pass |
| G (3,5) | high | high | yes | pass |
| H (6,3) | high | high | no | pass |
Every passing student submitted at least 3 assignments, so "assignments low" never co-occurs with passing in these eight rows. That is the zero-frequency situation, and it arose without contrivance.
The model is certain, and the certainty is manufactured. Q practises heavily — an attribute that all four passing students share and only one failing student does — and that evidence has been discarded entirely by a single zero from a sample of four. No amount of contrary evidence could ever overturn it.
Every attribute is binary, so Vⱼ = 2 and each denominator becomes 4 + 1(2) = 6.
Smoothed: P(fail | Q) = 8/13 = 0.6154, still a fail prediction but an honest one.
The verdict is the same; the confidence is completely different, and only one of the two numbers could be shown to anyone.
Q is genuinely a borderline case. In the original continuous features Q resembles student D — high practice, low assignments — and D failed, which is why fail is the right call. But it is a call based on one comparable student, and 0.6154 says so while 1.0000 lies about it. Smoothing is the difference between a model that reports uncertainty and one that cannot.
Worth noting what discretising cost. Section 2.2 established that assignments are worth twice as much per unit as practice hours, and collapsing both to high/low throws that away — along with the fact that H practised six hours rather than three. Gaussian Naive Bayes on the raw features would keep it. Discretisation is convenient for hand computation and for genuinely categorical data, and it is a loss of information everywhere else.
The pitfalls
Where marks are lost
- Skipping smoothing. Worked 2.3.6 is the whole argument. Any unseen feature-value-and-class pair produces a zero that vetoes the class permanently.
- Getting the smoothed denominator wrong. It is
count(c) + αVⱼ, whereVⱼis the number of values that feature can take — not the number of features and not the number of classes. Adding 1 to the numerator and 1 to the denominator is the classic error and it makes the probabilities fail to sum to 1. - Multiplying many probabilities in floating point. A hundred features multiply to something below the smallest representable double. Sum the logarithms instead; every implementation does.
- Forgetting the prior.
argmax P(x|c)alone is maximum likelihood, not MAP, and it ignores the base rate. With one class ten times rarer, that difference is the whole answer. - Reporting the posterior as a calibrated probability. See the depth box. Correlated features make it far too extreme.
- Applying categorical Naive Bayes to continuous features by rounding them. Either discretise deliberately, with justified bin edges, or use the Gaussian variant. Rounding a measurement to the nearest integer creates arbitrary bins and pretends they are meaningful.
- Assuming "naive" means "bad". It refers to one specific assumption. On text classification with limited data, Naive Bayes routinely beats models with a hundred times its parameter count.
Practice
P2.3.6.1 (direct) — Using the smoothed tables from Worked 2.3.6, classify R = (low practice, high assignments, no lab).
Identical numbers to Q, by an accident worth understanding: swapping "practice high, assignments low" for "practice low, assignments high" exchanges two factors that happen to be reciprocal in both classes, and swapping lab yes for lab no does the same. The lesson is that after discretisation Q and R are indistinguishable to this model, whereas in the original features they sit on opposite sides of the boundary x₁ + x₂ = 6 only if their totals differ. Discretisation destroyed the distinction.
P2.3.6.2 (variation) — A spam filter has 800 ham and 200 spam messages. The word "invoice" appears in 40 ham and 60 spam; "urgent" in 20 ham and 80 spam. Classify a message containing both, with Laplace smoothing over the binary present/absent feature.
The prior favoured ham four to one and the likelihoods overturned it decisively, because each word is roughly six to fifteen times more common in spam. Note how much work the independence assumption is doing: "invoice" and "urgent" almost certainly co-occur in real spam, so multiplying their evidence double-counts it and 0.9577 is too confident. The decision is still right, which is the depth box's point exactly.
P2.3.6.3 (interpretation) — A medical Naive Bayes model outputs P(disease | symptoms) = 0.99 for a patient. The disease affects 1 in 10,000 people. What should you check?
Whether the prior is in the model at all. A prevalence of 0.0001 is an extremely strong prior against the disease. If the model was trained on a balanced dataset — a common and reasonable choice for learning the likelihoods — then its implicit prior is 0.5 and the reported posterior is wrong by four orders of magnitude in the prior term. Recomputing with the true prior can easily turn 0.99 into something under 0.1. The fix is to adjust the prior explicitly at prediction time rather than retraining.
Whether the symptoms are independent. In medicine they are usually strongly correlated — fever, fatigue and elevated white cell count travel together — so each is counted as fresh evidence when it is largely the same evidence. This pushes the posterior toward 1 and is the second reason 0.99 is not trustworthy.
What happens next. Even a correctly computed high posterior for a rare disease should trigger confirmatory testing, not treatment, and the honest output for a screening model is a risk score with a stated interval. The concrete request to make is for the confusion matrix at the deployed threshold, at the true prevalence, plus a calibration curve — which is exactly the toolkit Unit 2B builds.
P2.3.6.4 (synthesis) — Naive Bayes and logistic regression can both produce a linear boundary on the same features. Using this section's depth box and 2.3.1, state precisely how they differ and when you would choose each.
What they model. Naive Bayes is generative: it estimates P(x | c) and P(c) and derives the posterior by Bayes' theorem. Logistic regression is discriminative: it estimates P(c | x) directly and never models the features at all. Gaussian Naive Bayes with a shared covariance across classes gives a linear boundary of exactly the form logistic regression fits, so the hypothesis spaces coincide while the fitting criteria do not.
How they are fitted. Naive Bayes needs one counting pass and has a closed form; logistic regression needs iterative optimisation, per 2.3.1. Naive Bayes accepts the independence assumption as a constraint and therefore converges to its best answer with very little data. Logistic regression makes no such assumption, so with enough data it reaches a strictly better fit — and with too little, its greater flexibility costs it in variance.
When to choose which. Naive Bayes when data per class is scarce, when features are numerous and sparse, when training must be instant or incremental, or when you want to reason explicitly about a changing prior — adjusting for a new prevalence is a one-line change. Logistic regression when you have enough data to afford the flexibility, when features are correlated, and above all when you need the output probability to mean something, since log loss trains calibration directly while Naive Bayes' independence violation destroys it.
Choosing Between Them
Six algorithms, one dataset, and the honest comparison. Also the payoff figure this whole file has been building toward.
All six on the spine
Every model in this file was fitted to the same eight students. Here is what each concluded.
| Model | What it learned | Train acc. | Depends on |
|---|---|---|---|
| Linear regression, thresholded | x₁ + 2x₂ ≥ 9 | 1.000 | All eight rows, through the sums |
| Logistic regression | x₁ + x₂ ≥ 6, with probabilities | 1.000 | All rows, weighted by margin |
k-NN, k = 3 | Nothing — stores the data | 1.000 | The 3 nearest rows to each query |
| SVM, hard margin | x₁ + x₂ ≥ 6, margin √2 | 1.000 | C, D, E, F only |
| Decision tree, depth 2 | Two questions, three leaves | 1.000 | The split thresholds, so a few rows |
| Naive Bayes, smoothed | Six conditional probabilities | 1.000 | All rows, through the counts |
All six reach 100% training accuracy, which is precisely why the third column is the least useful in the table. On eight separable rows every one of these models can fit perfectly, so training accuracy discriminates nothing at all — it is the structure of what was learned, and how few rows it rests on, that tells you what will happen next.
The fourth column is where the models genuinely differ. The SVM depends on four students and would be unchanged if the other four were deleted. The tree depends on two thresholds, one of which was placed to accommodate a single row. k-NN depends on nothing until a query arrives and then on three rows. Naive Bayes and the regressions use all eight, but only through summary counts and sums — so no individual student can move them far. That column is a rough guide to how each model will react to one bad label, and it ranks them in an order that training accuracy cannot see.
The payoff figure
Four decision boundaries on the same eight students
the comparisonThree shapes, drawn from identical data. The straight diagonal is what logistic regression and the SVM agree on. The staircase is the tree, forced to cut parallel to the axes. The jagged outline is k-NN, assembled from perpendicular bisectors between pairs of students. All three are correct on all eight training rows and they disagree substantially about every point in between — which is to say, about every prediction that matters.
How to actually choose
Start here
Logistic regression or gradient-boosted trees, almost always.
The first gives a fast, calibrated, interpretable baseline. The second is the strongest general performer on tabular data. Anything else needs a reason.
Interpretability is mandatory
Shallow decision tree — a readable rule list.
Logistic regression — signed coefficients and odds ratios.
Avoid forests, boosting and kernel SVMs, which require post-hoc explanation tools.
Very little data
Naive Bayes or a regularized linear model.
Strong assumptions are an asset when there is not enough data to estimate anything freely. Deep trees and k-NN will simply memorise.
Many features, few rows
Linear SVM or lasso.
Both cope with d > n. Avoid k-NN entirely — the curse of dimensionality of 2.3.2 — and avoid RBF kernels.
Complicated boundary, plenty of data
Gradient boosting, random forest, or an RBF SVM.
All three represent curved boundaries. Boosting usually wins on tabular data; the SVM struggles past about 105 rows.
Probabilities must be trustworthy
Logistic regression, or anything followed by calibration.
SVM distances and Naive Bayes posteriors both need a calibration step. Unit 2B measures whether it worked.
Prediction must be fast
Linear model — one dot product.
Avoid k-NN, which scans the training set, and kernel SVMs with many support vectors.
One class is rare
Naive Bayes handles the prior explicitly. Anything else needs class weights, resampling, and a threshold chosen on the metric you care about — which is Unit 2B, section 2.4.
The one rule that survives every dataset
The no free lunch theorem says that averaged over all possible problems, every algorithm performs identically. It is a statement about all conceivable problems, not about the ones you will meet, so it does not mean model choice is arbitrary — real data has structure, and some models match it. What it does mean is that there is no algorithm that is best in general, and therefore no substitute for trying two or three and measuring properly.
Which is why 2.1 came first. The protocol matters more than the model, and a carefully validated logistic regression beats a carelessly validated anything.
Practice
P2.3.7.1 (interpretation) — For each scenario, name a model and give one sentence of justification. (a) Predicting loan default, where every refusal must be explained to the applicant in writing. (b) Classifying 50,000 support tickets into 12 categories from their text. (c) Predicting machine failure from 400 sensor readings on 300 recorded failures. (d) Flagging fraudulent transactions, 0.2% positive, with a 40 ms latency budget.
(a) Logistic regression, or a shallow tree. A written reason requires a model whose decision decomposes into named factors; a signed coefficient with an odds ratio, or a three-question rule, both do. There is usually also a legal requirement behind this, which makes it a constraint rather than a preference.
(b) Multinomial Naive Bayes as the baseline, then linear SVM or logistic regression on TF-IDF features. Text gives thousands of sparse features where the independence assumption is wrong but harmless, and 50,000 rows across 12 classes is enough for a discriminative linear model to overtake it. Train the cheap one first so you know what beating it looks like.
(c) Regularized linear model, lasso or a linear SVM. With 400 features and 300 events, d > n: k-NN's distances are meaningless, a deep tree will memorise, and an unregularized fit has no unique solution. Lasso additionally tells you which sensors matter, which is likely the real deliverable.
(d) Gradient-boosted trees, thresholded on the cost-weighted metric. Prediction is a few hundred fast comparisons, comfortably inside 40 ms, and boosting handles the mixed tabular features fraud data usually has. The model choice is the easy part: at 0.2% prevalence, accuracy is useless, the threshold must be set from expected cost rather than left at 0.5, and class weighting is required — all of which is Unit 2B.
P2.3.7.2 (synthesis) — Student C sits at (2,3) and failed; student E sits at (3,4) and passed. Trace what each of the four models in the payoff figure does with a hypothetical student at the midpoint (2.5, 3.5), and explain why they disagree even though all four classify the training data perfectly.
The midpoint has x₁ + x₂ = 6, exactly on the linear boundary. Logistic regression outputs p = 0.5000 — a formal refusal to decide, which is the correct answer for a point equidistant from both classes. The SVM gives wᵀx + b = 0, on the boundary, and its sign convention resolves to a class arbitrarily; the honest report is that it is undetermined.
The tree asks x₁ ≤ 2.5, which is true, so it answers fail immediately without ever consulting the assignment count. It is confident, and its confidence rests on a threshold placed midway between two students. k-NN at k = 3 finds C and E tied nearest at d² = 0.5, one failure and one pass, and then a four-way tie at d² = 2.5 among A, D, G and F. The third distance is 2.5, so every row tied at it votes: six voters, A, C and D failing against E, F and G passing, a 3–3 deadlock.
They disagree because fitting the training data perfectly constrains a model only at the training points. Everywhere else the prediction is determined by the model's inductive bias — the linear models interpolate along a straight line, the tree extends its axis-aligned cuts, k-NN follows the nearest rows — and the eight students say nothing about which bias is right. This is the deepest point in the file: the training data selects among models within a family, and the family itself is your assumption, not the data's conclusion. Choosing it is the modelling work, and validating that choice is 2.1.
Cheat Sheet
Everything in this file worth having in front of you the night before. Every number comes from the same eight students.
2.1 Selection
Parameter: fitted from data. Hyperparameter: chosen by you, tuned on validation.
TRAIN / VALIDATION / TEST — a split that influenced a decision cannot report an honest number.
k-fold: k fits, every row validated once. Stratify by default.
Grid fits = combos × folds. Random: P(hit top 5%) = 1 − 0.95ᵀ
Spine: 60 fits for 4×3×5; 60 random draws hit with p = 0.9539
2.2 Multiple regression
S₁₁w₁ + S₁₂w₂ = S₁ₖ
S₁₂w₁ + S₂₂w₂ = S₂ₖ
w₀ = ȳ − w₁x̄₁ − w₂x̄₂ · Sᵢⱼ = Σxᵢxⱼ − (Σxᵢ)(Σxⱼ)/n
adj R² = 1 − (1−R²)(n−1)/(n−d−1)
Coefficient = effect holding the others fixed.
Spine: 16,2,20 / 2,10,22 → marks = 3 + x₁ + 2x₂, MSE 0.5, R² 0.9412
2.2 Regularization
Ridge (S + λI)w = Sₖ — shrinks, never zeroes, fixes singularity.
Lasso — soft threshold, gives exact zeros, selects.
Intercept never penalised. Standardise first.
Training MSE always rises with λ: choose λ on validation.
Spine: λ=4 → (0.8551, 1.4493); lasso kills w₁ between λ=20 and 40
2.3.1 Logistic
p = σ(z), z = wᵀx + w₀, boundary z = 0
z = ln(p/(1−p)) — log-odds is linear
odds ratio per unit = e^(wⱼ)
∂J/∂wⱼ = (1/n)Σ(pᵢ − yᵢ)xᵢⱼ
Convex, no closed form. Separable data → no finite optimum, regularize.
Spine: z = 0.5s − 3, boundary s = 6, log loss 0.3657, e^0.5 = 1.6487
2.3.2 k-NN
No training. Predict = vote of the k nearest.
Compare d², skip the root. Odd k. State the tie rule.
Small k = high variance; k = n = majority class. Effective parameters ≈ n/k.
Must scale features. Fails past ~20 dimensions.
Spine, Q=(2,5): d² = 1,2,4,5,8,10,16,20 for G,E,C,A,F,D,B,H
k=1,3,5 pass; k=7 fail. In minutes, k=1,3,5 all flip to fail.
2.3.3 SVM
Canonical form: closest points have ŷ(wᵀx+b) = 1
minimise ½‖w‖² s.t. ŷᵢ(wᵀxᵢ+b) ≥ 1
margin width 2/‖w‖ · w = Σαᵢŷᵢxᵢ · Σαᵢŷᵢ = 0 · Σαᵢ = ‖w‖²
αᵢ > 0 only for support vectors. Soft margin: 0 ≤ αᵢ ≤ C. Large C = less regularization.
Spine: w=(1,1), b=−6, margin √2, SVs C,D,E,F with α=0.5
2.3.4 Trees
H = −Σpᵢlog₂pᵢ · Gini = 1 − Σpᵢ²
gain = imp(S) − Σ(|Sᵥ|/|S|)imp(Sᵥ) — never drop the weights
Thresholds = midpoints of consecutive distinct values.
Axis-aligned only. Grow then prune beats early stopping.
Scaling is irrelevant. No extrapolation.
Spine: root x₁≤2.5 IG 0.5488, then x₂≤2.5 IG 0.7219, 3 leaves
2.3.5 Ensembles
Var(avg of B) = ρσ² + (1−ρ)σ²/B — ρ is the floor
OOB share (1−1/n)ⁿ → 1/e = 0.3679
Bagging: parallel, cuts variance, wants deep trees.
Boosting: sequential, cuts bias, wants stumps, T must be validated.
Forest adds m ≈ √d features per split to lower ρ.
AdaBoost: αₜ = ½ln((1−ε)/ε); misclassified mass always → 0.5
Spine round 1: ε=0.125, α=0.9730, Z=0.6614, D → 0.5
2.3.6 Naive Bayes
argmax₀ P(c)∏ⱼP(xⱼ|c) — sum logs in practice
Laplace: (count + α)/(count(c) + αVⱼ), Vⱼ = values of feature j
One zero vetoes a class permanently. Always smooth.
Posteriors badly calibrated under correlation; argmax usually survives.
Spine, Q = (high, low, yes): unsmoothed P(fail)=1 (artefact);
smoothed 1/18 vs 5/144 → 8/13 = 0.6154 and 5/13 = 0.3846
The spine, end to end
Every value below is derived somewhere in this file from the nine sums. Reproduce this column and you have the file.
| Quantity | Value | Section |
|---|---|---|
| Centred scatter S₁₁, S₂₂, S₁₂ | 16, 10, 2 | 2.0 |
| Feature correlation | 0.1581 | 2.0 |
| 4-fold CV accuracy, k-NN, any k | 0.8750 ± 0.2165 | 2.1 |
| Grid fits, 4×3 with 5 folds | 60 | 2.1 |
| Regression plane | 3 + x₁ + 2x₂ | 2.2 |
| MSE / RMSE / R² / adj R² | 0.5000 / 0.7071 / 0.9412 / 0.9176 | 2.2 |
| R², one feature at a time | 0.3676 and 0.7118 | 2.2 |
| Ridge at λ = 4 | w = (0.8551, 1.4493) | 2.2 |
| Logistic boundary and log loss | x₁ + x₂ = 6, 0.3657 | 2.3.1 |
| First GD step, centred, η = 1 | (0, 0.5000, 0.3750), J = 0.3935 | 2.3.1 |
| k-NN squared distances from Q = (2,5) | 1, 2, 4, 5, 8, 10, 16, 20 | 2.3.2 |
| SVM weight vector and bias | (1, 1) and −6 | 2.3.3 |
| Margin width / ½‖w‖² / Σα | 1.4142 / 1.0000 / 2 | 2.3.3 |
| Root information gain | 0.5488 bits at x₁ ≤ 2.5 | 2.3.4 |
| Second split information gain | 0.7219 bits at x₂ ≤ 2.5 | 2.3.4 |
| AdaBoost round 1 | ε = 0.1250, α = 0.9730 | 2.3.5 |
| Naive Bayes posterior for Q | 8/13 = 0.6154 fail | 2.3.6 |
Mixed Self-Test
Ten questions in no order, with no section labels, because a question paper does not label them either. Attempt all before opening any solution.
Q1. Six rows give Σx₁ = 18, Σx₂ = 12, Σy = 60, Σx₁² = 66, Σx₂² = 32, Σx₁x₂ = 42, Σx₁y = 198, Σx₂y = 132. Fit y = w₀ + w₁x₁ + w₂x₂.
Check through the point of means: 5.2 + 1.2(3) + 0.6(2) = 10.0 = ȳ. Note the feature correlation is 6/√96 = 0.6124, appreciably higher than the spine's, so these two coefficients are substantially entangled — a warning to attach to any interpretation.
Q2. A logistic model gives z = −2 + 1.5x₁ − 0.5x₂. For x = (2, 1), give z, p, the odds, and the decision at threshold 0.6. Then state the odds ratio for a one-unit rise in x₂.
An odds ratio below 1 means the feature reduces the odds — each extra unit of x₂ multiplies the odds by 0.6065, a 39% reduction. At threshold 0.6 this prediction is marginal: one more unit of x₂ takes z to 0 and p to exactly 0.5, flipping the decision.
Q3. Six points: (1,1), (2,2), (2,1) labelled −1, and (4,4), (4,5), (5,4) labelled +1. Find the hard-margin SVM.
The closest opposing pair is (2,2) and (4,4), differing by (2,2), so the normal direction is (1,1) and the boundary is perpendicular to it. Using s = x₁ + x₂: the negatives have s = 2, 4, 3 and the positives s = 8, 9, 9. The corridor runs from 4 to 8.
Same boundary equation as the spine, x₁ + x₂ = 6, but a margin twice as wide because the classes are further apart — and correspondingly a smaller ‖w‖. The margin is a property of the data, not of the equation you write down.
Q4. A node has 12 rows, 8 pass and 4 fail. Split A yields (6 pass, 0 fail) and (2 pass, 4 fail). Split B yields (7 pass, 2 fail) and (1 pass, 2 fail). Which does a tree choose, by information gain?
Split A, by roughly four times. It buys a completely pure branch holding half the rows, and even though its other branch is left at maximum impurity, that is cheaper than B's two mediocre branches. The tree takes A and continues working on the impure side — which is how trees grow.
Q5. An AdaBoost round has weighted error 0.30. Give α, the two multipliers, Z, and the misclassified mass afterwards. Then say what happens if a later round produces weighted error 0.55.
At ε = 0.55, α = ½ln(0.45/0.55) = −0.1003, which is negative. The learner is worse than chance, so the ensemble uses its prediction inverted, and it still contributes usefully. In practice most implementations stop instead: a learner that cannot beat 0.5 on the current weighting usually signals that the weak learner family is exhausted, and continuing adds noise. Note also that Z = 2√(0.55 × 0.45) = 0.9950 < 1, so even this round tightens the training-error bound slightly.
Q6. A random forest of 200 trees has out-of-bag accuracy 0.88; individual trees average 0.74 and their pairwise correlation is estimated at 0.5. How much variance remains relative to one tree, and what is the single most promising change?
Averaging 200 trees has removed only half the variance of a single tree, and it is within 0.5% of everything it will ever remove. The B term is exhausted; adding trees is pointless.
The promising change is to reduce m, the number of features considered at each split, because ρ = 0.5 is high and m is the direct lever on it. Each tree will get slightly worse and the ensemble should get better — tune m on a small grid and watch OOB accuracy. Failing that, switch to gradient boosting, which reduces bias rather than variance and so attacks the part a forest cannot.
Q7. Two features are recorded in different units: x₁ ranges 0–5 and x₂ ranges 0–5000. For each of k-NN, a decision tree, ridge regression and an RBF-kernel SVM, say whether standardising the features changes the model, and why.
k-NN — changes it drastically. Squared Euclidean distance adds (Δx₁)² to (Δx₂)², and the second term is up to 10⁶ times larger, so x₁ is effectively deleted. Standardising is mandatory.
Decision tree — no change at all. Splits depend only on the ordering of a feature's values, and standardising is monotone, so every candidate threshold maps to an equivalent one and the tree is identical. This is one of very few models where scaling genuinely does not matter.
Ridge regression — changes it. The unpenalised fit is unaffected in its predictions, since a linear model absorbs a rescaling into the coefficient. But the penalty λ(w₁² + w₂²) charges both coefficients equally, and x₂'s natural coefficient is roughly a thousandth of x₁'s, so it is barely penalised. The choice of units silently decides which feature is shrunk. Standardise.
RBF SVM — changes it, and worst of all. The kernel exp(−γ‖x − x′‖²) uses the same distance k-NN does, so x₁ is ignored, and a single γ now has to suit two wildly different scales, which it cannot. Standardise before tuning γ.
Q8. Naive Bayes is trained on 60 rows: 20 class X and 40 class Y. A binary feature is present in 15 of class X and in 0 of class Y. Give the smoothed and unsmoothed P(feature present | Y), and explain the practical difference for a test row where all other features favour Y.
Unsmoothed, the score for class Y is multiplied by zero, so P(Y | x) = 0 exactly, whatever the other features say. A row with ten strong indicators of Y and this one feature present is classified as X with certainty. That is a veto produced by the absence of one combination in 40 samples.
Smoothed, class Y's score is multiplied by 0.0238 rather than 0 — a heavy penalty, roughly a factor of 42 against it, which is appropriate since the feature genuinely never appeared with Y. But it is now a penalty that other evidence can outweigh. If the remaining features favour Y by more than 42 to 1 in likelihood ratio, Y wins. The difference between smoothing and not is the difference between strong evidence and an unappealable ruling.
Q9. A model reports training accuracy 1.000 and 5-fold CV accuracy 0.62 on 2,000 rows. Name the condition and rank five interventions: (a) more features, (b) increase λ, (c) deeper trees, (d) collect more data, (e) reduce k in k-NN.
Severe overfitting — high variance. Perfect training accuracy with CV near chance-plus-a-bit means the model has memorised 2,000 rows.
(b) increase λ first — it directly reduces variance and costs one training run. (d) collect more data is strictly effective, since variance falls roughly as 1/n, but it is usually the slowest and most expensive option, so it ranks second on merit and last on practicality.
The remaining three all make it worse. (a) more features adds parameters and therefore variance. (c) deeper trees is directly more capacity. (e) reduce k moves k-NN toward its highest-variance setting, and k = 1 guarantees the 1.000 training accuracy this model already has — which is a hint that the model may already be 1-NN, in which case raising k is the whole fix.
The general shape: naming the condition before choosing the fix is the entire exercise, because underfitting reverses this ranking completely.
Q10. On the eight students, logistic regression, the SVM, k-NN at k = 3 and a depth-2 tree all achieve 100% training accuracy. Design the smallest experiment that would give real evidence about which to prefer, and say what you would conclude if it came back inconclusive.
The experiment. Collect new students — the only thing that adds information here. Roughly 40 to 60 would let a 4-fold stratified cross-validation resolve differences of about 10 percentage points, and the folds would then be large enough for the standard deviation to be interpretable. Score all four models on the same folds with the same preprocessing fitted inside each fold, report mean and spread, and hold out a final test set touched once. Standardise the features for k-NN and the SVM, and use identical folds across models so the comparison is paired.
What to prefer meanwhile. The structural evidence already available says the two linear models are the better bet: the label is defined by a threshold on marks, marks are close to linear in the two features, and a linear boundary therefore matches the generating process. The tree needed a one-row leaf to fit, and k-NN's answer depends on the scaling. So logistic regression is the defensible default, with the SVM as its equal.
If it comes back inconclusive — four means within one standard error of each other — then the honest conclusion is that the data does not distinguish them, and you choose on the other criteria of 2.3.7: prediction cost, interpretability, calibration, and how the model behaves when a label is wrong. That is not a failure of the experiment. Discovering that a difference does not exist is a result, and reporting a winner anyway is the specific dishonesty that 2.1 exists to prevent.
Where This Goes Next
This file built the models. The rest of the course judges them, removes the labels, and stacks them.
Read Unit 2B next
Every model in this file was reported with training accuracy, and every section warned you that the number is meaningless. Unit 2B is the repair: the confusion matrix, precision and recall, F-scores, ROC and AUC, how to choose a threshold from costs rather than convention, and how to report a cross-validated result honestly. It then covers what to do when labels are scarce (2.5) and what you owe the people a model is applied to (2.6).
It uses the same eight students plus a ten-student test set, and the same colour contract. Nothing in it requires re-reading this file, but section 2.4 assumes you know what a logistic probability is, from 2.3.1.
| From here | Reappears as |
|---|---|
| 2.1 Selection and hyperparameters | Choosing k for k-means and the elbow method (3.2), the number of components in PCA (3.3), and every architecture and learning-rate decision in Units 4 and 5. The protocol never changes. |
| 2.2 Multiple regression and regularization | The output layer of a regression network (4.1); weight decay, which is ridge applied to a network (4.6); and the reconstruction objective of an autoencoder (5.x), which is least squares in disguise. |
| 2.3.1 Logistic regression | A single-neuron network with a sigmoid output — literally the same equations (4.1). Softmax and cross-entropy are the output layer of every classifier in Units 4 and 5. |
| 2.3.2 k-NN and distance | k-means clustering (3.2), which is the same Euclidean geometry without labels; hierarchical clustering's linkage distances (3.2); and the scaling requirement everywhere. |
| 2.3.3 SVM and margins | Hinge loss as a training objective (4.1); the kernel idea reappearing as the feature maps a network learns rather than assumes; and margin-based reasoning in the generalisation discussion of 5.5. |
| 2.3.4 Decision trees | The base learner for 2.3.5, and the interpretability baseline that Unit 2B's section 2.6 measures other models against. |
| 2.3.5 Ensembles | Dropout as an implicit ensemble of subnetworks (4.6); the variance argument behind averaging model checkpoints; and the practical benchmark that any deep model on tabular data has to beat. |
| 2.3.6 Bayesian classifiers | Gaussian mixture models (3.2), which are the generative idea with the class labels removed; and the prior-versus-likelihood reasoning behind exploration in reinforcement learning (Unit 6). |
Before you move on
You should be able to do six things from a blank page. Solve a two-feature normal-equation system from the nine sums and state what a coefficient means. Compute a logistic probability, its log loss and its odds ratio. Rank neighbours by squared distance and vote, and say why scaling matters. Write the canonical SVM constraints, find w and b for a small separable set, and identify the support vectors. Compute an information gain including the branch weights and pick a root split. And carry out one AdaBoost round, checking that the misclassified mass lands on 0.5.
If any of those is shaky, that section's practice ladder is the fastest repair. Unit 2B assumes all six.
Further reading
- Géron, Hands-On Machine Learning, 3rd ed., ch. 4–7 — the prescribed textbook, and the best match to this file's scope. Chapter 5 for SVMs, 6 for trees, 7 for ensembles, all as runnable code with plots. Light on derivations.
- Hastie, Tibshirani and Friedman, The Elements of Statistical Learning, ch. 3, 4, 9, 10, 12, 15 — the reference treatment. Chapter 3 for regression and shrinkage, 12 for SVMs, 10 for boosting, 15 for random forests including the correlation argument of 2.3.5.
- Bishop, Pattern Recognition and Machine Learning, ch. 4 and 7 — the careful account of logistic regression, the generative-versus-discriminative distinction of 2.3.6, and the full SVM derivation with Lagrange multipliers.
- Mitchell, Machine Learning, ch. 3 and 6 — the clearest introduction to decision-tree induction and to Naive Bayes, including the smoothing argument.
- Bergstra and Bengio, "Random Search for Hyper-Parameter Optimization" (2012) — the source of the
0.95ᵀargument in Worked 2.1b, and short enough to read in one sitting.
Spine dataset: eight students, practice hours against assignments submitted, marks out of 20, pass at 12. Every numerical value in this file was computed rather than estimated.
Next: Unit 2B — Evaluation Metrics, Semi-supervised Learning and Responsible AI