<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Matheus Nogueira on Medium]]></title>
        <description><![CDATA[Stories by Matheus Nogueira on Medium]]></description>
        <link>https://medium.com/@matnogueira?source=rss-e77622360fa3------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Oj0VJWCpDMpT1f7-uG9jIQ.jpeg</url>
            <title>Stories by Matheus Nogueira on Medium</title>
            <link>https://medium.com/@matnogueira?source=rss-e77622360fa3------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 19:22:10 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@matnogueira/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Search, Shrink or Compress: Three Ways to Rescue Linear Regression from Too Many Features]]></title>
            <link>https://medium.com/@matnogueira/search-shrink-or-compress-three-ways-to-rescue-linear-regression-from-too-many-features-de8f3011e2fb?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/de8f3011e2fb</guid>
            <category><![CDATA[feature-engineering]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[regularization]]></category>
            <category><![CDATA[principal-component]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Mon, 09 Nov 2026 15:44:12 GMT</pubDate>
            <atom:updated>2026-11-09T15:44:12.970Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>Too many features break linear regression. Best subset, ridge, lasso and PCR each fix it by trading a little bias for much less variance.</blockquote><p><em>Every image and example was taken from the following sources:</em></p><ul><li><a href="https://www.youtube.com/watch?v=sYZ9LLJ9CdE"><em>Lecture 7 — Seleção de Modelos Lineares e Regularização — Machine Learning 2026 Summer Course at IMPA</em></a></li><li><a href="https://w3.impa.br/~pauloo/teaching/ml/"><em>Machine Learning 2026, Prof. Paulo Orenstein</em></a></li></ul><h3>Introduction</h3><p>These notes cover Lecture 7 of the <strong>IMPA Summer Course — Machine Learning 2026</strong> (Paulo Orenstein), one article per lecture. You can find the articles for the earlier lectures on <a href="https://medium.com/@matnogueira">my Medium profile</a>.</p><p>Everything below is <strong>self-contained</strong>: you do not need to read the other posts to follow this one, as long as you are comfortable with least squares, the bias-variance trade-off, cross-validation and a little linear algebra.</p><p>The lecture covers Chapter 6 of the course textbook and asks what to do when a linear model has too many features. It starts with three open questions, then works through three families of answers: <strong>subset selection</strong> (best subset, forward and backward stepwise), <strong>shrinkage</strong> (ridge and the lasso, including how they relate to best subset and when each one wins) and <strong>dimension reduction</strong> (principal components regression).</p><h3>Three open questions, one phenomenon</h3><p>Linear models are powerful: interpretable, extensible and good at prediction. The idea is simple, but understanding how each part works is what lets you extend them and put statistical structure on top. Still, three questions remain open.</p><p>The first is <strong>too many variables</strong>. It is absolutely fundamental to linear regression that the sample size n exceeds the number of predictors p. This is not a case of breaking one small thing. When p &gt; n, the matrix XᵀX cannot be inverted and the whole structure falls apart.</p><p>The second is <strong>predictive accuracy</strong>. Even when p &lt; n, given an enormous set of features, is there a good subset of them to predict with? Can we improve the mean squared error by reducing variance?</p><p>The third is <strong>interpretability</strong>. Perfectly collinear features bring back the non-invertible matrix, but high collinearity is a problem too. The variance of the least-squares estimator is essentially</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/377/1*9oIxWE_94cts8U4i7HP3pg.png" /></figure><p>and with nearly collinear features, XᵀX becomes very small, so the variance explodes. Move the dataset a little and the estimate moves a lot.</p><p>These look like three separate questions, but the lecture argues they are all the same phenomenon: many features relative to the sample size. Sometimes that literally means p &gt; n; sometimes it means that some of the features are very similar to one another. The area is also more modern than linear regression itself, because statistics and machine learning follow the data people want to analyse. From the 1990s, genetics brought datasets with perhaps 50 people in a study and thousands of gene-expression features for each one, and that renewed the interest in these questions beyond pure mathematics.</p><p>Following the textbook, the lecture looks at three families of methods:</p><p>1. <strong>Subset selection:</strong> identify a subset of the p predictors related to the response, and train only on those.</p><p>2. <strong>Shrinkage (or regularisation):</strong> train with all p predictors, but “shrink” the coefficients towards zero, which reduces variance and discards variables.</p><p>3. <strong>Dimension reduction:</strong> project the p predictors into a space of dimension M &lt; p, and use those M projections as the variables of the model.</p><p>The first is a computational solution, with no mathematical guarantee and no mathematical sophistication either. The second is much more mathematical. It starts from the fact that least squares has no bias, since 𝔼[β̂] = β, but can have very large variance. Why not accept a little bias in exchange for a drastic reduction in variance? The third combines features rather than choosing among them. The book treats two methods of this kind, and this lecture covers the first.</p><h3>Best subset selection: compare everything, and pay for it</h3><p>We start with the computational family. The idea is silly but reasonable: compare all the models with k variables, for k = 0, 1, …, p, and keep the best. Once you fix k, the number of models to compare is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/139/1*P_RFaitJJ2POWCP3_4AhRA.png" /></figure><p>and you pick the one with the lowest training error, meaning the lowest residual sum of squares (RSS) or the highest R².</p><p>We usually distrust training error, but the reason is specific: it makes models with different numbers of features hard to compare, since the lowest training error always favours more features. Here the number of features is fixed. The risk of overfitting comes from comparing a five-feature model with a six-feature one and concluding that six is better. Among five-feature models, whichever does best even on training error probably has the most relevant features.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/983/1*KBT7DoAANx5l3JWYjxeS6g.png" /><figcaption>Figure 1: RSS (left) and R² (right) against the number of predictors. Each dot is one candidate model and the red line follows the best of each size. The curves flatten quickly, but this is training error, so the comparison is only fair within one size.</figcaption></figure><p>That settles the choice within each size, and leaves you with one best model per size. To choose among those, the argument no longer holds, because now the comparison is across sizes. You want to minimise validation error, not training error, otherwise you would always pick k = p.</p><p>K-fold cross-validation estimates this well, but you would have to run it for every one of those models, which can be heavy. Historically, though no longer today, this motivated alternatives for linear regression that estimate validation error directly. The first is Mallows’ Cₚ, or the Akaike Information Criterion (AIC):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/507/1*TTPz_YDC8_L6lHhO0kPZFw.png" /></figure><p>The second is the Bayesian Information Criterion (BIC):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/607/1*7mg_dl3wcpL1q2VidfNw1A.png" /></figure><p>And the third is adjusted R²:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/468/1*ZGw-631-qv_tbM1ChQCJXA.png" /></figure><p>These come from asymptotic arguments with strong assumptions, such as Normal errors, and the derivations are not trivial. Deriving or even using them is not something that happens much. It is still useful to know what they are doing when someone mentions them: each one takes a notion of training error and reweights it by the number of features to make the comparison fairer. From this lecture’s point of view, they are simply ways of avoiding cross-validation when you have to.</p><p>The whole algorithm fits in three steps:</p><p>1. Let M₀ be the null model, with no predictors.</p><p>2. For k = 1, 2, …, p: train all the models with exactly k predictors and keep the one with the lowest training error, calling it Mₖ.</p><p>3. Among M₀, M₁, …, Mₚ, choose the model with the lowest validation error, using k-fold cross-validation, Cₚ (AIC), BIC or adjusted R².</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nNryStjyDW3qGLUY0nNW3Q.png" /><figcaption>Figure 2: Six criteria against the number of predictors, with a cross at each optimum: Cp at 6, BIC at 4, adjusted R² at 7, the square root of BIC at 4, validation error at 6 and cross-validation error at 6. They are variations on a theme, not identical, and they all flatten beyond three or four predictors.</figcaption></figure><p>There are two serious problems with this. The first is computational: searching every model means 2ᵖ possibilities, so unless you have something like p = 4, you are in trouble, and p = 100 or p = 1000 is out of the question.</p><p>The second problem is worse, because it is statistical. You have one fixed validation set, a random sample. If you test five, seven or ten models, the chance that one of them looks perfect on this sample purely by luck is very low. If you test thousands, that chance starts to grow: you try so many things that you will stumble on a model that happens to be very good on this one validation set and bad on everything else.</p><blockquote><em>Testing too many models overfits the validation set itself, so a bigger search is not only slower but statistically riskier.</em></blockquote><h3>Forward and backward stepwise: searching a smaller space</h3><p>Both problems suggest the same remedy: search fewer models, in a more directed way. Fewer models are cheaper to run and less likely to overfit. The tool is a greedy algorithm that adds or removes one feature at a time, and adding one at a time gives <strong>forward stepwise selection</strong>:</p><p>1. Let M₀ be the null model, with no predictors.</p><p>2. For k = 1, 2, …, p: consider all the models that extend the current one with one additional predictor, and keep the one with the lowest RSS (or the highest R²).</p><p>3. Among M₀, M₁, …, Mₚ, choose the model with the lowest validation error.</p><p>The change is in step 2. Once you have decided on your ideal model with three features, finding the one with four only requires looking at the features that remain. You do not reconsider every four-feature model. An example from the book shows the consequence:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*X3qRNdf1KJgFuLeovAHNWQ.png" /><figcaption>Table 1: The two methods agree until the fourth model, when best subset drops rating while forward stepwise is stuck with its earlier choices.</figcaption></figure><p>Best subset can pick more than one new predictor at a time, while forward stepwise can only add one to what it already has. What is probably happening is that rating is highly correlated with the other features: it is very useful when you may only choose one, but once you may choose four, a better combination exists without it.</p><p>On training error you can guarantee something. Everything available to forward stepwise is available to best subset, so best subset never loses there. On test error you can guarantee almost nothing. Sometimes forward wins, sometimes best subset wins, and sometimes a sillier model beats both. On the test set, you run it and see what happens.</p><p>If you can walk forwards, you can walk backwards, and <strong>backward stepwise selection</strong> starts from the full model Mₚ. For k = p, p-1, …, 1, consider each of the k models that drop one predictor from the current one, and keep the one with the lowest RSS (or the highest R²). Then choose among the resulting models by validation error, as before.</p><p>Both directions choose among only</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/305/1*SN5eiXQFJpbydB6jFYjvlg.png" /></figure><p>models, where the 1 is the null model. That is far smaller than 2ᵖ, so the risk of overfitting the validation set drops too, because the procedure is less flexible about which features it can combine. Neither guarantees the best model, though that may not be a problem. The two can also end up in different places: run both looking for the best five-feature model and you may get two different answers, because in a greedy algorithm the path matters.</p><p>There is one practical asymmetry. Backward selection cannot be used with p &gt; n, because it begins by fitting the full model, which you cannot do. Forward selection can start small and stop at a model of size n, so for large p it is the only option.</p><p>Variations are easy to invent. <strong>Mixed stepwise</strong> may discard a selected predictor at each forward step, taking two steps forward and one back. <strong>Forward stagewise</strong> decorrelates the remaining predictors after adding one, before choosing the next. If some features are only useful together, you can add them as groups. A student suggested computing the correlation between features and keeping only one of each highly correlated pair, which the lecturer welcomed as domain knowledge, like blocks of genes that probably express the same thing. There is a catch, though: cross-validation assumes the validation data have never been seen, so computing those correlations on the whole dataset is a risk.</p><p>In this computational world you have few restrictions, but also few guarantees. Notice also what has and has not changed. The data, the class of linear functions and the squared-error loss are the same as before. The only change is how the minimisation is solved: sequentially, instead of in closed form, because the closed form needs an invertible XᵀX.</p><h3>Ridge regression: change the loss, not the search</h3><p>The stepwise methods still train many models, and they deal with the cost by shrinking the search. A recurring move in this course offers another way out, which is to change the loss function. The idea is to train <strong>one single model</strong> with all p predictors, with a loss that encourages many of the coefficients to be as close to zero as possible, and ideally exactly zero.</p><p>Why would that be a good idea? If the coefficient of a feature is zero, it is as if the feature did not exist. Tomorrow, to make a prediction, you would not even need to collect it. Forcing coefficients towards zero certainly introduces bias, because the expected value of the estimate is no longer β. But it can drastically reduce the variance of the estimators, and potentially lower the test error.</p><p><strong>Ridge regression</strong> determines the coefficients by solving</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3h4CHv-YMOmy-8LX5IaIWA.png" /></figure><p>The first term is the RSS of the linear model, exactly as before. The second term is new: λ times the squared L2 norm of the coefficients, which imposes a penalty in favour of shrinkage. Two extreme cases give the intuition. With λ = 0 the penalty disappears and you are back to ordinary least squares. With λ = ∞, any non-zero coefficient would make the loss infinite, so the only option is to set them all to zero, giving a constant prediction that ignores the features entirely. In between, λ is a hyperparameter that measures how much you care about fit versus shrinkage.</p><p>Remarkably, this problem has a closed form:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/742/1*UUAU4iBa87u_kN70mRraZQ.png" /></figure><p>Before, the trouble was that XᵀX was not invertible when p &gt; n. Simply adding λ along the diagonal, with λ &gt; 0, makes the matrix positive definite and therefore invertible. After a long stretch of the course with no closed forms, in logistic regression and GLMs, we have one again, even when p &gt; n. (Negative values of λ are a different matter: the lecturer called that case heretical and left it for after class.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bS9ct6afxmv9vYA0Nw94iA.png" /><figcaption>Figure 3: Ridge coefficients on the Credit data, against λ (left) and against the ratio of the ridge L2 norm to the least-squares one (right). The two panels show the same thing with a reconfigured axis. Everything drifts smoothly to zero, though rating (blue dotted) rises before it falls.</figcaption></figure><p>With λ close to zero, this is almost ordinary regression: limit sits above 400 and income near -300. As λ grows, the coefficients converge to zero, and past a certain point the prediction is essentially constant. The path need not be monotone, as rating shows. This is uncommon, and it echoes the earlier table: as limit is forced down, the information it carried is lost, and a previously less relevant variable can gain weight as the variables reconfigure. The only thing being imposed is that the L2 norm of the whole vector falls as λ grows.</p><p>How do we pick λ? The same way we pick any hyperparameter: try many values and use cross-validation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*q3Zb7ON3tj7gB4phPWgWOw.png" /><figcaption>Figure 4: Choosing λ for ridge. Left, cross-validation error against λ; right, the coefficients on the same axis. The dashed line at the minimum selects one vector of coefficients, at an intermediate λ rather than at either extreme.</figcaption></figure><p>Since the grid could have included values close to the extremes, an intermediate minimum suggests a model better than ordinary regression. This is where the opening questions start to combine: better prediction, a smaller effective set of features, and something easier to interpret.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PeOFgiDZ5ToFZZgHNB8prQ.png" /><figcaption>Figure 5: A simulation with ridge: squared bias (black), variance (green) and test MSE (pink), against λ (left) and against the norm ratio (right). The cross marks the lowest test MSE, reached where rising bias and falling variance balance.</figcaption></figure><p>As λ increases, bias rises and variance falls, so you are walking along the bias-variance trade-off. The lecturer added that you can prove there is always a non-negative λ giving a model strictly better on average than linear regression. The catch is that finding it is not trivial: you must estimate it by cross-validation, and the act of estimating it may be enough to lose the advantage. In that case, plain linear regression is worth keeping.</p><h4>Two practical details</h4><p>Ordinary least squares is invariant to scale. If you replace Xⱼ with cXⱼ, the optimal coefficient simply becomes β̂ⱼ/c, so metres or centimetres make no difference to the predictions. Ridge does not have this luxury. With a finite budget for the norm, you cannot multiply one coefficient by 100 and get away with it, so the same problem in metres and in centimetres gives different models. The fix is to put every predictor on the same scale, with empirical variance 1, before fitting:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/602/1*roFda7KpadBN8l_u7ViRMA.png" /></figure><p>This stops some coefficients being unfairly penalised more than others.</p><p>You may also have noticed that the penalty leaves out β₀. Nothing is mathematically wrong with penalising it, but the intercept acts as an estimate of the mean: if you had to predict a single number, it would be this one. In a genetics problem some features are relevant, but the intercept is always relevant, so it is not shrunk. The practical way to do this uses its least-squares form,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/451/1*vK3BaXspcXDEclDh_epvqA.png" /></figure><p>Substituting this into the residual of an observation gives</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qo3qgMTmGxRfQZ__EsOb3w.png" /></figure><p>So you centre y and the columns of X, marked below with the subscript c, and solve</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/778/1*KgA3WgyKsvN3GA8RO5dxYg.png" /></figure><p>Then you add ȳ back when predicting. Any Python package already does this for you.</p><h3>The lasso: coefficients that are exactly zero</h3><p>Ridge solves a lot, but it never quite does what the shrinkage idea promised at the start: discarding features. The second shrinkage method changes one small detail. The <strong>lasso</strong> solves</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Aedaftxm-iZAeDMnNZsQyA.png" /></figure><p>This is almost the same problem, with the L1 norm in place of the squared L2 norm. Why bother with a separate name? Because from an applied point of view it is very different. Ridge shrinks all the coefficients jointly and smoothly, and essentially none of them is ever exactly zero. The lasso sets several of them to zero: not nearly zero, but exactly 0.0.</p><p>Given how this lecture began, that matters a lot. Suppose you run the lasso on a genetics dataset and find that many genes are irrelevant to, say, predicting cancer. Tomorrow you do not need to collect those features at all. With ridge that never happens, since every feature still matters, some of them only very slightly, and you cannot throw them out. The price is that the lasso, unlike ridge, has no closed form.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bzUTuNDiGqAkLz8oslW1CA.png" /><figcaption>Figure 6: The lasso on the Credit data, against λ (left) and against the ratio of L1 norms (right). Compare with Figure 3: here income, limit and student hit exactly zero at finite λ, and the grey curves sit on the zero line for much of the path.</figcaption></figure><p>A student asked why these curves lose their smoothness. The answer is that the model is being forced to make a decision: include the variable or not. When a coefficient near zero becomes zero, that discrete decision has a systemic effect, and the model reconfigures in a non-smooth way. Ridge adjusts each coefficient smoothly, so nothing suddenly drops out. This ability to set coefficients to exactly zero is the main reason the lasso is so widely used.</p><h3>L0, L1, L2: one problem written three ways</h3><p>So far best subset and the penalised methods look like different worlds, one computational and the other mathematical. Rewriting the penalties makes them meet. Thinking in terms of Lagrange multipliers, which is where the λ comes from, ridge is equivalent to minimising the RSS subject to a budget s:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LNlt3nRNwze6dRr6ErGO_w.png" /></figure><p>Here s is a hyperparameter of the same character as λ: for every λ there is an s giving an equivalent solution, and vice versa. The lasso is the same with the L1 norm:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WMQ7lgZaWd6OLpQdyRMufg.png" /></figure><p>And best subset selection, the purely computational problem we started with, turns out to fit the same form:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HPjrbvI_hIjfYSjWslnoCQ.png" /></figure><p>This last constraint is the so-called <strong>L0 norm</strong>. It does not penalise the magnitude of the vector but the inclusion of a feature: include it and pay one, leave it out and pay zero. So we are moving from L0 to L1 to L2. L2 gives a closed form. L1 does not, but it has an enormous advantage over best subset: the problem is <strong>convex</strong>, which means we know how to solve it efficiently.</p><blockquote><em>One way to think of the lasso is as the convexification of best subset selection: a problem we would love to solve, replaced by a close one that we can.</em></blockquote><p>The analogy with classification is direct. There, the 0–1 loss is what we want but cannot minimise, so we use the logistic loss. Here the L0 norm is what interests us, but we cannot optimise it, so we use L1. Computation, algorithms, optimisation and mathematics, which looked like totally different ideas, start talking to one another. Asked whether these methods recover the “right” coefficients of some generating process, the lecturer was careful: there is no such guarantee. What you get is a coefficient vector with a small norm that predicts very well.</p><p>Why does L1 produce zeros in the first place? The book justifies it with a picture.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*z77Onr6VIAFEpwxEth8Feg.png" /><figcaption>Figure 7: With two predictors, the red curves are level sets of the squared error around the least-squares estimate, and the cyan region is what the budget allows. The lasso’s diamond (left) is touched at a corner on the β₂ axis, where β₁ is exactly zero; ridge’s disk (right) is touched at a point where neither coefficient is.</figcaption></figure><p>Solving the problem means finding the best level curve that still touches the admissible region. Ridge’s ball is smooth, so short of very special circumstances, the curve touches it at a point where no coefficient is exactly zero. The lasso’s ball is a diamond, and its geometry pushes the best point to the extremes, which are exactly the points where a coefficient vanishes.</p><p>The lecturer treats this picture with some suspicion. The lasso is meant for very large p, thousands or millions of features, and it is not clear that the drawing looks like this in such dimensions. There are good mathematical reasons for the behaviour, and it is very clearly understood, but the visual intuition is a bit tricky. The picture does its job of showing that the two norms do very different things.</p><p>You can also place the norms on a continuum. L0 would be just two sticks along the axes, since a variable is either in or out. L1 inflates those sticks into the diamond. From there the ball gets rounder and rounder, and at the infinity norm it becomes a square. Asked why not use something between L0 and L1, the lecturer turned the question around: what algorithm would you use to optimise it? L1 is the trade-off, because it is convex.</p><p>Stepping back, the problem now is to minimise a penalised squared error,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kb88yQZ9QbS6oBZCdIIJCA.png" /></figure><p>with the L1 norm or, for the closed form, the squared L2 norm. It is convex, so it admits global solutions. Stepwise methods approximate best subset algorithmically; these approximate it by changing the loss. And nothing here is special to linear regression. A student asked whether GLMs have an equivalent, and they do: somewhere inside there is a linear form, so you penalise β there, and a convex function composed with a linear one stays convex. Predicting bicycle use across Rio de Janeiro with every feature you can collect, you would use L1 if you think few features matter, and L2 if you just want stability for better prediction.</p><h3>Ridge or lasso? What each one assumes</h3><p>If the two penalties are so close, how do you choose? Since the lasso forces some coefficients to be exactly zero, you would expect ridge to do better when most coefficients are non-zero. A simulation from the book confirms it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/785/1*PNQ0yCCIxRkToJzd3N60kw.png" /><figcaption>Figure 8: When most coefficients are non-zero: squared bias (black), variance (green) and MSE (pink), with the lasso solid and ridge dotted. The left panel shows the lasso against λ; the right compares both against R² on the training data, where ridge’s MSE is lower because its variance is.</figcaption></figure><p>In both methods, larger λ means more bias and less variance. Here the bias curves are almost the same, but ridge gains in variance by shrinking all the coefficients homogeneously. Now run the same simulation with only two non-zero coefficients, without knowing which two.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/784/1*qI2PIlVZszSSuj14Ub7fhw.png" /><figcaption>Figure 9: The same layout when only two coefficients are non-zero. The right panel flips: the solid lasso curves now sit below the dotted ridge ones, and the lasso reaches the lower MSE.</figcaption></figure><p>The picture reverses: both the bias and the variance of the lasso are lower, and the gain in error is large. The moral is that the mathematics is very similar but the assumption behind it is not. With ridge you assume many features are relevant, but shrinking them gives a better prediction. With the lasso you assume that only some are relevant and the others should be exactly zero.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*olIbwwhE4w4l3ayzQceXcw.png" /><figcaption>Figure 10: Choosing λ for the lasso with 10-fold cross-validation: the error (left) and the coefficients (right), against the ratio of L1 norms. At the dashed line only two coefficients, pink and green, are clearly away from zero.</figcaption></figure><p>Tuning works just as before, and in this example the model chosen by 10-fold cross-validation uses only two features.</p><h4>A case you can solve by hand</h4><p>Because the lasso usually lives in very high dimension, refined understanding of its mathematics is hard. The case n = p with X = I simplifies everything: multiplying by the identity just picks out each entry, so each coefficient can be minimised separately. Ridge then minimises</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/829/1*AJ3L-OBBDfD8twG1IEppJQ.png" /></figure><p>and the solution follows at once from the closed form, since XᵀX = I and Xᵀy = y:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/304/1*gM4QoF4jzzYIC5u9geUULw.png" /></figure><p>The interesting part is that the lasso can also be solved here. It minimises</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/862/1*QVcW4o4bShr5JsCO3oNdUQ.png" /></figure><p>The absolute value has a kink at zero, with no derivative there. The technique is the <strong>subgradient</strong>, which amounts to pretending you can differentiate at zero, since any slope in the kink will do. Elsewhere the derivative is the sign function, and setting the first-order condition to zero gives three cases. For large positive values,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/774/1*4egc69XOz5vvw3CUHZww1A.png" /></figure><p>For large negative values,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/822/1*4ztyFsl28MlXHq21yUsEmw.png" /></figure><p>And everything small collapses:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/603/1*rouOt5U6pf-UpX35pyyw-A.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Slm3wrbMYhEAm5BkIm0tDw.png" /><figcaption>Figure 11: Coefficient estimate against yⱼ, with least squares as the dashed identity line. Ridge (left, red) tilts the line, pulling large values in the most. The lasso (right, red) is flat at zero in a band around the origin, then runs parallel to the identity at a fixed distance from it.</figcaption></figure><p>Here the two behaviours are plain to see. Ridge shrinks <strong>proportionally</strong>: the slope goes from 1 to 1/(1+λ), so a bigger yⱼ is pulled in by more. The lasso shrinks <strong>equally</strong>: anything very small collapses to exactly zero, and everything else moves by the same amount, whatever the size of yⱼ. This has everything to do with the L0 loss it approximates. In best subset, the size of a coefficient never mattered, only whether its feature was in. The lasso behaves similarly: some coefficients become zero, and the rest are not penalised more for being large.</p><h3>Principal components regression: compress the features first</h3><p>The third family takes a different route, somewhere between the other two. Instead of touching the search or the loss, it transforms X₁, …, Xₚ to improve the regression. Picture collecting everything you can about bicycle use in Rio: temperature, whether it is sunny, and much more. All those features look at the same phenomenon, which is how the weather is today. In genetics, many genes may relate to the same protein. Rather than choosing among them, why not combine them?</p><p>Suppose we build M &lt; p new variables, each a linear combination of the original predictors, with constants φ still to be chosen:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/563/1*-lXMpH1Xk8guV5FiWuFxJQ.png" /></figure><p>Then fit an ordinary linear regression on these new variables, with coefficients θ:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/786/1*KIYjZ-II2eB3SJEwa7TJ1g.png" /></figure><p>If the constants are well chosen, this can beat the original regression. To see what is going on, substitute the definition of the z and group the terms by x:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*QAk17CpP2XGIwY0xAMg3Eg.png" /></figure><p>So this is a special case of ordinary linear regression, where each coefficient is restricted to the form</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/550/1*lc_ytor-mdnSgCx1MxWzbA.png" /></figure><p>The smaller M is, the tighter the restriction, which is natural when you compress everything into a couple of features. As with stepwise methods we are restricting a space, but it is no longer a computational one: it is the space of admissible parameter values, as if we were changing the class of functions the model can take. If the Zₘ summarise the predictors well, <strong>principal components regression (PCR)</strong> can reduce variance without costing much bias. Some bias does come in, as with ridge and the lasso, because β can no longer be anything. A student asked whether this also narrows confidence intervals. If the constants are chosen from the data, the lecturer answered, you cannot even talk about confidence intervals, because estimating in two stages loses the assumptions behind them.</p><p>The remaining question is how to choose these new variables. For now, the proposal is to do it without using Y, a decision to be revisited in the next lecture. One reason is to avoid overfitting, since Y is already used to estimate the θ. The choice is this: Z₁ is the direction that maximises the empirical variance of the data. The second direction also maximises the variance, subject to being orthogonal to the first, and so on.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/845/1*NjdCB8G6eI0-4jmb3dMSaA.png" /><figcaption>Figure 12: Ad spending against population. The green line is the first principal component, the direction along which the points vary most. The dashed blue line is the second, orthogonal to the first, along which there is very little going on.</figcaption></figure><p>Nearly all the variation in these data happens along the green line, while the blue direction looks like noise. Notice that nothing here says what we want to predict; we are only trying to summarise. The two variables carry practically the same information, so we could consolidate them into one.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*gxLMzSv0PXtblMG-u_Qkfg.png" /><figcaption>Figure 13: Left, each point is projected onto the first component, with dashed segments showing the projections. Right, the same data rotated so that the first component is the horizontal axis and the second the vertical one, whose values stay small.</figcaption></figure><p>Using a direction means projecting onto it: each point in two dimensions lands on the green line, which summarises it with one number. With only two dimensions, there is just one orthogonal direction left, but in higher dimensions you repeat the search many times and keep only the first M directions. The second component looks unimportant here, and plotting the original variables against each component confirms it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KNH8MUYSWZDmXvP-zWOsVw.png" /><figcaption>Figure 14: Population (left) and ad spending (right) against the first principal component. Both follow it closely, so knowing this one number tells you most of what is happening with the two variables.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0YVyxUbLRaKZEXqjtsBMKw.png" /><figcaption>Figure 15: The same plots against the second principal component. The clouds show no strong relation, which supports summarising the data with the first component alone.</figcaption></figure><h3>Finding the directions, and choosing how many</h3><p>The picture needs mathematics behind it. Centre the columns of X to mean zero, again marking the result with the subscript c. The empirical variance is then the matrix</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/385/1*HUDgoHQDDJu4LMDG8FbqqA.png" /></figure><p>The direction we want maximises the variance, among unit vectors:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RdciMeYeUPdNeMljxcmBYg.png" /></figure><p>The constraint is necessary, because otherwise scaling v would make the objective arbitrarily large. The solution is the first eigenvector of S, the one associated with the largest eigenvalue. If you have only taken one linear algebra course, you may think of eigenvectors through the characteristic polynomial, but here is another lovely characterisation: write the Lagrangian of this problem and what comes out is exactly the eigenvalue equation Av = λ v. Eigenvectors can be defined as the solutions of this optimisation problem.</p><p>The combination of the features with the largest variance is then</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/323/1*h5fTTwJ1L_YggI2m4DAa9g.png" /></figure><p>This really is a projection, much as linear regression is a projection on the column space, with the usual inverse term disappearing because v₁ has norm one. Requiring the next vector to be perpendicular to the first gives the second eigenvector, and so on. In matrix form, if the singular value decomposition is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/401/1*AKsGTXQ40ylaVd0SuKvATQ.png" /></figure><p>then V holds the eigenvectors, and all the components come at once:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/301/1*9JkSYjqztYgGax14dxRDKw.png" /></figure><p>So the abstract-looking search for directions is nothing more than finding eigenvectors of the empirical covariance matrix, which is not hard at all with an SVD in hand. There is one last detail. As with ridge, scale matters: multiply ad spending by a trillion and both your eyes and the mathematics would suggest a different direction. So the columns are also normalised to variance 1, and the full algorithm reads:</p><p>1. Normalise the columns of X to mean zero and variance one.</p><p>2. Compute the SVD to obtain the eigenvectors in V.</p><p>3. For m = 1, …, M, obtain the m-th component by multiplying the normalised matrix by vₘ, the m-th column of V.</p><p>4. Return the predictions of the linear regression of y on the M components, with intercept ȳ:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/892/1*sJKgVLpSB_FWNeF7ibHxLQ.png" /></figure><p>How many components should you keep? Like λ, M is a hyperparameter, and cross-validation is the natural way to choose it. That is why the chapter on validation had to come before this one: the models now need hyperparameters, and they work only if you know how to choose them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KXFK8SNqTCpD1N9OWKY1tg.png" /><figcaption>Figure 16: PCR on simulated data: squared bias (black), test MSE (pink) and variance (green) against the number of components, in two simulations. In both, variance climbs steadily as bias falls, and the test MSE combines the two.</figcaption></figure><p>As you add components, the variance rises and the bias falls. And when M = p, you are essentially doing linear regression, so PCR is, once again, a way to trade bias against variance.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VB2RzMOL0n5A0maqweDhPA.png" /><figcaption>Figure 17: PCR on the Credit data: the coefficients (left) and the cross-validation MSE (right) against the number of components. The error stays high until it collapses at 10 components, and 11 brings essentially no further gain.</figcaption></figure><p>On the Credit data, the model with 10 components is the best, and 11 would be the usual regression. With 11 features, you can apparently reduce to 10 without losing any predictive performance.</p><p>The last slide compares the methods on a simulation built with n = 50 and p = 45, where y depends only on the first five principal components.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lA96VzYKWrtOQ7SaITzJIg.png" /><figcaption>Figure 18: Squared bias (black), test MSE (pink) and variance (green) when y depends on the first five principal components. Left, PCR against the number of components; right, ridge and the lasso (solid and dotted) against the shrinkage factor. Both show a steep early drop in error followed by a slow climb.</figcaption></figure><p>With PCR, adding components helps until you reach five. From there the bias stays flat, since y can already be expressed perfectly and extra features add nothing, while the variance keeps growing because the predictions depend more and more on the data. Ridge and the lasso are not the same figure, but they are very similar, and they tell the same story.</p><h3>Conclusion</h3><p>The lecture started from three questions that turned out to be one: what to do with many features relative to the sample size. Best subset answers it by brute force, comparing every model, at a cost that is both computational and statistical, and forward and backward stepwise make that search affordable by being greedy. Ridge and the lasso train a single model with a penalty: ridge has a closed form that works even with p &gt; n, while the lasso sets coefficients to exactly zero and is the convexification of best subset. Which one wins depends on whether you believe many features matter or only a few. PCR compresses the features into directions of maximum variance, found as eigenvectors, and runs a regression on the first few.</p><p>For all their differences, these methods share a logic. Sometimes you restrict the optimiser, sometimes the loss, sometimes the class of functions, and each time you do it carefully, with a hyperparameter balancing the two sides, to trade bias against variance and beat plain linear regression. The lecturer finds this dance between the three objects very beautiful, not least because the ideas arrived at very different times, the lasso being relatively recent. What comes next is taking the linear model to its limit, followed by extensions in directions that are surprising and very good at prediction.</p><p><em>Thanks for reading! If you found this useful, consider following for more articles on machine learning and statistical modelling.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=de8f3011e2fb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[One Recipe, Many Distributions — and How Not to Fool Yourself: GLMs, Cross-Validation and the…]]></title>
            <link>https://medium.com/@matnogueira/one-recipe-many-distributions-and-how-not-to-fool-yourself-glms-cross-validation-and-the-295e29a71a51?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/295e29a71a51</guid>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[mathematics]]></category>
            <category><![CDATA[boostrap]]></category>
            <category><![CDATA[glm]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Thu, 05 Nov 2026 18:00:53 GMT</pubDate>
            <atom:updated>2026-11-05T18:00:53.569Z</atom:updated>
            <content:encoded><![CDATA[<h3>One Recipe, Many Distributions — and How Not to Fool Yourself: GLMs, Cross-Validation and the Bootstrap</h3><blockquote>The GLM recipe goes beyond classification. Then: how to estimate test error with cross-validation, and uncertainty with the bootstrap.</blockquote><p><em>Every image and example was taken from the following sources:</em></p><ul><li><a href="https://www.youtube.com/watch?v=jf0wMpZ_guA"><em>Lecture 5 — GLMs e Métodos de Reamostragem — Machine Learning 2026 Summer Course at IMPA</em></a></li><li><a href="https://w3.impa.br/~pauloo/teaching/ml/"><em>Machine Learning 2026, Prof. Paulo Orenstein</em></a></li></ul><h3>Introduction</h3><p>These notes cover Lecture 5 of the <strong>IMPA Summer Course — Machine Learning 2026</strong> (Paulo Orenstein), one article per lecture.</p><p>If you want extra context, the earlier articles in the series follow the course order:</p><p>1. <em>Foundations of Statistical Learning: From Rules to Data, and What Makes an Estimate Good</em></p><p>2. <em>Linear Regression: Least Squares, Inference, and How Far a “Simple” Model Stretches</em></p><p>3. <em>When Linear Regression Breaks: Five Ways the Assumptions Fail, and How to Compute β̂ Without Inverting a Matrix</em></p><p>4. <em>Classification Is Not More of the Same: Logistic Regression, Discriminant Analysis, and How to Judge a Classifier</em></p><p>Everything below is <strong>self-contained</strong>: you do not need to read the other posts to follow this one, as long as you are comfortable with likelihoods, the idea of training and test error, and a little probability.</p><p>The lecture has two halves. The first closes Chapter 4 of the course textbook with <strong>generalised linear models</strong>: the recipe used to build logistic regression, applied to count data, gives <strong>Poisson regression</strong>. The second covers all of Chapter 5, on <strong>resampling methods</strong>. It asks how to estimate test error and choose hyperparameters without cheating, moving through the validation set, leave-one-out and k-fold cross-validation, the one-standard-error rule, the wrong and right way to cross-validate, and the practical traps around all of it. It then asks how to measure an estimator’s uncertainty when no formula is available, which is the job of the <strong>bootstrap</strong>.</p><h3>One recipe, many distributions: GLMs and Poisson regression</h3><p>So far the course has sorted its methods by the kind of output. If y is a real number, we use regression; if it is 0/1 or one of a few categories, we use classification. What if it is neither? A very common case is a <strong>count</strong>: the number of films someone watches in a month, or the rounded number of hours a day they spend on some activity. Linear regression can predict negative or fractional counts, and there is essentially no way to stop it. Logistic regression is built for categories with no order, which a count is not.</p><p>Rather than invent a new method for every new type of data, the lecture proposes to <strong>generalise the recipe behind logistic regression</strong>. That recipe had four steps. Choose a distribution for Y given X (there, the Bernoulli). Write its log-likelihood. Make the term that multiplies y linear in the features. Minimise the negative log-likelihood. For counts, a natural distribution is the <strong>Poisson</strong>, which only takes the values 0, 1, 2, and so on:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zm3dLr1_RR6fApJ2XZM0tA.png" /></figure><p>Its parameter λ(x) is at once the mean and the variance of Y given X = x. That is new: in linear regression the mean and the variance were separate things, while here choosing the mean already fixes the variance. Taking logs gives the log-likelihood of one observation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bZNlLkJUNsYzdb4Pxsm83Q.png" /></figure><p>The term multiplying y is log λ(x), so that is what we make linear:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/970/1*OvuMFeyxntuHG2nXOCxVEg.png" /></figure><p>Equivalently, λ is the exponential of a linear predictor, which keeps it positive. Estimating λ now means estimating the coefficients. Maximum likelihood (with the log-factorial term dropped, since it does not involve the parameters) gives the objective</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-Fqx-uZVFMrPLhnDUNVYHA.png" /></figure><p>Once more, yᵢ multiplies something linear in the parameters, and once more the problem is <strong>convex</strong> in the coefficients, so gradient descent or Newton’s method converge. The lecturer called it a mathematical miracle the course would not explain: for almost any reasonable distribution, precisely those in an <em>exponential family</em>, the same recipe leads to a convex problem. If you do not know what an exponential family is, you can set up the problem and check convexity directly. With β̂ in hand you get λ̂, and from it any probability you like, such as the chance of zero events or of more than five. To estimate the probability that someone spends more than five hours a day on their phone, from features like how often they exercise or how many books they read, sum the fitted Poisson probabilities beyond five.</p><p>Compared with linear regression, three things change:</p><p>- <strong>Variance.</strong> Poisson regression is rigid: the conditional mean and variance are the same number, λ. Heteroscedasticity, which was a problem to defend against in linear regression, is here part of the design.</p><p>- <strong>Non-negativity.</strong> Negative values are never predicted.</p><p>- <strong>Interpretation.</strong> Increasing a feature by one <em>multiplies</em> the expected count by e raised to its coefficient, instead of adding the coefficient. That is simpler again than in logistic regression.</p><h4>Bikes in the city</h4><p>The textbook example is a bike-sharing system, like the public bikes on some streets in Rio. Whoever runs it needs to know how many users to expect at a station each hour, so bikes can be moved around. That means predicting integers: nobody will ever see 3.5 users.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/449/1*bpV98K5rtulDy06UPBkPew.png" /><figcaption>Figure 1: Number of bikers per hour against the hour of the day, with the mean in green. In the early hours the counts are tiny and tightly packed; at busy hours they spread over hundreds. The spread grows with the mean, which is exactly what the Poisson model assumes.</figcaption></figure><p>Figure 1 shows why Poisson fits. At 2 a.m. there might be two users and almost no variation. At a busy hour, sun or rain moves the count a lot. The variability grows with the mean. The book fits a Poisson regression with dummy variables for each month and each hour of the day.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/450/1*Wy_42eWchKqbGIzSeo20QA.png" /><figcaption>Figure 2: Fitted coefficient for each month dummy, January to December. They are strongly negative in winter, rise through spring, stay high from May to October and fall back towards December.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/449/1*Um4Vj8YPvKGtP5MR7h9jaw.png" /><figcaption>Figure 3: Fitted coefficient for each hour-of-day dummy. The lowest value is around 5 a.m.; there is a peak in the morning, a plateau through the day and a higher peak in the late afternoon.</figcaption></figure><p>Negative coefficients are no problem; each one acts through e raised to its value. The data are from the US, so the warmer months are the busier ones, and the bikes are used far more during the day than before dawn (Figures 2 and 3). With a model like this, the operator does not have to wait for a dashboard to show that a station has run dry. They can predict how many users a station will see in two hours and move bikes in advance. They can also test hypotheses, for instance whether mornings are busier than afternoons.</p><h4>What Chapter 4 was really about</h4><p>The lecturer framed this as the twist at the end of the classification chapter. The methodology behind logistic regression was never a classification method. Pick a distribution for Y, write its log-likelihood, treat it as a loss and estimate the coefficients: that is a general philosophy. Interested in 0/1 outcomes, you chose the Bernoulli and got logistic regression. Interested in counts, you choose the Poisson and get Poisson regression.</p><p>Generalised linear models also come with confidence intervals and hypothesis tests, which are <strong>asymptotic</strong>, but work very well. There is no closed form for β̂. What you are guaranteed is that gradient descent, with a little care over the step size, reaches the right answer. Leaving linear regression can feel like losing things: first the formula, then the exact distribution of the estimator. But the world of models is enormous, and landing on something convex is a stroke of luck. The 0–1 loss was not convex, and the whole construction existed to swap it for a loss we can optimise. It is the course’s recurring theme again: the class of functions, the loss and the optimiser shape one another.</p><p>Asked whether the Poisson assumption can be tested, the lecturer said there are tests, but all of them require setting part of the data aside. In predictive work, unless you hold a strong prior, you run the model and see whether it works. Choosing Poisson should not be only about having the right support. It is also a question of whether the distribution suits how the data arise, just as Gaussian noise in linear regression was justified as the sum of many small independent effects. Asked about adding higher-order terms, the lecturer pointed to the usual risk: more terms, more overfitting, and you decide when to stop by looking at test error. That question leads straight into the rest of the lecture.</p><h3>Why training error proves nothing</h3><p>Chapter 5 changes hats. Until now the course has discussed <em>methods</em>: linear and logistic regression, LDA, QDA, naive Bayes. Now the topic is <em>methodology</em>: how to tell whether a method worked. In the lecturer’s experience, this matters much more than picking the right model. A poor model will be caught and discarded by a sound evaluation, but if the evaluation itself is wrong, everything is lost. The chapter has two parts, validation and the bootstrap, and both lean on computation rather than mathematics. As models grow more complex, expecting closed-form estimators and exact tests becomes naive.</p><p>The question left hanging at the end of the previous lecture motivates the first part. With so many classifiers available, how do you choose? The test set is meant to estimate performance in the real world. Say a doctor will use your model on patients only if its error is below 10%. If you use the test set to <em>pick</em> between LDA, QDA and naive Bayes, you keep whichever did best on it, and the error you report is optimistic: you are cheating. Choosing on the training set is worse still, because it pushes you towards far more predictors than you need.</p><p>Picture an intern who arrives with a fantastic prediction function. You will deploy it only if the error in production is below 5%, and the intern reports 1% on the training data. That number means nothing: a low training error is very easy to get, and you do not know what the intern did behind the scenes. The safe procedure is the one the course has used so far. Split the data at random into training and test sets, give the intern only the training part, and evaluate their model on the part you kept. Whatever they did — a neural network, a regression, overfitting or underfitting — the test estimate is fair, because they had no way to tune it.</p><blockquote><em>Training error usually underestimates test error dramatically; the gap between the two is what tells you something.</em></blockquote><p>The training error is not useless, though. Ask the intern for it alongside the test predictions, because the <em>relationship</em> between the two diagnoses underfitting and overfitting.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/773/1*2VvsPaKUimq-ms0g0bJ2OQ.png" /><figcaption>Figure 4: Prediction error against model complexity. The training error (cyan) only goes down. The test error (red) falls, bottoms out and rises again. The left side is labelled high bias, low variance; the right side low bias, high variance.</figcaption></figure><p>On the left of Figure 4 the test curve is still falling and stays close to the training curve, so there is no overfitting yet: high bias, low variance. On the right, the training curve keeps falling while the test curve climbs, which is the signature of a model that is too flexible. With complex models such as boosting or neural networks, looking at these two curves is the first diagnostic. A very low training error next to a high test error says to reduce flexibility.</p><h3>Three sets, not two: the validation set</h3><p>The intern also has decisions to make: logistic regression or naive Bayes, LDA or QDA, which polynomial degree. They should do what you did, at their own scale. Split the data they received into a training part and a <strong>validation</strong> part, fit on the first and compare candidates on the second. In practice the data end up in <strong>three</strong> sets:</p><p>- <strong>training data</strong>, used to fit the model for each candidate value of a hyperparameter;</p><p>- <strong>validation data</strong>, where the hyperparameter with the lowest error is chosen;</p><p>- <strong>test data</strong>, used once the final model is fixed, as the measure of future performance.</p><p>The distinction is between <strong>parameters</strong>, which the algorithm learns (the coefficients of a regression), and <strong>hyperparameters</strong>, which it does not (LDA versus QDA, the degree of a polynomial). Getting this wrong is, in the lecturer’s words, one of the most common and most basic errors in the field. Note also that each set holds less data than you have. The usual practice is to retrain the chosen model on everything before handing it to the doctor, since there is no reason to waste data, which makes the estimates along the way somewhat pessimistic.</p><p>A student asked how models trained with different losses can be compared. The evaluator fixes <em>their</em> loss, say mean squared error or the 0–1 loss, and announces it. Each colleague can train however they like, logistic loss included, but everyone delivers predictions, and those are scored with the evaluator’s fixed loss. The training loss is just one more choice inside each method.</p><p>The <strong>validation-set approach</strong> is the procedure used so far: split the data in two, train on the first part, compute the error on the second, and pick the candidate with the lowest error.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/710/1*S8SOvJqBQeo6-3aMQldNwg.png" /><figcaption>Figure 5: The validation-set approach. The n observations are split at random into a training part (blue) and a validation part (orange).</figcaption></figure><p>The lecture’s example is a polynomial regression of fuel efficiency (mpg) on horsepower in the textbook’s Auto data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/485/1*auqDCYz74TtaVZmfYYJuzg.png" /><figcaption>Figure 6: Validation mean squared error against polynomial degree for a single random split. The error drops sharply from degree 1 to degree 2 and then stays roughly flat.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/484/1*axSfZouqqhZHfRAYtTmoQA.png" /><figcaption>Figure 7: The same experiment repeated with different random splits, one curve each. Every curve drops from degree 1 to degree 2, but their levels differ widely, from around 16 to above 23 at degree 2.</figcaption></figure><p>Figure 7 exposes the problem. A different random 10% held out gives a different curve. Every split agrees that degree 2 beats degree 1, but the level of error you would call acceptable changes a lot, so a random choice ends up driving your view of the model. As a student pointed out, one unlucky split can send a cluster of unusual points into validation that the model never learned to predict. The approach has three problems:</p><p>- the estimates vary a lot with the split;</p><p>- only part of the data is used to train;</p><p>- the validation error tends to <strong>overestimate</strong> the test error, because the model is trained on a fraction of the data and will later be retrained on all of it, where more data will make it better.</p><h3>Leave-one-out and k-fold cross-validation</h3><p>Instead of fixing one split forever, <strong>cross-validation</strong> rotates the validation role through the data. In <strong>leave-one-out cross-validation</strong> (LOOCV), for each point i from 1 to n, you train the model on every point except i, predict point i, record the error, and finally average the n errors.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/696/1*tbF8AIqB7w3PPed9Wpd7Lg.png" /><figcaption>Figure 8: Leave-one-out cross-validation. In each of the n rounds a single observation (orange) is held out and the model is trained on all the others (blue).</figcaption></figure><p>Write ŷᵢ⁽⁻ⁱ⁾ for the prediction of point i from the model that never saw it. In regression the LOOCV estimate is the average squared error,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/856/1*IQMSAUr1iF5OF-_z0jixjQ.png" /></figure><p>and in classification it is the average 0–1 loss,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/857/1*ZEjAUJDDY8oRVAShD9IE1w.png" /></figure><p>LOOCV fixes two flaws of the validation set. It is <strong>not random</strong>: it always returns the same number. And it uses almost all the data for each fit, so it <strong>does not overestimate the test error</strong>; with n = 10,000, each model trains on 9,999 points.</p><p>It has two drawbacks of its own. The first is subtle. Averaging is supposed to reduce variance, but the n errors being averaged are <strong>not independent</strong>. The model trained on points 2 to n is almost identical to the one trained on point 1 and points 3 to n, so their errors are highly correlated, and averaging correlated quantities buys little. The second is blunt: LOOCV <strong>retrains the model n times</strong>. Some deep-learning models cost millions of dollars to train once, so that is out of the question.</p><p>Asked whether the gap between LOOCV and other estimates says something about generalisation, the lecturer said not as far as they knew. These are meta-methods for evaluating methods, the theory behind them is thin, and that is an open research problem. So there is no single answer about which to use. There are computational limits and there is practice.</p><p>The practical answer is the middle ground: <strong>k-fold cross-validation</strong>. Divide the data into k subsets, typically k = 5 or 10. For each fold, train on the other k − 1 folds and compute the error on the one left out, then average the k errors. Has anyone proved that 5 is the right number? The lecturer joked that we are born with five fingers on each hand and ten in total, and that this is not much of an exaggeration.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/626/1*egU0GeL3uyLsKrwf4XQMiQ.png" /><figcaption>Figure 9: k-fold cross-validation with five folds. The data are split into five blocks; each round holds out one block (orange) and trains on the other four (blue).</figcaption></figure><p>Every point is used for training at some stage, which the validation set never managed, and the cost is 5 or 10 fits instead of n.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/587/1*YNXJkc_UlvE-JCU81eMUtA.png" /><figcaption>Figure 10: Estimated MSE against polynomial degree on the Auto data. Left, LOOCV: a single deterministic curve. Right, 10-fold CV run with several different random splits: the curves almost lie on top of each other.</figcaption></figure><p>Compared with Figure 7, the 10-fold curves in Figure 10 barely move from one split to another. Side by side, the two methods compare like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HrJLviOtxk30CyRabvY3qA.png" /><figcaption>Table 1: LOOCV and k-fold cross-validation compared on the points made in the lecture.</figcaption></figure><p>k-fold trains on less data than is available, so like the validation set it overestimates the error, though less badly. In LOOCV the training sets are so alike that averaging n folds reduces variance less than you would hope, whereas k-fold training sets overlap but differ by a non-negligible amount. On every count k-fold sits between the two extremes, and it is what people use in practice.</p><h3>Choosing hyperparameters, and the one-standard-error rule</h3><p>Do cross-validation estimates pick the same hyperparameter the test error would? The textbook checks with three simulated regression problems.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/815/1*mXGradGi553dRUNnfHrh3g.png" /><figcaption>Figure 11: Three simulated regression problems, MSE against flexibility. True test MSE in blue, 10-fold CV in orange, LOOCV dashed in black; the crosses mark each curve’s minimum. The CV curves do not reproduce the test curve exactly, but their minima land close to the test minimum.</figcaption></figure><p>The curves in Figure 11 are not identical, but they are not far apart either. Since the point of validation is to choose a hyperparameter, here the flexibility, what matters is where each curve has its minimum. CV would pick roughly 6, 4 and 10 in the three panels; the cross on the blue curve shows what the test data would have picked, which you are not allowed to use. Not peeking costs something, but not much, and 10-fold and LOOCV make practically the same choices.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/845/1*iuBCeCvlog-7c4HqF1vk1Q.png" /><figcaption>Figure 12: Classification error rate against the order of the polynomial (left) and against 1/K for KNN (right). Training error in blue, test error in orange, 10-fold CV in black; crosses mark the minima. CV and test error do not pick exactly the same point, but they pick nearby ones.</figcaption></figure><p>Classification behaves the same way (Figure 12): CV may pick an order of three where the test error would pick two, and choosing the minimum of the CV curve is a natural rule. There is a catch, though. 10-fold CV is itself random, and its minimum might sit where it does purely by chance. The fix is a heuristic — much of this lecture is, the lecturer admitted — called the <strong>one-standard-error rule</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/419/1*NTRjGo3vaxma1I3hdI9V8g.png" /><figcaption>Figure 13: Forward selection, misclassification error against subset size p. The 10-fold CV error is in blue with standard-error bars; the true error is in orange. From p = 9 to p = 15 the CV estimates are practically level, well within each other’s bars.</figcaption></figure><p>With 10 folds you have ten error estimates, so besides their mean you can compute their standard deviation, which measures how much the error varies across folds; those are the bars in Figure 13. Taking the minimum would mean choosing p = 10 or so, but every model from 9 to 15 (honestly, up to 20) has essentially the same CV error, and one of them will come out lowest by luck. The rule is to find the minimum, agree that everything within one standard error of it is indistinguishable, and among those <strong>choose the simplest model</strong>. Here that is p = 9.</p><h3>The wrong way and the right way to cross-validate</h3><p>Cross-validation is a simple procedure, and that is exactly why it is easy to get wrong. The lecturer’s example comes from papers, published in journals such as <em>Nature</em>, that made this mistake.</p><p>You want to predict whether each of n = 50 people has cancer, using a nearest-neighbour classifier (one neighbour) and p = 5,000 gene-expression measurements. The proposal: using all the data, find the 100 genes most correlated with the outcome, then estimate the classifier’s error on those 100 genes with 10-fold CV. Is that reasonable?</p><p>Exaggerate the setup to see the problem. Suppose the outcome is decided by a coin flip and has nothing to do with genetics. All 5,000 features are then irrelevant, and any classifier built on them should be wrong about 50% of the time. The procedure above reports a 10-fold CV error of <strong>3%</strong>. Imagine publishing a cancer predictor with that accuracy. It has nothing to do with the model; it is a fundamental error in the evaluation.</p><p>The selected genes are the 100 most correlated with the <em>entire</em> outcome vector, test folds included. Out of thousands, they are the ones that by chance looked most related to y, so the relationship is spurious, but every fold has been handed features that are good for its own targets. Whatever the test fold is, it no longer resembles a new sample, because the features were chosen using exactly its labels. The decision about which features to use was made after seeing the outcomes you are trying to predict.</p><p>The right way moves the selection inside the loop. Split the data into 10 folds. For each fold, using only the other nine, select the 100 best genes <em>and</em> train the classifier on them, then compute the error on the held-out fold. Average the 10 errors. Done this way, the estimate goes back to about 50%.</p><p>This feels odd, because <strong>different folds will choose different features</strong>. A student asked how a single final model could come out of that. The answer is that cross-validation evaluates a <em>method</em>, not a fitted model. If your method is “regression with forward selection of 100 features”, each fold runs that method and produces an error, and the average is the CV estimate for the method. To deliver a model, you run the same method on all the data. Whether it ends up with the same features as any fold is irrelevant; what was validated is the procedure. Which procedure is good for choosing features is the topic of the next lecture.</p><blockquote><em>Any methodological choice learned from the data — such as which features to use — has to happen inside the cross-validation.</em></blockquote><h3>When the assumptions behind evaluation fail</h3><p>Even a correctly run cross-validation rests on assumptions, and errors in evaluation are the critical ones. The lecturer did not go deep, but listed the main ways real projects break them:</p><p>- <strong>Data leakage</strong>: information about the label sitting in the features. The wrong-way CV above is one case. In a famous X-ray model that detected broken bones with startling accuracy, the model had learned to read annotations on the films, not the images.</p><p>- <strong>Sample bias</strong>: training and test data come from different distributions. In election polling some groups are more reluctant to answer. In one famous US election, supporters of a controversial candidate avoided admitting it on the phone; the polls pointed to the other candidate, and the result was the opposite.</p><p>- <strong>Selection bias</strong>: the training data are not a random sample. Paediatric studies cannot randomly assign babies to risks; nobody will take pacifiers away from some babies and give them to others to see whether they cause harm.</p><p>- <strong>Non-stationarity</strong>: the distribution changes over time — seasons, relevant politicians. With global warming, predicting the future from past data alone underestimates the temperature.</p><p>How do you catch these when the data are too abstract to spot an annotation? There is no universal answer. The course may give the impression that most of the work is clever models, but in a real project about 80% of the time goes into looking at the data. In one project segmenting fetal MRI scans, the model worked beautifully until it suddenly failed on a few cases. Piercings near the scanned region distorted the images and were very rare, and triplets were another rare case. Analysing the data first and then studying where the model’s errors are largest almost always reveals the problem. The field of interpretability, which tries to understand how complex models make their predictions, helps here too.</p><p>A related question came up about very imbalanced outcomes, such as a rare cancer: careless folds could put every positive case in one fold, and the model would never see one. There are two remedies. One is to genuinely randomise the order and trust each fold to look like the whole. The other is to <strong>stratify</strong>, forcing every fold to contain the same share of positives.</p><h3>The bootstrap: uncertainty without formulas</h3><p>The second half of Chapter 5 turns from test error to <strong>uncertainty</strong>. Until now, finding the standard error of an estimator was done case by case. Take the sample variance:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/709/1*37xHpxTyn4BjhJrHqe_PvQ.png" /></figure><p>To get its standard error, the course assumed the data were Normal. Then a rescaled version of the estimator follows a chi-squared distribution with n − 1 degrees of freedom, and knowing the distribution answers every question: its variance, or the probability that it exceeds some threshold. But all of that rested on Normality. What if the data are not Normal, or the estimator is too complicated to have a known distribution, or has no closed form at all? An idea from the second half of the last century was to set the mathematics aside and use the computer instead; machines, the lecturer noted, have improved a little since the 1950s.</p><h4>Two assets</h4><p>The motivating example is from finance. X and Y are the returns of two assets.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/446/1*0Du4xaLfWmq0r70JZSNW7g.png" /><figcaption>Figure 14: Simulated returns X and Y of two assets. The two are positively related, with a lot of scatter.</figcaption></figure><p>You invest a fraction α in X and the rest in Y, for a return of αX + (1 − α)Y. You want a good return but also a safe one, so you choose α to minimise its variance. The variance is quadratic in α, and minimising it gives</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/603/1*IS6b4Ub5_e4WITmvIExkxw.png" /></figure><p>You do not know these variances and the covariance, so you “put a hat on everything” and plug in the usual estimates:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/595/1*mLwg8m-sUVY857h3QVt78Q.png" /></figure><p>Suppose your sample gives an estimate of 0.6. Can you trust it? With a different sample, would it move to 0.59, or to 0.3? With real money at stake, that is the question that matters. If you knew the joint distribution of X and Y, you could simulate many samples, compute the estimate on each, and look at how much it varies, which is what the textbook can do because its data are simulated.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/571/1*C5HgNZl2C1wQi3CfAdGlfg.png" /><figcaption>Figure 15: Four samples simulated from the same joint distribution of the two returns. They look broadly alike, yet each gives a different estimate of α.</figcaption></figure><p>In real life you have one sample and no distribution.</p><h4>Pulling yourself up by your bootstraps</h4><p>The bootstrap’s answer is to <strong>resample from the original sample and pretend that this is equivalent to drawing new samples</strong>. You have n points, so you draw n points from them <strong>with replacement</strong>. Some points repeat and others are left out. You compute the estimate on this new set, repeat the whole thing m times, and use the standard deviation of the m estimates as the standard error. In effect you simulate the uncertainty without ever knowing the estimator’s distribution.</p><p>The name, as the lecturer told it, comes from the stories of Baron Munchausen. Stuck in quicksand and about to drown, the baron pulled himself out by his own bootstraps. The method seems to do something similar, creating a sense of variability out of a single sample.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/584/1*hFQOSgTzyA0Y140GlxR76g.png" /><figcaption>Figure 16: The bootstrap mechanism with an original data set Z of three observations. Each bootstrap data set Z*1, Z*2, …, Z*B draws three rows with replacement — the first, for instance, contains observation 3 twice — and each one yields its own estimate of α.</figcaption></figure><p>Three observations are far too few in practice, but they fit on a slide (Figure 16). Each resampled table is treated as if it came from the same distribution as the original, each gives an estimate, and the spread of those estimates is the uncertainty.</p><h3>Why the bootstrap works, and when it doesn’t</h3><p>Does it work? In the textbook example, with m = 1,000 resamples (a usual number), the average of the bootstrap estimates is 0.5996, close to the true value of 0.6. The standard deviation of the estimates is 0.083, and that is the bootstrap standard error:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*8ZeI-jLgesDr_rFNTZyfZw.png" /></figure><p>That is close to the true value. More useful still, the <em>whole distribution</em> of the bootstrap estimates approximates the true sampling distribution of the estimator.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/955/1*o7g2iTfXOz2Jwi4SfhnZYQ.png" /><figcaption>Figure 17: Left, the distribution of α̂ over many samples drawn from the true population (orange). Centre, the distribution of 1,000 bootstrap estimates from a single sample (blue). Right, boxplots of both. The purple line marks the true α = 0.6. The two distributions have very similar shape and spread; the bootstrap one is centred slightly lower.</figcaption></figure><p>The secret is not only that the standard error comes out right. In many cases the resampled data sets are distributed much like the real data, and when the distributions are close, any statistic you extract from them is close too (Figure 17).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9b0TOegA7COGz0G5BZBSGg.png" /><figcaption>Figure 18: Left, the real world: a population P generates the data Z by random sampling, and an estimate f(Z) is computed from them. Right, the bootstrap world: an estimated population P̂ generates bootstrap data Z* in the same way, giving a bootstrap estimate f(Z*).</figcaption></figure><p>Figure 18 puts this more formally. In the real world, an unknown population generates your data and you compute an estimate, and not knowing that population has been the problem all course long. In the bootstrap world, the role of the population is played by the <strong>empirical distribution</strong> of your sample. Treat the sample as if it were the whole population, like a histogram: if the value 3 appeared 20 times and the value 4 appeared 100 times, draw according to those frequencies. Sampling n points from it independently is what produces the repetitions. If the estimated population resembles the real one, statistics computed in the bootstrap world resemble the real ones.</p><p>Are there guarantees? There are, but they belong to a statistics course. Even stating what kind of convergence is meant is not trivial, since something empirical is converging. In practice, the lecturer said, it almost always works, with an asterisk discussed below. As for how many resamples, the limit is computing power: 1,000 is common and 10,000 is better if you can afford it. The more the better.</p><p>The payoff is large. All the work of deriving t and F tests, and the tests for logistic regression the course never derived, existed to find the distribution of a statistic, and the bootstrap now provides that distribution by computation. That will matter more and more as models become too complex for formulas. The bootstrap can give prediction intervals too, though more modern methods exist for that. It was introduced alongside a close relative, the <strong>jackknife</strong>, named after the pocket knife because it is useful for so many things.</p><h4>The asterisk</h4><p>The bootstrap is a non-parametric way to get the distributional properties of an estimator — standard errors, confidence intervals and the like. It relies on the resamples behaving like genuine independent samples, as if you could keep asking the doctor for one more data set. That approximation usually holds, but it can fail:</p><p>- <strong>Small samples.</strong> With n = 3, as on the slide, the resamples cannot capture the variability.</p><p>- <strong>Dependent data.</strong> Resampling points independently destroys the dependence. In a series where the temperature is rising, drawing points at random scrambles the temporal structure, which is the most important thing in it.</p><p>- <strong>Skewed, heavy-tailed distributions or rare events.</strong> The bootstrap distribution may not reflect them. The convergence results behind the method need some regularity; in practice this is rarely a problem unless the distribution is truly strange.</p><p>- <strong>Discontinuous statistics</strong>, such as the sample minimum or maximum. The maximum of a resample rarely matches the maximum of the full data, so the bootstrap underestimates it. For means, standard deviations or the probability of exceeding a threshold, it generally works.</p><p>There are fixes, including the jackknife, the parametric bootstrap and the <strong>block bootstrap</strong> for dependent data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/535/1*7AiaEvZ4JeaLl4721_NCSQ.png" /><figcaption>Figure 19: Block bootstrap on a time series. Top, the observed series with five coloured blocks. Middle, the blocks drawn and pasted side by side. Bottom, the resulting bootstrap series, which keeps the short-range structure inside each block.</figcaption></figure><p>Instead of resampling points, the block bootstrap resamples whole blocks, hoping each block preserves part of the time dependence. In Figure 19 blocks 1 (green), 2 (blue), 3 (red), 4 (pink) and 5 (yellow) are cut and pasted whole. It is not perfect: the right block length depends on how long the dependence lasts, which nobody knows in advance, and smarter variants exist. For the independent data this course works with, the plain bootstrap is enough.</p><h3>Conclusion</h3><p>The lecture began by showing that logistic regression was never just a classification method. Choose a distribution for the response, write its log-likelihood, make the term multiplying y linear and minimise: the result is a generalised linear model. With the Poisson distribution, that recipe produces a convex, interpretable model for counts whose variance grows with its mean by design.</p><p>The rest of the lecture was about not fooling yourself. Training error proves nothing; a held-out test set is the only fair judge, and the gap between the two diagnoses under- and overfitting. Choosing models needs a third set, and a single validation split is noisy and pessimistic. Leave-one-out is deterministic but expensive and correlated, and k-fold cross-validation is the practical compromise, best combined with the one-standard-error rule. Every data-driven step, feature selection included, must happen inside the folds, otherwise a coin flip can look like a 3% error rate. Even then, leakage, biased samples and a changing world can undo an evaluation that looks correct. For uncertainty, the bootstrap replaces derivations with resampling: treat the sample as the population, recompute the estimator many times, and read the uncertainty off the spread, while remembering that it can fail for tiny samples, dependent data, extreme distributions and discontinuous statistics.</p><p>Both tools are computational and need few assumptions, which is why they will carry over to the far more complex models ahead. They are also how you protect yourself when a team builds models for you: a set nobody has seen is exactly how data competitions score their entrants. The lecturer recalled weather-forecasting contests where you predict a month ahead, impossible to game, and one where you had to “predict the past”, which was horrible for everyone because cheating was so easy. The next step is the question that has been hovering since feature selection came up: when there are many features, how do you choose which ones to use? With cross-validation in hand, the course is ready to answer it.</p><p><em>Thanks for reading! If you found this useful, consider following for more articles on machine learning and statistical modelling.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=295e29a71a51" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Classification Is Not More of the Same: Logistic Regression, Discriminant Analysis, and How to…]]></title>
            <link>https://medium.com/@matnogueira/classification-is-not-more-of-the-same-logistic-regression-discriminant-analysis-and-how-to-3b788ced731f?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/3b788ced731f</guid>
            <category><![CDATA[logistic-regression]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[classification]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Thu, 22 Oct 2026 22:57:50 GMT</pubDate>
            <atom:updated>2026-10-22T22:57:50.796Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>Classification Is Not More of the Same: Logistic Regression, Discriminant Analysis, and How to Judge a Classifier</strong></h3><blockquote><em>The loss classification wants can’t be optimised. This lecture rebuilds it around probabilities and asks how to judge a classifier.</em></blockquote><p><em>Every image and example was taken from the following sources:</em></p><ul><li><a href="https://www.youtube.com/watch?v=Bfm9w3NEiNU"><em>Lecture 4— Métodos Lineares para Classificação— Machine Learning 2026 Summer Course at IMPA</em></a></li><li><a href="https://w3.impa.br/~pauloo/teaching/ml/"><em>Machine Learning 2026, Prof. Paulo Orenstein</em></a></li></ul><h3><strong>Introduction</strong></h3><p>These notes cover Lecture 4 of the <strong>IMPA Summer Course — Machine Learning 2026</strong> (Paulo Orenstein), one article per lecture.</p><p>If you want extra context, the earlier articles in the series follow the course order:</p><p>1. <em>Foundations of Statistical Learning: From Rules to Data, and What Makes an Estimate Good</em></p><p>2. <em>Linear Regression: Least Squares, Inference, and How Far a “Simple” Model Stretches</em></p><p>3. <em>When Linear Regression Breaks: Five Ways the Assumptions Fail, and How to Compute β̂ Without Inverting a Matrix</em></p><p>Everything below is <strong>self-contained</strong>: you do not need to read the other posts to follow this one, as long as you are comfortable with least squares, logarithms and exponentials, gradients, and a multivariate Normal density.</p><p>The first week of the course was about regression. This lecture, which follows Chapter 4 of the course textbook, turns to classification and argues that it is not “more of the same.” The natural loss cannot be optimised directly, and bolting linear regression onto 0/1 labels goes wrong. That leads to modelling the probability of each class instead, either directly, with logistic regression and its multi-class version (the <strong>discriminative</strong> methods), or through Bayes’ rule, with LDA, QDA and naive Bayes (the <strong>generative</strong> methods). We then compare them all and finish with how to evaluate a classifier when a single error rate hides most of the story. Generalised linear models, also on the lecture’s agenda, were deferred to the next class.</p><h3><strong>Why classification is not “more of the same”</strong></h3><p>In classification we predict a <strong>qualitative</strong> variable: a class that may have no natural order at all, like eye colour taking the values brown, blue or green. Code them as 0, 1 and 2, and the job looks familiar: find a function f̂ that takes features X and returns one of those three codes.</p><p>Returning a bare label throws information away, though: it says nothing about how confident the method is. “Your eyes are blue, and I am 90% sure” and “your eyes are blue, and I am 55% sure” are very different statements. So we usually predict a <strong>probability</strong> of belonging to each class — a real number between 0 and 1, smoother and more informative than a label. There is a second reason, which turns out to matter more: a discrete function is miserable to optimise, and probabilities smooth the problem out.</p><p>Figure 1 shows the idea on the textbook’s credit-card data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/435/1*IcfYDeLoIxwC6P8HV_lzGA.png" /><figcaption><em>Figure 1: Each orange tick is a person, placed at 1 if they defaulted on their debt and at 0 if they paid, against their credit-card balance on the horizontal axis. The blue curve models the probability of default: essentially zero for balances up to about 1000, rising steeply after that, and close to 1 by 2500. It is not a label but a smoothly varying probability.</em></figcaption></figure><p>The setup keeps the three decisions of every method in the course — a class of functions, a loss and an optimiser — but the predictive function gains a two-level structure:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*neQpQ05nMoQuuc6ijYeZIA.png" /></figure><p>What we really model is p̂(x), the estimated probability of class 1, and the prediction is the <strong>most probable class</strong>. We would like p̂ to be as close to linear as possible, but it cannot be exactly linear: probabilities cannot be negative or exceed 1, and they must sum to 1. A function h, still to be chosen, will enforce that.</p><p>The natural loss is the <strong>0–1 loss</strong>: pay 0 for the right class and 1 for the wrong one. The obvious plan is to find the coefficients that minimise its average over the sample, the way we did with squared error. It does not work. Figure 2 shows why.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/807/1*yKl24-0FqAcZaNfBnsgFyA.png" /><figcaption><em>Figure 2: The 0–1 loss as a function of the predicted probability. Left, when the true class is 0: no loss while the prediction stays below 0.5, a loss of 1 above it. Right, when the true class is 1: the mirror image. Each panel is a step: flat everywhere except for a jump at 0.5.</em></figcaption></figure><p>The loss is neither convex nor smooth, and its derivative with respect to p̂(x) is zero almost everywhere. By the chain rule, the gradient with respect to any coefficient is that zero times something, so it vanishes almost everywhere no matter how p̂ is parametrised. Gradient descent has nowhere to go, and the comfortable world of linear regression — convex, unique solution, closed form — is gone.</p><p>The way out is to rethink the loss, using the interplay between function class, optimiser and loss that the course keeps returning to. We train on a <strong>surrogate loss</strong>: something close to what we want but mathematically better behaved.</p><p><strong>The obvious surrogate, and why it fails</strong></p><p>The first candidate is the only loss we know so far, the squared loss. Run a linear regression with Y ∈ {0, 1} and use the fitted values as p̂. It is even aligned with the goal, because for a 0/1 variable the conditional expectation is exactly the probability of class 1:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7xJqAqOS8yyUbdmqRIacTQ.png" /></figure><p>Then threshold at 0.5. The lecture lists three problems with this. There is no obvious way to extend it to more categories, such as {stroke, cancer, diabetes}. The fitted values need not lie in [0, 1], so they are not probabilities: one negative coefficient and a large positive feature can produce a “probability” of −8. And the loss punishes the wrong things. If the true class is 1 and the model says 3 — so confident that it overshot the ceiling — it pays (3 − 1)² = 4. Had it predicted 0, which is much worse in any sensible sense, it would have paid only 1. Confident correct answers get over-penalised.</p><p>The underlying issue is that we are forcing a method built for real-valued y onto a problem that is intrinsically different. We need a different loss and, with it, a different predictive function.</p><h3><strong>From a coin flip to the logistic loss</strong></h3><p>The lecture’s proposal is to think statistically about what Y is. A variable that only takes the values 0 and 1 has essentially one possible distribution, the <strong>Bernoulli</strong>: a coin flip that lands on 1 with probability p(x) and on 0 with probability 1 − p(x). So we write</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vdtzUyICYnWGaBQHS0lbHA.png" /></figure><p>There is not much choice here: if Y is binary, it is Bernoulli. What we get to model is the function p(x).</p><p>This suggests a loss. <strong>Maximum likelihood</strong> picks the p̂ that makes the observed sample as probable as possible. The sample is the only thing we know actually happened, so we make it as unsurprising as we can. The probability of observing a particular label y can be written compactly as</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*secGtUXbl1pSg0J9whr43A.png" /></figure><p>which is just a tidy way of saying “p(x) if y = 1, and 1 − p(x) if y = 0”, since one of the two exponents is always zero. We minimise throughout the course, so we take the negative. We also take the logarithm, which is monotone and so costs nothing. The result is the loss</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KPUc00TUU-_ZGqKWXTexoQ.png" /></figure><p>the <strong>logistic loss</strong>. It is a smooth substitute for the 0–1 loss, and because it is smooth we can optimise it. It is also more informative. It distinguishes being wrong with conviction from being wrong while unsure, which the 0–1 loss, recording only hit or miss, cannot do. Figure 3 makes this concrete.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uLgHwbh96aDU2BXDrpW8rg.png" /><figcaption><em>Figure 3: The 0–1 loss (black step) and the logistic loss (cyan curve) against the predicted probability, for true class 0 (left) and 1 (right). The logistic loss shadows the step but is smooth. It is near zero when the prediction is right and confident, still positive when it is right but hesitant, and it shoots up as the prediction becomes confidently wrong.</em></figcaption></figure><p>The small price paid when right but hesitant is, in the lecturer’s words, the problem telling you there is still something to learn. This is the first big break from linear regression, which kept its natural loss to the end; here a discrete loss is deliberately traded for a smooth one.</p><h4><strong>Making the log-odds linear</strong></h4><p>Averaged over the sample, and with the terms regrouped so that what multiplies yᵢ stands apart, the objective is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CdIOQ20qY5KdBUU6O-Nebg.png" /></figure><p>Minimising over every possible function is hopeless, so, as in linear regression, we impose linear structure. We do not make p itself linear, because that brings back negative probabilities. We make linear <strong>the term that multiplies y</strong>, the logarithm of the odds:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/998/1*ihex2mwHE23HgHMBzX58lQ.png" /></figure><p>The lecturer was candid that the deep reasons for this choice involve exponential families and natural parameters, material for a full statistics course. One consequence can be checked by hand, though. Write s for the linear predictor, exponentiate both sides, and collect the p̂ terms:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fke6opi9KvuiiyDvoniuig.png" /></figure><p>Therefore</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/633/1*Q3S9gmJ7Je1wLmnz4XJxbQ.png" /></figure><p>This is the <strong>logistic function</strong> applied to the linear predictor, and it is exactly what a probability should be: positive, below 1 because the denominator exceeds the numerator, and summing to 1 with the probability of class 0, which is one over the same denominator. If you would rather skip the statistical mysticism, read it as a <strong>normalisation</strong> that squeezes a linear score into [0, 1].</p><p>Substituting this form back into the loss turns a search over functions into a search over coefficients:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l6biZORqrHekhahwON0RAw.png" /></figure><p>Here βᵀxᵢ is shorthand for β₀ + β₁ xᵢ₁ + ⋯ + βₚ xᵢₚ. And then a gift arrives: this problem is <strong>convex</strong>. We never asked for convexity; it was earned the moment we chose to model linearly the term that multiplies y, and it will reappear every time that recipe is followed. Modelling another parameter, such as the mean, would instead impose constraints like keeping p̂ in [0, 1] and could make the optimisation non-convex.</p><blockquote><em>Accuracy is the loss we want but cannot optimise; the logistic loss is the smooth version we can.</em></blockquote><h3><strong>Fitting it: convexity, gradient descent, and Newton in disguise</strong></h3><p>With a convex objective, we are back near linear-regression territory, minus the closed form. Here is the loss again:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vLu-kKEk3VgUlDSs5iqU8A.png" /></figure><p>Gradient descent only needs the first derivative. The second derivative is what proves convexity. If vector derivatives feel unfamiliar, differentiate with respect to each βⱼ separately and stack the results; it comes to the same thing.</p><p>The first term differentiates to yᵢ xᵢ. For the second, the chain rule gives one over one-plus-exponential, times the exponential, times xᵢ, and that ratio of exponentials is precisely p̂(xᵢ). So the gradient is a sum of residuals weighted by the features:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EmmMcEJ37JgzBgOX7jBzjQ.png" /></figure><p>Here X is the feature matrix, y the vector of labels and p̂ the vector of fitted probabilities. Differentiating once more, only p̂(xᵢ) depends on β, and the derivative of the logistic function turns out to be p̂(1 − p̂). The Hessian is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mYtLqFsIIMIWiS9mS_TOLA.png" /></figure><p>where W is the diagonal matrix whose entries are p̂(xᵢ)(1 − p̂(xᵢ)). Those entries are positive, so the Hessian is positive semidefinite, and the loss is convex in β, as promised.</p><p><strong>Gradient descent</strong>, a first-order method, starts anywhere and repeatedly steps against the gradient with a step size ηₜ:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*a1h2uqfG6hR6MZ0onkWtVQ.png" /></figure><p>Convexity guarantees it converges. <strong>Newton’s method</strong>, a second-order method, also uses curvature: it steps by the inverse Hessian times the gradient. Plugging in the two expressions above, the factors of 1/n cancel:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gmf-WaQ2S0fG4qAWHUouPQ.png" /></figure><p>Now comes the part the lecturer clearly enjoyed. Write the current β̂ₜ as (XᵀWX)⁻¹XᵀWXβ̂ₜ, which changes nothing. Also slip W W⁻¹ in front of the residual, which is allowed because W is diagonal with positive entries. Both terms then share the factor (XᵀWX)⁻¹XᵀW:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tfGZvvwzMkIOCeWMVvbroQ.png" /></figure><p>If W were the identity, this would read (XᵀX)⁻¹Xᵀzₜ: a plain linear regression of zₜ on X. With a diagonal W it is a linear regression <strong>with weights</strong>, the solution of a weighted least-squares problem:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CY5F7SKMjQPyrXq2BaW9lQ.png" /></figure><p>The i-th observation effectively enters with a weight equal to the square root of p̂ᵢ (1 − p̂ᵢ). This is <strong>iteratively reweighted least squares</strong> (IRLS). Buried inside a classification problem, every Newton step is a least-squares fit, which we know how to solve efficiently without inverting a matrix — with the QR or SVD decompositions, or with gradient descent. Fitting a logistic regression with Newton’s method is walking through parameter space solving one linear regression per step.</p><blockquote><em>Newton’s method for logistic regression is a sequence of weighted linear regressions.</em></blockquote><p>The price relative to linear regression is that the closed form (XᵀX)⁻¹Xᵀy is gone, and with it the exact distribution of β̂. All we have is an iterative method, run until it converges.</p><p><strong>Reading the coefficients, and why “student” flips sign</strong></p><p>Run logistic regression on the credit-card data in Python or R, modelling default as a Bernoulli whose probability is the logistic function of an intercept plus a coefficient times balance, and you get a familiar-looking table.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cUkihSKNyb9kfaMzfhvGiA.png" /><figcaption><em>Table 1: Logistic regression of default on balance alone. The coefficients come from running gradient descent or Newton’s method to convergence.</em></figcaption></figure><p>The standard errors and p-values come from <strong>asymptotic tests</strong>, not derived in class: they generalise the linear-regression calculations and again trace back to modelling linearly what multiplies y. The catch is the word <em>asymptotic</em>: the test is exact only as n goes to infinity, where the estimator’s distribution becomes Gaussian. It is an approximation, but one scientists use routinely, especially with large samples.</p><p>To read the table, compute probabilities. At a balance of 1,000 dollars, plugging the coefficients into the logistic function gives</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/918/1*AZo7u2a137eEteXN2XxJvQ.png" /></figure><p>and the same calculation at 2,000 dollars gives 0.586. Doubling the balance takes the probability of default from under 1% to over 50%.</p><p>Now fit a different model, with a single 0/1 feature: whether the person is a student.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hOBeUAFoU8UXtJSnsEqOnA.png" /><figcaption><em>Table 2: Logistic regression of default on student status alone. The positive coefficient says students are more likely to default.</em></figcaption></figure><p>A student gets a probability of default of 0.0431 and a non-student 0.0292 — “slightly sad news,” the lecturer told a room full of students. But these are two separate regressions. Using balance and student status together needs a multiple regression, fitted exactly as before by maximum likelihood.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6BhVWlVCG-AX87qhmkVwsg.png" /><figcaption><em>Table 3: Logistic regression on balance, income and student status together. The student coefficient has flipped from positive to negative.</em></figcaption></figure><p>With balance and income in the model, the student coefficient is <strong>negative</strong>. A bank that already knows your balance and income should be <em>more</em> willing to lend once it learns you are a student. The explanation is a <strong>confounding variable</strong>, shown in Figure 4.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/757/1*f8eG1unkO1qtp2dpDqGUjw.png" /><figcaption><em>Figure 4: Left, default rate against credit-card balance, for students (orange) and non-students (blue). At every balance the orange curve sits below the blue one. The dashed horizontal lines, read in class as each group’s overall default rate, are the other way round, with students higher. Right, the distribution of balance by student status: the students’ box sits clearly higher.</em></figcaption></figure><p>Three facts combine here. Students carry larger balances: they are starting out, earn little and pay for their education, “in particular in the United States.” People with large balances default more. Yet among people with the same balance, students default less — of all the people who owe money, in the lecturer’s words, students are the best: young, starting to earn, investing in an education that will pay off.</p><p>Alone, being a student goes with owing money, and owing money goes with default, hence the positive coefficient. With balance in the model, the coefficient is read <strong>conditionally on the other features</strong>: at a fixed balance of, say, 1,000 dollars, you would rather lend to a student. The confusion comes from the correlation between the features.</p><p>The lecture closed this part with the method’s weak spots:</p><p>- <strong>Collinearity</strong> inflates the variance of the coefficients, as in linear regression; with perfect collinearity there is no solution.</p><p>- <strong>Perfect separation</strong> is a new problem. If the classes can be split perfectly, infinitely many curves reach zero training error, the slope wants to go to infinity, and gradient descent keeps stepping without ever converging. This is always the case when the number of features is at least the number of samples.</p><p>- <strong>Interpretation</strong> is harder than in linear regression. A unit increase in a feature no longer adds a fixed amount; its effect on p̂ passes through an exponential over one plus an exponential.</p><p>- <strong>Inflexibility</strong>: for some problems, such as classifying handwritten digits (MNIST, later in the course), the model hits a performance ceiling.</p><h3><strong>More than two classes: multinomial regression</strong></h3><p>What if there are more than two classes, say emergencies that are heart attacks, overdoses or epileptic seizures? This was one of the failures of the regression shortcut, and here the statistical framing pays off: we just need a distribution with support on K classes. That is the <strong>Multinomial</strong> with a single draw, in place of the Bernoulli, with one probability per class, all summing to 1:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_TvwF8SGEfZ76WuMGnqOAQ.png" /></figure><p>Only the factor of the observed class survives. Once the first K − 1 probabilities are known, the last is one minus the rest, so we single out class K and model linearly the log of each class’s probability relative to it:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iTYkr_90y4f3_5JDvoCgBg.png" /></figure><p>Each of the K − 1 classes gets its own p + 1 coefficients, for (p + 1)(K − 1) parameters in total. This is exactly what logistic regression did with two classes: there, class 0 was “one minus class 1”, and that is where the “1 −” in the log-odds was hiding. Solving for the probabilities in the same way gives</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/824/1*H7xZtMaPOhRFGujGkMwKxw.png" /></figure><p>for k = 1, …, K − 1, while the last class gets 1 over the same denominator. Class k uses its own coefficients, and the denominator sums over all classes. The probabilities lie in [0, 1] and add up to 1. The negative log-likelihood has the same shape as before — a linear part plus the log of one plus a sum of exponentials — the parameters come from maximum likelihood with gradient descent or Newton, and the problem is still convex.</p><p>Where the regression shortcut had no answer for many classes, the generalisation here came for free: put a sensible distribution on Y and let it suggest the functional form. The lecturer called logistic regression perhaps one of the most important methods in machine learning, one the course will keep extending, all the way to neural networks.</p><p><strong>A different route: generative models and LDA</strong></p><p>Everything so far estimated ℙ[Y ∣ X] directly, by assuming a functional form for it. The second half of the lecture takes a route with a very different structure. Estimate two other things instead:</p><p>1. ℙ[X ∣ Y]: given the class, how are the features distributed?</p><p>2. ℙ[Y]: how likely is each class before seeing any features?</p><p>Then combine them with <strong>Bayes’ rule</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*obtLIip2g6LHGGLPqV6k8w.png" /></figure><p>The first ingredient is the strange one. Until now the features were fixed and the randomness lived in Y. Purely for modelling purposes, we flip that and treat X as random given the class. Take predicting whether a patient develops diabetes next year from their insulin level and how much they exercise. The usual question is: given this insulin level and no exercise, what is the probability of diabetes? The flipped question is: given that this person <em>will</em> develop diabetes, what is their insulin level likely to be today, and do they exercise?</p><p>The denominator plays no role in the decision. We want the most probable class for a given x, and the denominator is the same for every class. Because these methods model how the features are distributed within each class — enough, in principle, to <em>generate</em> features — they are called <strong>generative</strong>. Methods that model ℙ[Y ∣ X] directly, like logistic regression, are <strong>discriminative</strong>.</p><p><strong>Linear discriminant analysis</strong></p><p>The first generative method, <strong>linear discriminant analysis</strong> (LDA), makes two assumptions. The features within each class follow a multivariate Normal distribution, and the probability of each class, πₖ, is estimated by its share of the training data. If 90% of a representative sample is not diabetic, then with no features at all the best guess for a random person is a 10% chance of diabetes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/562/1*4czEPvmZGCG6_nG5Zzx-Ww.png" /><figcaption><em>Figure 5: A simulated problem with three classes (green, blue, yellow) and two features. Left, contours of the three Gaussians that generate the data. They are the same ellipse shifted to three places, because the covariance is shared, and the dashed lines are the Bayes decision boundaries. Right, a sample from each class, with the boundaries LDA estimates from it (solid) next to the Bayes boundaries (dashed). Every boundary is a straight line.</em></figcaption></figure><p>A student asked whether anything was being assumed about the spread of the Gaussians. Something is, and it is the heart of the method: <strong>all classes share the same covariance matrix</strong> Σ. The ellipses in Figure 5 have the same shape and differ only in where they sit. The class therefore enters the density only through its mean μₖ:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sRuTF-yIuGmh6g3M1Mgr_g.png" /></figure><p>By Bayes’ rule, the probability of class k given x is a constant times πₖ fₖ(x). Now absorb into the constant everything that does not depend on k: the power of 2π in front, the determinant of the shared Σ, and, after taking logs and expanding the quadratic form, the term in xᵀΣ⁻¹ x. What remains is the <strong>discriminant function</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*POe7vEARxQRXDKUnZK8kSw.png" /></figure><p>Predict the class with the largest δₖ(x). Another student asked how terms that depend on x can be thrown away. The answer is that for a given x we only compare classes, so a term added equally to every class cannot change the winner. If two discriminants are 1.2 and 1.5, adding 10 to both still picks the second.</p><p>Where does “linear” come from? Look at the points where you are indifferent between classes k and l, that is, where δₖ(x) = δₗ(x). On both sides x appears only multiplied by a fixed vector, so the set of such points is a hyperplane. The indecision between green and yellow in Figure 5 is exactly a straight line. Gaussian classes with a shared covariance produce <strong>linear decision boundaries</strong>.</p><p>To use it we estimate three things from the sample:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FjdQyRD1Bz6kYitv_Kjrog.png" /></figure><p>These are the class’s share of the data and its centre of mass. The covariance is the unbiased estimator, <strong>pooled</strong> across classes because they share it, with each point centred on its own class mean:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PbI8y90pw_i4BWKivlAiDw.png" /></figure><p>Then compute every class’s discriminant at x with these estimates plugged in and pick the largest. Both the modelling and the reasoning are a world away from logistic regression.</p><h4><strong>When to prefer LDA</strong></h4><p>The lecture gives two situations. First, <strong>when the classes are well separated</strong>, LDA is more stable than logistic regression. Perfect separation is exactly what breaks logistic regression, whereas for LDA it is the easy case: put one Gaussian in each corner. Second, <strong>with a small sample and features that are roughly Normal within each class</strong>, LDA does better.</p><p>The second point reflects a philosophy that runs through the course: strong parametric assumptions beat assumption-free methods when they are reasonable and lose when they are wrong. LDA needs a strong one, because it models a p-dimensional X given Y — far more to estimate than logistic regression’s one-dimensional Y given X — and Gaussianity with a shared covariance keeps that manageable. Asked whether the assumptions could be tested afterwards, the lecturer gave a machine-learning answer: compare the model with others and judge by its predictions.</p><h3><strong>Loosening LDA: QDA and naive Bayes</strong></h3><p>LDA’s recipe is “class prior times class density, normalised,” with Normal densities sharing Σ. Changing the shape of the densities generalises it in two directions.</p><p><strong>Quadratic discriminant analysis</strong> (QDA) gives each class its own covariance Σₖ. That is the only change, but it matters. The quadratic term in x and the log-determinant can no longer be absorbed into a constant, because both now depend on the class:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ONTdWUJpiypFILd7DTxDRQ.png" /></figure><p>The boundaries become <strong>quadratic</strong>. Figure 6 shows what that buys and what it costs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/662/1*_YxMrcblupqLqId_VNwBTg.png" /><figcaption><em>Figure 6: Two two-class problems, with the Bayes boundary (purple dashed), LDA (black dotted) and QDA (green solid). Left, the true boundary is linear and LDA lies almost on top of it, while QDA bends away. Right, the true boundary is curved; QDA follows it closely and LDA’s straight line cannot.</em></figcaption></figure><p>QDA gains flexibility at the price of many more parameters. A student asked why, in the left panel, QDA does not simply estimate near-identical covariances and recover the line. The lecturer compared it to polynomial regression: a quadratic fit <em>could</em> produce a straight line, but given the extra flexibility it will use it to chase the data, which is overfitting to some degree.</p><p><strong>Naive Bayes</strong> takes a different shortcut. The real burden of discriminant analysis is a distribution over a p-dimensional vector: few are tractable (the Gaussian is one, hence its use), and you must estimate every correlation between features — insulin with glucose, exercise with age — a number of parameters quadratic in p, per class for QDA. Naive Bayes assumes the features are <strong>independent within each class</strong>, so the joint density factorises into one density per feature:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0qe49Y5ybYV7irpiXvieUA.png" /></figure><p>That is useful when p is large. With Normal marginals, independence means a diagonal covariance, and the discriminant becomes a sum of one term per feature:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KrkdMW62q2SeCoUcwJcdPw.png" /></figure><p>up to a constant. But the Normal case is where you least need naive Bayes, since LDA and QDA exist. Its real strength is that <strong>each marginal can be anything</strong>. Fitting a Gaussian to a 0/1 variable like “exercises or not” makes no sense; naive Bayes can use a Bernoulli for it and Gaussians for the rest, or estimate a marginal with a histogram. That suits dummy variables and text, where predicting the next character is inherently discrete. The independence assumption is strong, but in practice it usually works — and, asked how to test it, the lecturer again answered: use a test set and see whether the predictions are good.</p><h3><strong>Comparing the methods: four ways to write the log-odds</strong></h3><p>We now have four classifiers built on quite different ideas. The lecture compares them with one rewrite. Choosing the class that maximises ℙ[Y = k ∣ X = x] is the same as maximising its log-ratio against a reference class K, so look at what each method assumes that log-odds looks like.</p><p>For <strong>logistic regression</strong> it is linear, by construction:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hlq2pS9wgTshcHVyJo9aHw.png" /></figure><p>For <strong>LDA</strong>, the ratio is class k’s prior times its Gaussian, over the same product for class K, with the same Σ in both:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zg8U8SwaPBme9UQgZUj5Vg.png" /></figure><p>A ratio of exponentials is the exponential of the difference. The term xᵀΣ⁻¹ x appears in both the numerator and the denominator and cancels, leaving only a part that is linear in x. So LDA’s log-odds is <strong>also linear</strong>. Logistic regression and LDA have the same flexibility — what one can represent, the other can too — and differ only in how they fit the parameters to data. Nothing in the derivations so far made that obvious.</p><p>For <strong>QDA</strong> the same calculation leaves the quadratic terms standing, because the covariances of class k and class K differ:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JkFiVXiNKb6sYM2Ywbr1bA.png" /></figure><p>For <strong>naive Bayes</strong>, the log of a product becomes a sum, one function per feature:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*6iAuXpdnJi3dqp1UY2gh9Q.png" /></figure><p>Each gₖⱼ depends on which marginals were chosen, so it can be linear, quadratic or something else. But the features act on the log-odds <strong>independently</strong>: there is no product of xⱼ with xⱼ₊₁, so the features never “talk to each other,” whereas QDA’s quadratic terms let them.</p><p>Put together, these four forms explain how the methods are related:</p><p>- <strong>Logistic regression is not generative.</strong> It never models X given Y.</p><p>- <strong>LDA is a special case of QDA</strong> (QDA could, improbably, estimate the same covariance for every class), <strong>but it can perform better.</strong> If the classes really do share a covariance, the lecturer would bet on LDA. QDA spends part of its sample estimating flexibility it does not need, which is the intuition for why a more flexible method can lose to a more rigid one.</p><p>- <strong>Every classifier with linear boundaries is a naive Bayes,</strong> LDA included: choose each gₖⱼ linear and the naive Bayes form reproduces the linear one. With Normal marginals that share a variance per feature across classes, naive Bayes <em>is</em> LDA with a diagonal covariance, since then the two make the same assumptions.</p><p>- <strong>QDA and naive Bayes are not special cases of each other.</strong> QDA has interactions; naive Bayes has arbitrary per-feature functions.</p><blockquote><em>The methods make different assumptions, and none of them is uniformly better.</em></blockquote><h4><strong>What the experiments say</strong></h4><p>The textbook makes this concrete with six simulated scenarios, shown in Figure 7.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/585/1*CG53n0xFwhIGnzXSg19Qbw.png" /><figcaption><em>Figure 7: Test error of six classifiers — KNN with one neighbour, KNN with the neighbourhood chosen by cross-validation, LDA, logistic regression, naive Bayes and QDA — in six different scenarios. The winner changes from panel to panel; in Scenario 4, for instance, QDA is clearly best, while in Scenario 3 naive Bayes is clearly worst.</em></figcaption></figure><p>The lecture did not go through the scenarios one by one. The point is that when you change how the data are generated so that they match one method’s assumptions, that method wins. There is no universally best classifier; there are assumptions, and a method is good when its assumptions are.</p><p>A student objected that with enough data the most flexible method should win. The answer was a qualified yes. An unnecessarily flexible method spends statistical information on parameters it did not need, so it loses to a simpler method with the right assumptions — but as the sample grows, that gap shrinks. That, the lecturer said, is one of the great revolutions of recent years: with datasets the size of the internet, flexible methods probably beat very parametric ones, which is why neural networks with billions of parameters sit behind modern AI, against the tens or hundreds in the textbook’s examples. In this lecture’s problems, the right parametric assumptions still pay off — a fundamentally empirical intuition, the lecturer added, since for neural networks there is very little theory.</p><p>The moral is a warning against downloading several models, keeping the best score and shipping it. Understand what each method assumes, look at your data, and match the model to it.</p><h3><strong>Judging a classifier: confusion, thresholds, and the ROC curve</strong></h3><p>Every method above was trained on a surrogate — the logistic loss, or a Gaussian likelihood — because the loss we care about could not be optimised. To <em>evaluate</em>, we go back to that loss, the 0–1 error rate: the fraction of observations whose predicted class differs from the true one.</p><p>It describes what we want, but it is not very informative when the errors are unbalanced between the classes. Take a real IMPA problem: detecting deforestation by comparing this month’s sub-images of Brazil’s forests with last month’s. Nearly every sub-image shows no clearing, so a model that always answers “no deforestation” is right ninety-something percent of the time — excellent on paper, and useless. Likewise, almost everybody repays their loans, so a bank model that lends to everyone scores nearly as well as one that took months to build, and the 0–1 loss cannot tell them apart.</p><p>A better description is the <strong>confusion matrix</strong>, which separates the two ways to be right from the two ways to be wrong.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iLSjkFbVA6-mYAwlg9T6zg.png" /><figcaption><em>Table 4: The confusion matrix. The diagonal holds the correct predictions; the off-diagonal cells are the two kinds of error, which the 0–1 loss counts as equally bad.</em></figcaption></figure><p>The 0–1 loss weighs a false positive and a false negative the same, and in real problems we often care much more about one than the other.</p><h4><strong>An example with LDA</strong></h4><p>Use LDA to predict default for the 10,000 people in the textbook’s data, predicting “yes” when the estimated probability of default exceeds 0.5. There are 333 defaulters (positives) and 9,667 non-defaulters (negatives). The training error is low: 23 false positives plus 252 false negatives, out of 10,000, is 2.75%. But a model that always says “no” errs only on the 333 defaulters, about 3.33%, so a model that ignores the features is almost as good. The source of the error is the <strong>false-negative rate</strong>: of the 333 people who defaulted, the model missed 252, about 76%. It largely fails to recognise the case a bank cares about most.</p><p>The cheap fix is to <strong>lower the threshold</strong> — call someone a likely defaulter when the estimated probability exceeds 0.2 instead of 0.5. This happens <em>without touching the model</em>. The probabilities p̂ are already estimated; only the rule that turns them into a class changes.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wVIm8mUeWFfG4uJNvDRwfA.png" /><figcaption><em>Table 5: LDA on the default data at two thresholds. Lowering the threshold to 0.2 moves people into the “predicted yes” row: positive predictions grow from 104 to 430.</em></figcaption></figure><p>The false-negative rate drops from about 76% to 138 out of 333, about 41%. The price is paid elsewhere: the false-positive rate rises from 23 out of 9,667 (about 0.2%) to 235 out of 9,667 (about 2.4%). The overall error rate, counting the off-diagonal cells, rises to 235 plus 138 out of 10,000, about 3.7%. That is expected, since the most-probable-class rule at 0.5 is what minimises the 0–1 loss. Forcing more positive predictions trades errors on one side for errors on the other. Whether that trade is worth making depends on the problem. A court, for example, operates under a presumption of innocence: our society decided that jailing an innocent person is much worse than freeing a guilty one, so the two errors are deliberately weighed differently.</p><h4><strong>The whole range of thresholds</strong></h4><p>Figure 8 sweeps the threshold from 0 to 0.5.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/729/1*nhk8Iffnz9I86ortYhObZw.png" /><figcaption><em>Figure 8: Error rates of the default classifier as the threshold goes from 0 to 0.5: the false-negative rate (blue dashed) climbs from 0 to about 0.75, the false-positive rate (orange dotted) falls from about 0.7 towards zero, and the overall 0–1 error (black solid) drops quickly and then flattens near the bottom.</em></figcaption></figure><p>At a threshold of 0, essentially everyone has a probability above it and is predicted positive, so there are no false negatives at all. As the threshold rises, the model becomes less eager to say “positive”: false negatives climb and false positives fall. The overall 0–1 error combines the two.</p><p>The <strong>ROC curve</strong> packs every threshold into one picture. It plots the true-positive rate against the false-positive rate as the threshold moves (ROC stands for <em>receiver operating characteristic</em>, a name from engineering that, as the lecturer noted, has nothing to do with anything here).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/326/1*zprCpyrCmIAs8PPh0MFG9A.png" /><figcaption><em>Figure 9: An ROC curve, with the true-positive rate on the vertical axis and the false-positive rate on the horizontal one, both running from 0 to 1. The curve rises steeply near the left edge and hugs the top of the square, far above the dotted diagonal.</em></figcaption></figure><p>At threshold 0 everything is predicted positive, so both rates are 1 and the curve starts at the top-right corner. At threshold 1 nothing is predicted positive, both rates are 0, and it ends at the bottom-left. In between, the closer the curve hugs the <strong>top-left corner</strong>, the better. That corner means catching every positive without a single false positive: perfect prediction.</p><p>Until now every evaluation needed a threshold. But if someone hands you a classifier and asks which threshold to use, there is no good answer without knowing how they weigh false positives against false negatives — and whoever builds the method usually does not know. The <strong>area under the curve</strong> (AUC) sidesteps the question by aggregating over all thresholds. A perfect classifier fills the unit square and scores 1; the further a curve sags from the corner, the smaller the area. The closer to 1, the better, whatever threshold is eventually chosen.</p><p>Choosing that threshold is not a data problem; it comes from the application and the people who own it. A doctor looking at a slightly suspicious exam would rather order more tests. A deforestation alert that dispatches inspectors should not cry wolf. Rio’s SMS rain alerts face both risks: warn too often without rain and people stop believing, fail to warn before a storm and the result can be a catastrophe. The nature of the problem, not the data, should set the threshold.</p><h3><strong>Conclusion</strong></h3><p>The lecture’s case against “more of the same” was made in steps. The 0–1 loss has zero gradient almost everywhere, and the squared loss on 0/1 labels produces non-probabilities and punishes confident correct answers. Modelling Y as a Bernoulli replaces both with the logistic loss; making the log-odds linear yields valid probabilities and a convex problem, which Newton’s method solves as a sequence of weighted least-squares fits. Coefficients must be read conditionally, as the student example showed, and the Multinomial extends everything to many classes.</p><p>The generative route reached the same destination from the other side: model the features within each class, then apply Bayes’ rule. A shared Gaussian covariance gives LDA and its linear boundaries, separate covariances give QDA, and independent features give naive Bayes. Written as log-odds, all four methods sit in one picture. Logistic regression and LDA are equally flexible, QDA adds interactions, and naive Bayes adds arbitrary per-feature functions, so which one wins depends on whose assumptions match the data. Finally, the 0–1 error rate turned out to be a poor judge of a classifier on its own. The confusion matrix shows which errors are being made, the threshold trades one kind for the other without retraining, and the ROC curve and its area summarise performance across every threshold.</p><p>That leaves a practical question the lecture closed on. With LDA, QDA, naive Bayes and logistic regression all on the table, how should we choose among them, and is “use a test set” really the right answer? That is the subject of the next lecture.</p><p><em>Thanks for reading! If you found this useful, consider following for more articles on machine learning and statistical modelling.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3b788ced731f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[When Linear Regression Breaks: Five Ways the Assumptions Fail, and How to Compute β̂ Without…]]></title>
            <link>https://medium.com/@matnogueira/when-linear-regression-breaks-five-ways-the-assumptions-fail-and-how-to-compute-%CE%B2%CC%82-without-3c397f3c44c2?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/3c397f3c44c2</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[mathematical-modeling]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[linear-regression]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Wed, 14 Oct 2026 14:52:26 GMT</pubDate>
            <atom:updated>2026-10-14T14:52:26.465Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>When Linear Regression Breaks: Five Ways the Assumptions Fail, and How to Compute β̂ Without Inverting a Matrix</strong></h3><blockquote>Every guarantee of least squares rests on assumptions. This lecture asks what happens when real data breaks them, and what to do about it.</blockquote><p><em>Every image and example was taken from the following sources:</em></p><ul><li><a href="https://www.youtube.com/watch?v=P5HASTfCXwQ"><em>Lecture 3 — Regressão Linear e Revisão da Semana 1 — Machine Learning 2026 Summer Course at IMPA</em></a></li><li><a href="https://w3.impa.br/~pauloo/teaching/ml/"><em>Machine Learning 2026, Prof. Paulo Orenstein</em></a></li></ul><h3><strong>Introduction</strong></h3><p>These notes cover Lecture 3 of the <strong>IMPA Summer Course — Machine Learning 2026</strong> (Paulo Orenstein), one article per lecture.</p><p>If you want extra context, the earlier articles in the series follow the course order:</p><p>1. <em>Foundations of Statistical Learning: From Rules to Data, and What Makes an Estimate Good</em></p><p>2. <em>Linear Regression: Least Squares, Inference, and How Far a “Simple” Model Stretches</em></p><p>Everything below is <strong>self-contained</strong>: you do not need to read the other posts to follow this one, as long as you are comfortable with expectations, variances, and enough linear algebra to recognise a matrix inverse and an orthogonal matrix when you see one.</p><p>This lecture closes the first week of the course. It opens with a compressed review — the three decisions behind any machine-learning method, empirical risk, the test set, bias and variance, and the whole linear-regression story in one slide — before paying the debt left from the previous class: the <strong>five problems</strong> that appear when linear regression meets real data (correlated errors, heteroscedasticity, outliers, leverage points, collinearity), each with a diagnostic and a fix. It ends with a question that sounds like an implementation detail and is not: how is β̂ actually computed, if inverting a matrix is a bad idea? The second half of the class was a hands-on Python lab; this article covers the expository part only.</p><h3><strong>The first week on one slide</strong></h3><p>Where are we? By the end of Chapter 2 of the course, supervised learning had settled into a small number of moving parts, and it is worth having them in front of us before we start breaking things.</p><p>We receive <strong>training data</strong> — pairs (xᵢ, yᵢ), with features in some space 𝒳 and outputs in 𝒴, usually the real line for regression or a set of categories such as {0, 1} for classification. The goal is a function that predicts well, and “well” is measured by a <strong>risk</strong>, the expected value of a loss:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/899/1*EH2xboTpzTOsn8OTmhJ5XQ.png" /></figure><p>In words: among all functions in a class ℱ, pick the one with the smallest average loss. Two things in that line are choices. One is the class ℱ, which we have not committed to yet. The other is the <strong>loss</strong> L, which says how bad a wrong prediction is. And the data are being treated as random — the object being minimised is an expectation, not a fixed function we could plot.</p><p>About the loss there is, as the lecturer puts it, “not much fight.” For regression we use the squared loss, and the moment we do, the target is determined: it is the conditional expectation 𝔼[Y ∣ X = x]. For classification with two classes we use the 0–1 loss, an indicator that is one when we got the class wrong, and the target becomes the most probable class given x. Both are sensible; the first week spent some effort convincing us of that.</p><p>Sensible or not, we cannot compute either minimiser, because we have no access to the distribution of Y given X. The way out is to swap the population average for a sample average — the <strong>empirical risk</strong> — and minimise that instead.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Txv3AGLSfcxUNhygqGd46Q.png" /></figure><p>The hat on f̂ is doing honest work: this function was trained on our data and depends on the sample.</p><p>That one line is the whole course in miniature, because it exposes the <strong>three basic components</strong> of any machine-learning method: the <strong>class of models</strong> ℱ, the <strong>loss</strong> L, and the <strong>optimizer</strong> — how we actually find, inside the class, the function that minimises the objective. The strengths and weaknesses of every method we will meet come down to how those three decisions are made, and the third is the subtle one; it will stay subtle for a while. Linear regression picks a very specific class and gets away with a trivial optimizer, because the problem is convex with a closed-form solution. Nearest neighbours picks essentially no restriction on the class and pays for it with an optimizer that is much looser — nothing is being solved exactly.</p><h4><strong>Judging the choice: test error, and where the error comes from</strong></h4><p>Once we have a f̂, we need to know whether it is any good. Averaging the squared errors on the training set is a bad idea, and the argument is worth restating: in polynomial regression the training error only goes down as you add higher powers, so a training-error criterion would always pick the highest degree available — taken to its conclusion, it would tell you to include every feature that exists. The fix is a <strong>test set</strong>, a precious block of data the model never sees, on which the error is computed honestly.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/953/1*HdLw3tTdJhCiMBXW0mkQ2Q.png" /></figure><p>The test error also has a decomposition the lecture calls both universal and intuitive. For a fresh point (x₀, y₀) with y₀ = f(x₀) + ε, add and subtract the average prediction 𝔼[f̂(x₀)] inside the square, expand, and the cross terms vanish:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zFGrzDzxYS3kutHSFH0FwA.png" /></figure><p>where the bias is the gap between the average prediction and the truth. The last term is error we commit no matter which method we choose — it is <strong>irreducible</strong>. The first two are ours. <strong>Bias</strong> is the error of approximating f with an f̂ that cannot capture its nuances; it comes from a class that is too restricted, linear methods fitting a non-linear truth being the canonical case. <strong>Variance</strong> is how much the estimate would change if the training data changed; it comes from a method flexible enough to stick to the particular sample it saw. In the polynomial picture: if the truth has degree 10 and we fit a parabola, infinite data will not save us; if we fit degree 50, we interpolate every point and capture noise instead of signal. If we can tell which of the two is happening, we have some hope of improving the model.</p><p>That is Chapter 2 in two slides. Chapter 3 applied all of it to one specific class of models, and that is where the lecture turns next.</p><h3><strong>Linear regression in one slide: β̂ is random, and that is the point</strong></h3><p>When the class ℱ is the linear functions, choosing f̂ collapses to choosing p + 1 numbers — much easier than choosing an object in an infinite-dimensional space — and the empirical-risk problem has a closed-form answer. Stack the responses into a vector y and the features into a matrix X with n rows and p + 1 columns (the extra column is all ones, for the intercept). Then</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TQAeM6S4FrQ1W9EPsgmxVg.png" /></figure><p>There is a lot behind that formula. On the linear-algebra side, it says we are projecting y onto the column space of X. On the statistical side — the part that matters today — betâ is a <strong>random object</strong>. It depends on y, and y carries a random error, so it makes sense to ask for its mean and variance. If the errors are independent with zero mean and common variance σ²,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1006/1*Bwu26r6mds_cxMEJfxwgOA.png" /></figure><p>This is more useful than it looks. Add the distribution we consider most reasonable for the error — a Gaussian — and we get the distribution of betâ for free:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*GC2iINCzM6FedOCGlX2-Mw.png" /></figure><p>And once we have the distribution of betâ we can do hypothesis tests and confidence intervals, which the lecturer describes as the most important method behind essentially every scientific discovery of recent years. This is how science decides what is true.</p><p>One comma in that story: the tests involve σ², which we do not know. It is estimated by the average squared residual, with a denominator that accounts for the p + 1 parameters already spent:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/810/1*jAD9lEypqeGv9M9_ZmWqZA.png" /></figure><p>Besides being reasonable, this is the maximum-likelihood estimator — maximum likelihood being on the course’s prerequisite list, which the lecturer takes as one more reason to revisit that list. With sigmâ² in hand, the test statistics follow: a <strong>t test</strong> for whether a single coefficient is zero, an <strong>F test</strong> for whether a whole group of coefficients is zero at once.</p><p>The philosophy is always the same, and it will come up repeatedly in the computational part of the course. Assume the null hypothesis. Use a statistic whose distribution under that hypothesis is known. If the value you observe is too extreme for that distribution, then, rather than concluding that unlikely things happen, you conclude that you probably had the wrong view of the world, and you reject. The <strong>p-value</strong> is the probability, under the null, of seeing something as extreme as what you saw or more; a very small p-value is what triggers the rejection.</p><blockquote><em>A hypothesis test does not prove the alternative. It says: if the null were true, this data would be a surprise — and surprises are evidence that the null is wrong.</em></blockquote><p>That is the machinery. The previous lecture then asked what you actually do with it.</p><p><strong>Four practical questions and three extensions</strong></p><p>Four questions fall out of this way of seeing the world. The review is brief, because the point of today’s class is what comes after.</p><p><strong>Which subset of predictors should I use?</strong> You cannot test every subset. The answer was computational: <strong>forward selection</strong> (start with nothing and add features one by one until the model is good enough), <strong>backward selection</strong> (start with everything and remove what looks irrelevant), or a <strong>mixed</strong> strategy. The question will return, because it is really the same question as “how many powers do I include in a polynomial regression?”, and it is a serious problem without a good general answer. In genetics a single test gives you thousands of features per person, and you rarely have thousands of people; linear regression breaks when p &gt; n — intuitively, and mathematically too, since Xᵀ X is then not invertible.</p><p><strong>Is any predictor actually significant?</strong> Not by checking whether betâⱼ differs from zero — it almost always does. Regress “will it rain tomorrow” on “how many grains of rice I ate today” and a specific sample will give you something like 0.04, because betâ is random and every sample wobbles. The t and F tests are the technology that says whether 0.04 is an effect or just the wobble you would expect around zero if the true coefficient were zero.</p><p><strong>Is the fit good enough?</strong> The test error is the excellent answer. If you only have training data, training error is not a good metric, but you can weight it against the number of features — the residual standard error does that with its denominator:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/629/1*9lJEOVCEL7B2KIjRtZzzBQ.png" /></figure><p>The RSE is a <em>mathematical</em> compensation for adding features; the test set is a <em>computational</em> one.</p><p><strong>How do I predict, and can I put an interval on it?</strong> The prediction is the new feature vector times betâ, and there are formulas for <strong>prediction intervals</strong> around it. This is the interval people actually want: a doctor does not want an interval on betâ, which they cannot interpret, but “this person has the disease with probability 43%, and with 95% confidence that number is between 40 and 50.”</p><p>The <strong>extensions</strong> were simple in form and large in effect. A binary categorical predictor enters through an indicator — a <strong>dummy variable</strong>, artificial in the sense that we created it — so the model reads β₀ + β₁ plus noise for observations in class 1 and β₀ plus noise otherwise; with more classes you add one indicator per non-base class. An <strong>interaction</strong> lets two features talk to each other through their product,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IF1_M6XUTYnyNFDqJEc1mQ.png" /></figure><p>so that the effect of raising X₁ depends on X₂; the <strong>hierarchy principle</strong> says to keep the main effects when you include the interaction. And <strong>non-linearity</strong> comes from powers of a feature — polynomial regression — because what is linear in linear regression is the relationship between y and the coefficients, not the features. Given a good basis for your function space, linear regression is exactly the tool for writing y in that basis.</p><p>What was left from Chapter 3 is the problems. We made a lot of assumptions along the way and, in the lecturer’s words, the real world is the real world: everything fails. The hope is not that the assumptions are always exactly true but a form of protection: if they fail a little, the methods still basically work; if they fail badly, we have a fix. There are five things to check, the same five as in the course textbook: correlation between the errors, non-constant error variance, outliers, leverage points, and collinearity. We take them in order.</p><h3><strong>Problem 1: correlated errors</strong></h3><p>Everything inferential in the last section rested on one line.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*8FAdDkesxrzjXmTtZ2xM3A.png" /></figure><p>Two hypotheses hide in “iid Normal,” and they have very different severities.</p><p><strong>Normality</strong> is generally not the end of the world. The Gaussian is a very generic distribution in nature: when many essentially random errors accumulate into one phenomenon, the result tends to be Gaussian, which anyone who sat through an engineering statistics course has seen. Still, if it is not precisely true, the inferential procedures — standard errors, confidence intervals, hypothesis tests — become invalid, because each of them used “the distribution is Gaussian, therefore…” somewhere. There are fixes, and the problem sets will get to them.</p><p><strong>Independence</strong> — the first “i” in iid — may very well be the end of the world. Think about time series in finance, or spatial series in meteorology: data that vary over time, or over space. It would be very strange for the errors there to be independent. If a company is doing very badly today, you do not expect it to recover perfectly tomorrow, nearly go bankrupt the day after, and bounce back again; if things are going well now they will probably keep going well, and if they are going badly they will probably keep going badly. That kills independence between consecutive errors. In space it is the same story: if it is raining at one point, the chance that it is raining just next to it is not low — there is a cloud there — so the error you collect at one location tells you something about the error at the neighbouring one.</p><p>In those settings the independence assumption is simply too strong, and the generic remedy is to <strong>model the correlation</strong>: model enough of it that, once accounted for, it disappears from the errors. Time series has a whole set of methods for this, several of them built on linear regression; spatial statistics has its own; the slides mention Gaussian processes as a specific tool. None of that is covered in this course, though its second half brings methods that handle this kind of structure well — text being the flagship example: if the previous character was “ã”, anyone who has read Portuguese has a good idea of what comes next, and that dependence is exactly what a model like a GPT is built to capture. The point for now is narrower: linear regression can reach these problems, as long as the correlation is modelled rather than ignored.</p><p>How do you see correlation in the errors? Plot the residuals against the observation index.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y4B0KH0eGYroNjv-1mIsQg.png" /><figcaption><em>Figure 1: Residuals against observation number for three simulated series, with correlation ρ = 0.0 (top), 0.5 (middle) and 0.9 (bottom) between consecutive errors. In the top panel the residuals jump above and below the dashed zero line essentially independently. As ρ grows, runs appear: in the bottom panel, once the residual is high it tends to stay high for a stretch, then drift below zero and stay there. That persistence is the visual signature of correlated errors.</em></figcaption></figure><p>When the residuals go up and down independently, as in the top panel, the iid picture is plausible. When they look like the bottom panel — tendencies that persist, a residual that keeps climbing and then keeps falling — you certainly do not want to apply linear regression naively, at least not for its inferential procedures: t tests, F tests, confidence intervals.</p><p>Correlation is about the errors talking to each other. The next problem is about the errors being different sizes.</p><h3><strong>Problem 2: heteroscedasticity</strong></h3><p>There was a subtle assumption in the setup that, as the lecturer notes, nobody called him out on. Writing 𝕍[εᵢ] = σ² says the variance of the error is the same for every i. What if it is not? What if, for instance, the errors grow with the features?</p><p>His example is a bathroom scale. Weigh people, and the scale has its normal, modest variation. Start weighing much heavier things — a sofa, say — and the variance of the readings may grow with the weight being measured. The variance of the error now depends on the value. That situation has a name that the lecturer clearly enjoys, <strong>heteroscedasticity</strong>, and it means precisely this: 𝕍[εᵢ] = σᵢ², a variance that changes with i.</p><p>Even with independent, Gaussian errors, this can happen, and the diagnostic is a plot of the fitted values ŷᵢ — the prediction for each observation — against the residuals.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/667/1*la7U2j3jqLGf7UYsSmcDMg.png" /><figcaption><em>Figure 2: Residuals against fitted values when regressing the raw response Y. Around small predictions (fitted values near 10) the residuals sit tightly around zero; as the prediction grows towards 30 the cloud fans out into a funnel. The red curve tracks the mean residual, which stays near zero throughout; the two blue curves track the spread, and they open up as the prediction increases. A few labelled points (998, 975, 845) sit at the top of the funnel.</em></figcaption></figure><p>Read it as the scale example: when the prediction is small the error is small, and as the predictions get much larger the error grows a lot. Within the instrument’s expected range there is little deviation; at the extremes, the deviation is extreme too. A student asked about the blue curves, and the answer is that they behave like a predictive band on the residuals — capturing, say, the 5th and 95th percentiles, or the 25th and 75th, the lecturer was not sure which — so that even though the mean residual is near zero everywhere, the band makes it visible that the uncertainty is not.</p><p>There are two fixes.</p><p>The first is to <strong>transform the data</strong>. Instead of modelling y = Xβ, model the logarithm of y as Xβ. This is still a linear regression — still linear in β — but it changes the nature of what is being predicted. The log barely moves small values and flattens large ones, pushing them down, which stabilises the regression. Here is the same example from the textbook after the transformation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/645/1*rxwbTDenq0Uh3SVZVulcBA.png" /><figcaption><em>Figure 3: The same regression with log(Y) as the response. The funnel is gone: the residual cloud has roughly the same vertical spread across the whole range of fitted values (2.4 to 3.4), and the blue spread curves run nearly parallel to the red mean curve instead of diverging. A handful of points (437, 671, 605) sit below the band, but the systematic widening has disappeared.</em></figcaption></figure><p>The second fix is a more parametric modelling of what is going on: <strong>weighted linear regression</strong>. Ordinary least squares treats every point in the sample as worth the same — the first observation, the second, the tenth. Figure 2 says that is not quite right: the points with high predictions carry a lot of uncertainty and should matter less when we estimate betâ, while the points where we predict 10 and the values really do cluster near 10 are well estimated and deserve priority. Giving each observation a weight formalises that instinct; deriving the weighted estimator is left for the problem sets.</p><p>Heteroscedasticity is about the whole cloud of errors changing shape. The next two problems are about individual points misbehaving — first in y, then in x.</p><h3><strong>Problem 3: outliers</strong></h3><p>The lecturer’s definition of an outlier is, by his own admission, relatively subjective, like everybody’s: it is a point whose value of y is very improbable. Something with an enormous error, generally for structural reasons. You are trying to predict something that is outside everything you would expect.</p><p>The textbook figure shows the same data three ways.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/501/1*jtIX6wMfgJLKiCMgQEAqEA.png" /><figcaption><em>Figure 4: Y against X, with observation 20 (red) sitting far above the rest of the cloud. Two least-squares lines are drawn: the solid red one is fit with point 20 included, the dashed blue one without it. They lie almost exactly on top of each other — the outlier barely moves the fit.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/501/1*v49NEuFcjrIy1TbORdATFA.png" /><figcaption><em>Figure 5: The same data as residuals against fitted values. Every point but one lives in a band between roughly −1.5 and 1.5; observation 20 sits alone near a residual of 5. This is where the outlier is obvious.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/501/1*no7JwOJJweD0MWeQFj_YGw.png" /><figcaption><em>Figure 6: Studentized residuals — each residual divided by an estimate of its own standard deviation — against fitted values. The picture is Figure 5 on a normalised scale: the bulk of the points stay within about ±2, while observation 20 exceeds 6.</em></figcaption></figure><p>Now the important observation. Points like this <strong>do not usually affect the fit</strong>. Delete point 20 in your head and ask what happens to the line: it would move from the dashed blue one to the red one, which is basically no move at all. The prediction stays the same. What the outlier does ruin is the <strong>evaluation metric</strong>. Compute the mean squared error and it explodes, because there is one horrible point in the sum.</p><p>So how do you deal with it? This is where the lecture gets, in its own word, polemical.</p><p>If you have a deep conviction that the outlier exists because of a <strong>structural error</strong> — something that certainly should not be there — you remove it. Suppose you are measuring the heights of the students in the room: 1.70 m, 1.80 m, 1.90 m, 1.60 m, and then a 6-metre reading. Surely, at the moment of measuring, someone measured the auditorium instead of a student. That number captures nothing about the height of a person in this course, and it can go.</p><p>But notice the word <em>surely</em>. In practice this move is very, very dangerous, because it only makes sense if you can convince everyone else. Imagine publishing your results and writing “this point I did not like, so I threw it away.” It is extremely suspicious: precisely the point where your prediction is terrible is the one you discard, and suddenly your predictions are good. The bar for convincing others that a point can be dropped is very high, and it is not something to do lightly.</p><p>If you cannot clear that bar, the outlier is telling you something about the model. Perhaps the model needs to be more complex, or needs other predictors. Or perhaps it is telling you that the error distribution is wrong: under a Gaussian, improbable things do not happen, and you are looking at one. Maybe the errors should be modelled with a heavier-tailed distribution — Laplace, for instance. What happens to the regression when you do that is, again, a problem-set question.</p><blockquote><em>An outlier barely moves the least-squares line, but it wrecks the error metric — and removing it requires convincing the world that it was a mistake, not an inconvenience.</em></blockquote><p>Outliers are unusual in y. Their cousins are unusual in x, and those do move the line.</p><h3><strong>Problem 4: leverage points</strong></h3><p>If outliers are unusual values of y, <strong>leverage points</strong> are unusual values of xᵢ. And they come with a problem the outliers did not have: they definitely affect betâ.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/545/1*VFiAOkf_0eHok78rQtAp-g.png" /><figcaption><em>Figure 7: Y against X with two flagged observations. Point 20 is the outlier from before — unusual in Y, ordinary in X. Point 41 sits at X ≈ 4, well beyond the range of every other observation. The solid red line is the fit with point 41 included and the dashed blue line the fit without it; unlike in Figure 4, the two lines now visibly diverge, tilted by the single far-out point.</em></figcaption></figure><p>Point 20 does not make much difference to the line. Point 41 does, because the line has to twist to accommodate a point that is far away. Push that point even further out and nudge it slightly, and the entire line would have to move a lot because of one observation. That is, as the lecturer puts it, very uncomfortable.</p><p>A second panel makes a subtler version of the same point.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/534/1*B-xIzJdl2M7-1qVhVCKlXw.png" /><figcaption><em>Figure 8: Two predictors plotted against each other. Most observations lie along a tight diagonal band, outlined by the dashed blue ellipse. The red point, at roughly (1, −1), is unremarkable on either axis alone — plenty of points share its X₁ value, and plenty share its X₂ value — but it is far from the joint pattern. It is a leverage point that inspecting each variable separately would never catch.</em></figcaption></figure><p>The advantage over outliers — where it was hard to convince anyone a problem even existed — is that leverage has a very reasonable measure. The idea is a derivative: take the prediction ŷᵢ and differentiate it with respect to yᵢ. If I nudge the observed value, does my prediction at that point move a lot? For point 20, no — the prediction there stays essentially put. For point 41 the change is drastic.</p><p>To compute the derivative, recall that the fitted values are a linear function of the observed ones, ŷ = Hy, where H is the hat matrix X(XᵀX)⁻¹Xᵀ. Then ŷᵢ is the i-th row of H times y, and differentiating with respect to yᵢ leaves the diagonal entry.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vMkwc9Ls68f2wwm49n-iYQ.png" /></figure><p>This is the <strong>leverage</strong>, or influence value, of observation i: how much the fitted value at i responds to its own observed value. It lies between zero and one — proving that is left as an exercise — and it gives you a number to check whether leverage points are showing up in your regression.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/535/1*9Wl_fxGA7gsEjF2VTO07ew.png" /><figcaption><em>Figure 9: Studentized residuals against leverage, the diagnostic plot that separates the two kinds of trouble. Observation 20 sits at the top left: a huge residual (near 5) but low leverage (about 0.03), an outlier in Y with little pull on the fit. Observation 41 sits far to the right at a leverage of about 0.26, ten times the typical value, with a residual around 3 — an unusual X that drags the line. The bulk of the data cluster at the bottom left with small residuals and small leverage.</em></figcaption></figure><p>Both outliers and leverage points are about single observations. The last problem is about the predictors themselves.</p><h3><strong>Problem 5: collinearity</strong></h3><p>Collinearity came up in the previous lecture; here the goal is to add a couple of subtleties. The problem is a <strong>linear relationship between predictors</strong>.</p><p>Take the credit-card example from the course. You want to predict a customer’s end-of-month balance, and you regress it on the credit limit the bank gave them and their credit rating: balance on limit plus rating. Sounds reasonable. What you do not know is that the bank secretly sets the rating as a linear function of the limit — say rating = 80 + 0.06 × limit.</p><p>Why is that a problem? Because with those two features the design matrix X <strong>no longer has full rank</strong>, so Xᵀ X is not invertible — and then, going back to the formula, betâ = (Xᵀ X)⁻¹ Xᵀ y simply does not exist.</p><p>The example from last week was the dummy-variable version. You include one indicator for “has a PhD” and another for “does not have a PhD.” The implicit assumption that bites is that X already contains a column of ones, for the intercept. The two indicators sum to exactly that column, so you are back to a linear relationship, and the matrix is again singular. In practice, when you sit down to write code, the software detects this automatically and handles it — in effect, “you are being silly, let me fix that.” It takes some carelessness to land here — an indicator for Brazilian alongside one for non-Brazilian, say.</p><p>It is worth being concrete about what actually goes wrong, because it is more than numerical instability in a matrix inversion. Suppose two features are identical, X₁ = X₂, and you ask the model for β₀, β₁, β₂. Predicting with the triple (betâ₀, betâ₁, betâ₂) gives exactly the same predictions as predicting with (betâ₀, betâ₁ + 100, betâ₂ − 100):</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MdxXodz4RuALsaeH1VZPIA.png" /></figure><p>and the last term is zero because x₁ = x₂. You are asking the model to do something impossible. You give it two features and ask how much weight to put on each, and it answers that there are infinitely many combinations of weights giving the same prediction, with no way to tell them apart. The parameters are <strong>not identifiable</strong>. That is the real reason the variance of betâ goes to infinity in this case — and with it, obviously, the hypothesis tests and confidence intervals go out the window.</p><p>Perfect collinearity is relatively rare. What actually happens, and is more problematic, is <strong>approximate collinearity</strong>: two features that are almost linearly related, so that combined linearly they are almost a third one. The model does pick a betâ₁ and a betâ₂, but it starts mixing up the subtle difference between two nearly-identical features with the ordinary statistical error present in any regression. Change the sample a little and the estimates would shift in a different way; attributing effect to one feature or the other becomes very hard. Even in these less extreme cases, the variance of the estimators can get very high.</p><p>The genetics example shows why this is not a corner case. With one feature per gene, you would expect several genes to serve the same function — the immune system, say — and to be expressed together. That alone makes the regression suffer from something like collinearity: the estimated variance of betâ comes out enormous, which is the model’s way of saying “you are asking me to distinguish coefficients for features that are all very similar.” Including too many features has this price: beyond being useless for prediction, they can blow up the variance of the estimator. The course will come back to this.</p><h4><strong>Diagnosing it: from correlation to the VIF</strong></h4><p>A student asked whether there is a test for which parameters are entangled with which. With just two predictors, plain <strong>correlation</strong> is enough: in the credit example, every time the rating goes up, the limit goes up too, and the correlation captures exactly that. With p predictors it is more delicate, because pairwise correlations do not reveal <strong>multicollinearity</strong> — one feature being nearly a linear combination of several others.</p><p>The diagnostic is the <strong>variance inflation factor</strong>. For the j-th coefficient,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/530/1*IrHxlKXes0iEBearFYP0VQ.png" /></figure><p>where the R² in the denominator is the one you get by regressing Xⱼ on all the other features — using the j-th feature as the response and everything else as predictors. If you are in doubt, you compute this for each feature and look for values that are very large. It is a partial solution, but it does measure the degree to which Xⱼ is explained by the rest.</p><p>The formula looks, in the lecturer’s phrase, absolutely crazy, but there is a reason for it. The variance of the j-th coefficient can be decomposed into a factor that depends only on the j-th feature and a factor that depends on all the others.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tqXNX1KkaSsfNqKhkkZWxg.png" /></figure><p>The first fraction is what the variance of betâⱼ would be on account of Xⱼ alone. The second fraction — the VIF — is how much the other features inflate it. That is what makes the VIF a genuine generalisation of correlation to p variables: it isolates the influence of X₋ⱼ on the variance of betâⱼ. The proof may come later in the course; the slides give the intermediate step, in which the jj entry of the inverse is written in terms of the regression of Xⱼ on the others.</p><blockquote><em>Collinearity is not a numerical nuisance. It means the data cannot tell two explanations apart, and infinite variance is the honest answer to an unanswerable question.</em></blockquote><p>So: linear regression has several problems, and there is a fix or a diagnostic for each one. That is not surprising — Gauss was already looking at this, and there has been time to think of good solutions. One question remains, and it is about the computer rather than the statistics.</p><h3><strong>Computing β̂ without inverting a matrix</strong></h3><p>The closed form for the coefficients has an inverse in it, and depending on which path through mathematics you took, that inverse should make you uneasy. Inverting a matrix is a real problem: it tends to be numerically <strong>unstable</strong> and <strong>slow</strong>. So the question is whether you have to. Go back to the derivation and the answer is no. There are smarter ways to get betâ than computing an inverse, even on a computer — especially on a computer — and they go through two decompositions from linear algebra, <strong>QR</strong> and <strong>SVD</strong>, each useful in different circumstances.</p><h4><strong>The QR route</strong></h4><p>Write the design matrix as X = QR, where Q is orthogonal — meaning Qᵀ Q = I — and R is upper triangular. Now go back to the first-order condition, the moment where the inversion happened, and refuse to commit that original sin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/528/1*lII3QUoXXuXNBQe4FS7cQg.png" /></figure><p>Substitute the decomposition. On the left, Xᵀ X = Rᵀ Qᵀ Q R, and the Qᵀ Q in the middle is the identity and dies, leaving Rᵀ R. On the right, Xᵀ y = Rᵀ Qᵀ y. Both sides start with Rᵀ, which cancels, and the problem collapses to</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/346/1*cm_ThR0YJgADYTB2sBoj8Q.png" /></figure><p>You could invert R at this point, but you do not even need to, because R is upper triangular, and a triangular system reads off from the bottom up. The last row of R is zero everywhere except its last entry, so multiplying it into betâ kills every term but one, and the last coefficient falls out directly. The row above it has two non-zero entries — one multiplying the coefficient you just found, one multiplying the next unknown — so you isolate that unknown and solve. Keep going up the rows and every coefficient comes out by <strong>back substitution</strong>, one division at a time. There is no matrix inversion anywhere in the procedure.</p><h4><strong>The SVD route</strong></h4><p>Alternatively, write X = U D Vᵀ, with U and V orthogonal and D diagonal. The same trick as before — the inner Uᵀ U is the identity — gives</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/508/1*T9sg7kWZGhITg8-hdtgTgg.png" /></figure><p>Now the formula for betâ asks you to invert that, and that turns out not to be hard at all. The inverse of an orthogonal matrix is its transpose, and the inverse of a diagonal matrix is the diagonal of the reciprocals, so the inverse is V D⁻² Vᵀ. Multiply through by Xᵀ y = V D Uᵀ y, and the Vᵀ V that appears in the middle disappears for free.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RYO0u_ioj-0oxTL-PkJsMQ.png" /></figure><p>The only thing that ever gets inverted is D, a diagonal matrix, which is trivial. When you sit down to write code, one of these two routes is what runs underneath any least-squares call — QR or SVD, depending on the sizes involved.</p><h3><strong>Conclusion</strong></h3><p>The lecture’s first half was a map of the territory covered so far: every method is a choice of model class, loss and optimizer; empirical risk replaces the expectation we cannot compute; a test set replaces the training error we cannot trust; the test error splits into bias, variance and noise; and for linear regression all of this is explicit, down to the distribution of betâ that makes t tests, F tests and p-values possible.</p><p>The second half was the bill for that explicitness. Correlated errors invalidate the inference, and you spot them as persistence in a residual sequence and fix them by modelling the correlation. Heteroscedasticity shows up as a funnel in the residual-versus-fitted plot, and a log transform or a weighted regression closes it. Outliers barely move the line but wreck the error metric, and removing one requires an argument the rest of the world will accept. Leverage points do move the line, and the diagonal of the hat matrix measures exactly how much. Collinearity makes the parameters non-identifiable — infinitely many coefficient vectors, one prediction — and the VIF isolates how much each coefficient’s variance is inflated by the other features. And finally, betâ should never be computed by inverting Xᵀ X: a QR decomposition with back substitution, or an SVD that reduces the inversion to a diagonal matrix, does the job stably.</p><p>The recurring lesson is that the assumptions behind a method are not fine print. They are the places where you should expect it to break, and knowing where to look is most of the diagnosis. With the linear model understood — guarantees and failure modes alike — the course is ready for the next family of methods.</p><p><em>Thanks for reading! If you found this useful, consider following for more articles on machine learning and statistical modelling.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3c397f3c44c2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Linear Regression: Least Squares, Inference, and How Far a “Simple” Model Stretches]]></title>
            <link>https://medium.com/@matnogueira/linear-regression-least-squares-inference-and-how-far-a-simple-model-stretches-f3bff91a1a4c?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/f3bff91a1a4c</guid>
            <category><![CDATA[data-science]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[linear-regression]]></category>
            <category><![CDATA[mathematical-modeling]]></category>
            <category><![CDATA[statistics]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Mon, 28 Sep 2026 17:05:18 GMT</pubDate>
            <atom:updated>2026-09-28T17:05:18.327Z</atom:updated>
            <content:encoded><![CDATA[<blockquote>Linear regression is the one model in the course we can take apart completely — and taking it apart is exactly what makes it stretch further than its name suggests.</blockquote><p><em>Every image and example was taken from the following sources:</em></p><ul><li><a href="https://www.youtube.com/watch?v=h_8fUxdA0QU">Lecture 2 — Regressão Linear — Machine Learning 2026 Summer Course at IMPA</a></li><li><a href="https://w3.impa.br/~pauloo/teaching/ml/"><em>Machine Learning 2026, Prof. Paulo Orenstein</em></a></li></ul><h4><strong>Introduction</strong></h4><p>These notes cover Lecture 2 of the <strong>IMPA Summer Course — Machine Learning 2026</strong> (Paulo Orenstein), one article per lecture.</p><p>If you want extra context, the earlier article in the series follows the course order:</p><p><em>1. Foundations of Statistical Learning: From Rules to Data, and What Makes an Estimate Good</em></p><p>Everything below is <strong>self-contained</strong>: you do not need to read the other post to follow this one, as long as you are comfortable with expectations, variances, basic calculus, and enough linear algebra to be unafraid of a matrix inverse.</p><p>This lecture instantiates the general supervised-learning setup for the simplest model in the course. We derive the coefficients explicitly, work out their means, variances and distributions, build confidence intervals and hypothesis tests on top of them, rewrite everything in matrix form until a geometric picture appears, prove that this estimator is optimal in a precise sense, and then show that the model bends to accommodate categories, interactions and curves — because “linear” was never a statement about the features.</p><h4><strong>Where linear regression sits in the learning setup</strong></h4><p>The general supervised setup has four moving parts: training data, a class of predictive functions, a loss that scores predictions, and an optimizer that picks the best function in the class. Linear regression is what you get when you fill in each part with the simplest reasonable choice.</p><p>The training data are pairs, (x₁, y₁) through (xₙ, yₙ), with features in ℝᵖ and responses in ℝ, and we assume n &gt; p throughout — more samples than features. Keep a concrete question in mind while the notation piles up: how many hours of sleep will you get tonight? That is Y; how much magnesium you took, whether you exercised, how old you are — those are the features X.</p><p>The predictive class is restricted to functions of the form</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xPQjdZUBhZSbjf0dDj2Fcw.png" /></figure><p>In words: a weighted sum of the features plus a constant. That restriction is what gives the method its name.</p><p>The loss is the familiar squared error, L(y, f̂(x)) = (y − f̂(x))². If I predict eight hours and you sleep seven, I missed by one, and squaring makes the miss positive either way. The optimizer must then find the coefficients that make the average of that loss as small as possible:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*l12pc82Ck-oH9esAS8_Xlg.png" /></figure><p>In words: choose the coefficients that minimise the average squared miss on the training sample.</p><p>Here is what makes this lecture different from every later one. That optimization problem has a <strong>closed-form</strong> solution — an explicit formula, no iterative method, no learning rate, no convergence to worry about. Optimization, which will dominate the course once neural networks arrive, is almost invisible here.</p><p>That simplicity buys two things. We can understand every mathematical detail of this model, more than we will be able to see once the models get sophisticated enough to hide their internals; and precisely because we understand every piece, the model is easy to extend. Someone who uses this simple method well often goes deeper than someone running a neural network without knowing what is happening inside it.</p><p>The plan is four parts: simple regression with one feature, multiple regression with many, extensions of the linear model, and the practical problems that arise when the assumptions we are about to make fail. The last part needs its own lecture — we will assume freely here and pay for it later.</p><h4>Simple linear regression: finding the line</h4><p>Start with a single feature. The assumed model is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/578/1*lc5Ccx4P9Zv1HZQfAgTF3g.png" /></figure><p>with β₀ and β₁ unknown numbers we want to estimate and ε the error term. Once we have estimates, prediction for a new point is just ŷ = betâ₀ + betâ₁ x — feed in how much magnesium, read off the hours of sleep.</p><p>We choose the coefficients by minimising the <strong>residual sum of squares</strong> over the training sample:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YjOpeoubJhc0HVCsl15muw.png" /></figure><p>In words: pick the line whose vertical misses, squared and added up, are as small as possible.</p><p>This objective is convex in the two coefficients, so there is no need for anything clever: differentiate, set to zero, solve. Differentiating in the intercept gives a sum that must vanish, and since the intercept does not depend on i, we can isolate it directly:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/432/1*pcVWq-F5O0GIHImiIa4P4Q.png" /></figure><p>where the bar denotes the sample average — a piece of notation that will be with us from now on. That expression still contains betâ₁, so it is not yet a solution; we need a second equation. Differentiating in the slope and substituting the intercept we just found gives one, and isolating produces</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/514/1*YLgf151vSvxsObBBJUYJaA.png" /></figure><p>That is already usable, but it is usually written in a symmetric form, with the deviation from the mean on both sides of the numerator:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/599/1*uHChtedLwc5ADamdv7LSKg.png" /></figure><p>The rewrite needs justification. Subtracting ȳ inside the numerator costs nothing because the extra term is exactly zero: ȳ comes out of the sum, and what is left is the sum of xᵢ − x̄, which vanishes by the definition of x̄. The same argument, with the roles of xᵢ and x̄ swapped, kills the corresponding term in the denominator. Small identities like this keep reappearing, so it is worth doing on paper once — and since slides make all of this look like it follows from nothing, this is the one lecture where redoing the algebra yourself is genuinely within reach.</p><p>&gt; Both formulas are computable from the training sample alone. Nothing in them refers to a quantity we do not have.</p><p>Now put it to work on the advertising data, regressing product <em>sales</em> on the <em>TV</em> budget. The formulas give betâ₀ = 7.03 and betâ₁ = 0.0475, so the fitted model is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/817/1*P-f1SrWUKlwMuLbXSp8OKQ.png" /></figure><p>The slope has a direct reading: every 100 dollars put into TV advertising is associated with an estimated increase of 100 × 0.0475 = 4.75 in sales.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/473/1*2U9-LQ12gih1vmBpoyrY2A.png" /><figcaption>Figure 1: Each red dot is one market; the blue line is the least-squares fit, and the thin grey segments are the residuals it minimises. The trend is unmistakably increasing, but notice two things the line does not capture — it sits above the data at the low end and the vertical spread of the points widens noticeably as TV spend grows.</figcaption></figure><p>The fit looks reasonable and the increasing relationship is obvious. But the model pays a price at the extremes, and the variability of the points grows with the budget — hold on to that, it is one of the assumptions we will have to revisit.</p><h4>What is random here, and what it costs us</h4><p>Everything so far treated the estimates as numbers. They are not. Both are computed from the training sample, and a different sample would give different values — they are random variables, so what are their mean, variance and distribution?</p><p>Fixing the features, the only source of randomness is the error term, with 𝔼[εᵢ] = 0 and 𝕍[εᵢ] = σ². Since the errors average out to zero, 𝔼[yᵢ] = β₀ + β₁ xᵢ, and because expectation is linear and the features are fixed, it passes straight through the estimator’s formula. The β₀ terms cancel in the numerator, a factor of β₁ comes out, and numerator and denominator become the same sum:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/799/1*u84__GLnoXQGJO4s754_7Q.png" /></figure><p>In words: on average, the estimators hit the true values. They are <strong>unbiased</strong>.</p><p>Being right on average is not the whole story: an estimator that is right on average but swings wildly can be far worse than one that barely moves. So we need the variance too, and variance being a quadratic operator rather than a linear one makes the computation heavier. Write the slope estimator as a linear combination of the responses, with weights</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/478/1*vN0m7ZQo5eBT-4yxag3apw.png" /></figure><p>and use that each response has variance σ²:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/568/1*8lDjzlZzFgllFt6dfyx-AA.png" /></figure><p>In words: the more spread out your feature values, the more precisely you can pin down the slope.</p><p>The intercept takes one more step, since it mixes two random quantities. Expanding the variance of a difference gives a term for ȳ, a term for the slope, and a covariance between them. The first is σ² / n; the third is zero, again because the deviations from the mean sum to zero. What is left is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/830/1*PpjEdvyiRb0tgNpkKErP7g.png" /></figure><p>These formulas are exact. They are also useless as they stand, because both contain σ² — the variance of the error — and we do not know it. It has been a symbol this whole time. Is it 10? Is it 0.4? Nobody knows, which means nobody can actually compute the standard error of a coefficient from a sample.</p><p>The way out is the obvious one, and it is where the argument starts folding back on itself: estimate it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/724/1*kTGdRFss3xZnxnb_fvAkrQ.png" /></figure><p>The intuition: if the estimates are close to the true coefficients, the residual yᵢ − ŷᵢ is close to the error εᵢ itself. And since the errors have mean zero, their variance is the expectation of their square — approximated here by the sample average of the squared residuals. Why the divisor is n − 2 rather than n is genuinely not obvious, and the lecture leaves it as a debt to be settled once we have the matrix machinery.</p><p><strong>The distribution, and why we need one more assumption</strong></p><p>Mean and variance are not enough to build a confidence interval; for that we need the distribution. And that requires an assumption we have not made yet: that the errors are independent and normally distributed,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/439/1*mLOPtIHg48kUrw3KJCKjxA.png" /></figure><p>This is a genuine addition: before, we only assumed the errors had mean zero and variance σ²; now we are pinning down their shape.</p><p>The payoff is immediate. A linear transformation of a normal variable is still normal — the mean and variance may change, the shape does not — and both estimators are linear transformations of the responses, hence of the errors. So both are normal, and a normal is fully determined by its mean and variance, which we already have. Standardising the slope,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/637/1*lFd8ZWxMGAeEX0-DlH0DnA.png" /></figure><p>and since a standard normal satisfies ℙ[|Z| ≤ 2] ≈ 0.95, isolating β₁ inside that inequality gives a 95% interval.</p><p>Except the interval contains the standard error, which contains σ², which we do not know. We have to substitute the estimate — and substituting changes the distribution. To describe how, two definitions.</p><p>If Z₁, …, Zₙ are independent standard normals, the sum of their squares follows a <strong>chi-squared distribution</strong> with n degrees of freedom:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/462/1*zRY9Wvy6W-j2cCfIBhYR8w.png" /></figure><p>And if a standard normal Z and a chi-squared K with n degrees of freedom are independent, the ratio below follows a <strong>t</strong> <strong>distribution</strong> with n degrees of freedom:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/362/1*OaoCzTNmYrpouDBruydLkg.png" /></figure><p>Neither is mysterious in practice: both are distributions a computer can plot, and any probability you want from them is a lookup.</p><p>The fact that ties this together — stated here, proved later in full generality — is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/461/1*EMCXDAq_P4MwCdTG29MnNw.png" /></figure><p>Now divide the standardised slope by that quantity, appropriately normalised. The numerator is a standard normal, the denominator is the square root of a chi-squared over its degrees of freedom, and the two are independent (also proved later). By definition, the ratio is a t:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/525/1*DzOmNdEBRZW7chQa0btssA.png" /></figure><p>Notice the two hats stacked in the denominator. The first says betâ₁ is an estimator, so it has a standard error; the second says that standard error itself had to be estimated.</p><p>For large n the t distribution is very close to the normal, and ℙ[|t| ≤ 2] ≈ 0.95 still holds. So the interval survives the substitution:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0MCXFiRz1IHSB1Eo503e2A.png" /></figure><p>and every quantity in it is computable from the sample. For <em>sales</em> on <em>TV</em>, where betâ₁ = 0.0475, the 95% confidence interval is [0.042, 0.053]. Instead of “your friend will sleep 7:15”, we can say “with 95% confidence, between these two numbers” — a considerably more honest statement.</p><p>One caveat, the one everyone gets wrong the first time. It is tempting to read that interval as “β₁ has a 95% chance of being in here”. It does not. β₁ is not random — it is a fixed number we happen not to know. What is random is the <em>interval</em>: draw many samples, compute the interval each time, and about 95% of them will contain the true β₁. The coefficient stays put; the interval moves around it.</p><h4>Hypothesis tests and the p-value</h4><p>There is a useful observation buried in that interval. If it does not contain zero, the data are telling us something: β₁ is very likely inside, zero is outside, so β₁ is probably not zero. And β₁ = 0 is precisely the statement that X has no effect on Y.</p><p>That reframing is how science usually asks its questions. Formally,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nH5sqDaVHIH9mcr7dN4lMg.png" /></figure><p>Does the vaccine work? Does the Higgs boson exist? Does magnesium affect sleep? Every one of these, once you have a model, becomes a question about whether a coefficient is zero.</p><p>Under the null hypothesis, β₁ = 0, so the statistic</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/302/1*wfCewdCXCsI3-BAuEmBVxg.png" /></figure><p>should follow tₙ₋₂ — it is the same statistic as before with the hypothesised value plugged in. Compute it on the observed sample, call the result the observed t, and ask: if we really lived in the world where H₀ holds, how probable would it be to see something at least this extreme? That probability is the <strong>p-value</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2yY5FpWyTP-DDk7pDld9CQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/486/1*vQwoeZGg9t-coIq6O7OZwg.png" /><figcaption>Figure 2: Under the null hypothesis the statistic follows the bell-shaped curve drawn here. The observed t statistic (red star) has landed far out on the right; the p-value is the shaded blue area in both tails beyond that point in absolute value — the probability of a result at least this extreme in a world where the coefficient really is zero.</figcaption></figure><p>The logic is a proof by discomfort. Assume we live in the null world; compute how surprising our data would be there. If the answer is “extremely surprising” — say, less than a 5% chance — and yet here the data are, the sensible conclusion is that we probably do not live in that world. So we reject it.</p><p>The p-value is a controversial object these days, and deservedly scrutinised, but it remains useful. Applied to the advertising data:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iU7le5VJ1QMn_eeVW2lVlg.png" /><figcaption>Table 1: Simple regression of sales on TV. Each t-statistic is just the coefficient divided by its standard error, and both p-values are far below any conventional threshold.</figcaption></figure><p>Both statistics are large and both p-values are tiny, so it is implausible that either coefficient is zero: there is a real relationship between TV spend and sales. Which the scatterplot already suggested — and that is exactly the point of the machinery. Your eyes work beautifully in two dimensions and not at all in five, let alone a billion; these formulas are what your eyes are doing, written down.</p><p>One honest limitation, raised in class: rejecting H₀ assumes the functional form is reasonable. The whole test is conditional on the relationship being linear, and if it is not, the test is answering a question about a model that does not describe the data. We will get to that.</p><h4>Measuring the fit: RSE and R²</h4><p>Before adding more features, we need a way to say how good a fit is. Two measures show up.</p><p>The first is the <strong>residual standard error</strong>, essentially a standardisation of the mean squared error:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/849/1*eLq2kgloE1T0J8cZF2GbSw.png" /></figure><p>It is not used heavily in practice, but it is standard vocabulary and it will come back shortly with a real role to play.</p><p>The second is dimensionless and much more common — the <strong>coefficient of determination</strong>, R², the fraction of the variance of Y that the model explains:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NL_omCgLst6-SKQIlDY_0A.png" /></figure><p>The comparison is against a deliberately silly model: one that ignores X entirely and always predicts the sample mean. Asked how long someone will sleep, this model shrugs and says “in my sample, humans sleep about eight hours”. R² asks how much better than that you did. Predict every point perfectly and R² = 1; do exactly as well as the silly model and R² = 0. So 0 ≤ R² ≤ 1, larger being better.</p><h4>Multiple regression, matrices, and a picture</h4><p>One feature is limiting. In the advertising example it makes far more sense to use TV, radio and newspaper together than to pick one. The model becomes</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sP_exVoAENkAKGEAGHjjYA.png" /></figure><p>Where the first half of this lecture leaned on probability, this half leans on linear algebra — writing indices by hand for an arbitrary number of features is not a life anyone wants.</p><p>Stack the responses into a vector y of length n and the features into a matrix X with n rows and p+1 columns, whose first column is all ones. Rows are observations — your friends — and columns are features: milligrams of magnesium, hours of exercise, age. The leading column of ones absorbs the intercept, and the n separate equations collapse into one:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/399/1*UPZ5ePcxLYi2D7HGp9WfcA.png" /></figure><p>The objective collapses too — squaring each residual and summing is exactly multiplying the residual vector by its own transpose:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nymn3mYVdI3RX5cy1LYWiw.png" /></figure><p>Differentiating with respect to a vector makes people nervous the first time, but two rules cover it: the derivative of aᵀβ is a, and that of βᵀAβ is 2Aβ for symmetric A. Applying them,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rKiOx19eTZ7lf6LQGiLNwg.png" /></figure><p>Assume X has full rank. Then XᵀX is positive definite, the problem is convex, and setting the gradient to zero gives the first-order condition Xᵀ(y − Xbetâ) = 0, which we solve to get</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/588/1*23LZYDNiQUeHphtTNlM_XA.png" /></figure><p>Strange-looking the first time, and worth getting used to — it is the formula the rest of the summer is built on, and everything we did by hand in the one-feature case is a special case of it.</p><p>Predictions follow immediately. Substituting,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F1_5MJXKdj9zmSwhg8518g.png" /></figure><p>H is the <strong>hat matrix</strong>, for the obvious reason: hand it the observed responses and it hands back the fitted ones. It puts the hat on.</p><p>That is a lot of structure for one matrix, and structure is never an accident. H is symmetric, straight from its form, and <strong>idempotent</strong> — applying it twice does nothing new, since the inner (XᵀX)⁻¹XᵀX collapses to the identity:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*F1_7fTIIyW5Ud84qUQYDjg.png" /></figure><p>Symmetric and idempotent together are the defining properties of an <strong>orthogonal projection</strong> — here, onto the column space of X.</p><p>The geometry makes this concrete. Rewrite the first-order condition using Xbetâ = Hy: it says Xᵀvarepsilon̂ = 0, where varepsilon̂ = y − ŷ is the residual vector. In other words the residuals are orthogonal to every column of X, hence to the whole subspace they span.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/618/1*IEkIgk3y-1Djv5F4NslJew.png" /><figcaption>Figure 3: The green plane is the column space of X, spanned here by two feature vectors X₁ and X₂. The observed response y (thick arrow) sticks out of the plane; the fit Xβ̂ is its shadow directly below, assembled from the scaled features β̂₁X₁ and β̂₂X₂. The residual ε̂ = y − Xβ̂ is the dashed vertical segment, and the small square marks the right angle it makes with the plane.</figcaption></figure><p>So here is what fitting a linear model actually is. The column space of X holds every prediction the model is capable of producing. The response vector lives in ℝⁿ and, since that subspace has dimension p+1 with n &gt; p+1, y generally sits outside it. Linear regression finds the closest point inside: the orthogonal projection. The residual, being the leftover, is perpendicular to everything the model could have predicted.</p><blockquote>Least squares is not an optimization problem in disguise. It is a projection, and the normal equations just say that the leftover points straight out of the model’s reach.</blockquote><p>The lecture marks this geometry as optional material — not exam content, but the thing that makes the formula stop looking arbitrary. Two assumptions are quietly doing work: full rank of X, and the convexity that follows from it. Convexity, note, is in the coefficients, not in the features. Both come back later.</p><h4>Gauss–Markov: why this estimator and not another</h4><p>Nothing so far explains why we should prefer this estimator over the many others we could write down. The Gauss–Markov theorem does.</p><p>Among all <strong>linear unbiased</strong> estimators of β, the least-squares estimator has the smallest variance. Precisely: for any vector c and any estimator of the form betã = Ay with 𝔼[betã] = β,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/531/1*JHoWDwx4tqjLHe9-hH_4zQ.png" /></figure><p>In words: no linear unbiased estimator beats least squares on variance, in any direction you care to look. This is the property summarised by calling betâ the <strong>best linear unbiased estimator</strong>, or <strong>BLUE</strong>.</p><p>The proof is short enough to sketch. Write any competing linear estimator as betã = ((XᵀX)⁻¹Xᵀ + D)y for some matrix D — that covers everything linear. Demanding unbiasedness for <em>every</em> possible β forces DX = 0. Expanding the variance with 𝕍[y] = σ² I, the cross term carries a factor of XᵀDᵀ = (DX)ᵀ = 0 and vanishes, leaving the competitor’s variance equal to ours plus σ² |Dᵀc|² — a squared norm, which cannot be negative.</p><p>It pays off when connected to the decomposition of prediction error into squared bias plus variance. Our estimator has zero bias and, among unbiased linear estimators, the smallest variance — so it also has the smallest mean squared error in that class. Within the linear unbiased world, this is the right answer, not merely a convention.</p><h4>Inference when there are many features</h4><p>With the matrix formulation in hand, every inference result from the one-feature case generalises, and the debts we left behind get paid.</p><p>Unbiasedness first. Substituting the model into the estimator,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CJWXjUQf-3K1NCgouSGDNg.png" /></figure><p>and the second term has expectation zero, so 𝔼[betâ] = β. The variance follows from 𝕍[ε] = σ² I:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/605/1*peFS2Aq38Vym7HZ8hMNPEA.png" /></figure><p>This mirrors the simple case — σ² over a sum of squared deviations becomes σ² times an inverse matrix, with x̄ absorbed into the column of ones. Assuming normal errors, betâ is a linear transformation of a normal vector and is therefore jointly normal:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/762/1*xUxw5RFHrlL3CSGxhwh0jw.png" /></figure><p>The error variance is estimated the same way as before, with the degrees of freedom generalised:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Y1RMk3qmS5_nDu_d-r5CzA.png" /></figure><p>Setting p = 1 recovers the n − 2 from the simple case.</p><p>For a single coefficient, write betâⱼ = eⱼᵀbetâ, where eⱼ has a one in position j and zeros elsewhere. Sandwiching the covariance matrix between two copies of eⱼ just picks out a diagonal entry:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/748/1*q__AQ6JAon8d61h8b6tGHQ.png" /></figure><p><strong>Paying the chi-squared debt</strong></p><p>Now the result we assumed on faith earlier. Start from the residual vector and use HX = X, which kills the systematic part:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5eJLIAaCtZS_StEHQYr5sA.png" /></figure><p>Since I − H is symmetric and idempotent for the same reasons H is, the sum of squared residuals is a quadratic form in the errors alone: (n − (p+1))sigmâ² = εᵀ(I − H)ε.</p><p>Take a spectral decomposition I − H = QΛ Qᵀ with Q orthogonal. Idempotence forces every eigenvalue to satisfy λ² = λ, so each is zero or one. Rotating the errors by an orthogonal matrix leaves their distribution alone — the spherical normal is rotation-invariant — so the rotated errors are still independent normals with variance σ². The quadratic form is therefore a sum of squared standard normals, with as many terms as there are eigenvalues equal to one:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/745/1*7ID08FkOFjDHRrzdQkQf3A.png" /></figure><p>And the trace is easy. The trace of a projection matrix is the dimension of the subspace it projects onto, so tr(H) = p+1, giving tr(I − H) = n − (p+1). Hence</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/658/1*Ngl7sspjHAGHPjNdHnwvgQ.png" /></figure><p>The n-2 version we used earlier is the case p = 1, and the mysterious divisor was never mysterious: it counts the dimensions the projection leaves over. One loose end — the t distribution requires numerator and denominator to be independent, and betâ and varepsilon̂ are, because the covariance between them vanishes. (Left as homework in class; the computation is two lines.)</p><p><strong>Testing one coefficient, and testing several</strong></p><p>The t test now carries over unchanged apart from the degrees of freedom:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/895/1*zKwPLcmu_vjDS1jr3rV56Q.png" /></figure><p>But having many features raises a question that could not be asked before. With one feature, the only thing to test was whether that coefficient was zero. Now we might want to know whether <em>several</em> are jointly zero — whether anything at all among magnesium, exercise and diet affects your sleep. No single t test answers that, and running one per coefficient is not a substitute.</p><p>The tool is the <strong>F test</strong>. To test H₀: βₖ₊₁ = ⋯ = βₚ = 0, compare the residual sum of squares of the restricted model — the one fitted with those coefficients forced to zero, giving predictions ŷᵢ⁽ᵏ⁾ — against that of the full model:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5r8Eg7-qHMcG9Edq1-_kKw.png" /></figure><p>Two special cases matter. Taking k = p-1 tests a single coefficient, and the F test then turns out to be equivalent to the t test — a non-trivial but very doable exercise. Taking k = 0 tests whether <em>all</em> of the coefficients are zero, which is the model’s global significance test.</p><p>The proof reuses everything we just built. The F distribution is a ratio of independent chi-squareds, each divided by its degrees of freedom. Partition the design matrix into the columns being tested and the rest, and let H₋ₖ be the projection onto the restricted model’s column space. The full model’s residual sum is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/774/1*AE0y17WmBhghmLgi8bFc-g.png" /></figure><p>and an analogous computation shows that the <em>difference</em> of residual sums is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/762/1*IBMTcvNbsM-BNkNX1htJqw.png" /></figure><p>which is, geometrically, the squared length of the extra projection gained by adding the extra columns.</p><p>Independence of the two makes the ratio an F, and it comes from (I-H)(H − H₋ₖ) = 0: projecting onto the big subspace and then onto a smaller one inside it is the same as projecting straight onto the smaller one. Zero cross-covariance plus joint normality gives independence, and the ratio is F by definition.</p><h4>Reading a regression table without fooling yourself</h4><p>Time to actually fit the advertising data with all three channels.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2vv1ejgt-BpmRDT97bQsoA.png" /><figcaption>Table 2: Multiple regression of sales on all three advertising channels. TV and radio are overwhelmingly significant; newspaper is the only coefficient whose p-value is nowhere near small.</figcaption></figure><p>Newspaper is not significant, which may be surprising. Here is a clue: the correlation between newspaper and radio spend is 0.35. The two plausibly reach a similar audience, so once money is already flowing into radio, adding newspaper does not buy much — and radio, being strongly significant, absorbs the shared effect.</p><p>This is where the interpretation of a multiple-regression coefficient matters. βⱼ measures the average effect of Xⱼ on Y <strong>holding all the other predictors fixed</strong> — not the number you would get by regressing Y on Xⱼ alone. “Given that I am already spending heavily on radio, does a bit more newspaper help?” is the question the table answers, and the answer is: not much. That is a different statement from “newspaper advertising is irrelevant”.</p><p>And the largest trap is one significance cannot protect you from. Take a beach in Australia, regress shark attacks on ice-cream sales, and the coefficient comes out beautifully significant. Ice cream does not cause shark attacks: on hot days more people buy ice cream <em>and</em> more people go into the water, which raises the chance of an encounter. Sell no ice cream and the beach is empty.</p><blockquote>A statistically significant coefficient is evidence of association. It is never, by itself, evidence of causation.</blockquote><p>The same caution applies to the running example: even a highly significant magnesium coefficient would not show that magnesium <em>causes</em> better sleep, only that the two move together in the data.</p><h4>Four questions you actually ask in practice</h4><p>Four questions organise the practical use of multiple regression: is at least one predictor significant? Is there a subset worth keeping? How good is the fit? And how accurate is a prediction for a new point?</p><p>Significance. Use the F test with k = 0 to test whether all coefficients are zero at once — under the normality assumption on the errors. To test a single coefficient, put that variable last and use k = p-1, which is the t test again.</p><p>Variable selection. With p predictors there are 2ᵖ subsets, so exhaustive search dies quickly. Three practical strategies:</p><p>- <strong>Forward selection</strong> — start from the null model and add, at each step, the variable that most reduces the residual sum of squares: fit every one-feature model (magnesium alone, exercise alone, age alone), keep the best, then try adding each remaining feature to it.<br>- <strong>Backward selection</strong> — start from the full model and drop, at each step, the variable with the largest p-value.<br>- <strong>Mixed selection</strong> — like forward selection, but a variable already included can be dropped if its p-value crosses a threshold later on.</p><p>All three are greedy, and none is guaranteed to find the best subset — a reasonable path, not an optimal one. Better strategies come later in the course.</p><p>Fit. There is a trap here that echoes the training-error story: the residual sum of squares always (weakly) decreases when you add a predictor, even a useless one, so judging models by it always picks the fullest one. The residual standard error corrects for this by dividing by the remaining degrees of freedom,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/658/1*5S_UCckBNxXEQ5LxfkYsMQ.png" /></figure><p>so adding a barely useful variable lowers the numerator but also lowers n − (p+1), and the RSE can <em>increase</em>. Complexity gets penalised rather than rewarded.</p><p>Beyond any single number, plot the residuals — against the fitted values, or against each predictor. In the advertising fit they show a distinct belly: over-predicting in the middle of the range, under-predicting at both ends. That shape is the model telling you it is missing a curvature.</p><p>Prediction intervals. For a new point, the prediction is ŷₙ₊₁ = xₙ₊₁ᵀbetâ. The prediction error has mean zero and variance</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KsSkihihuuZS9eh1OAAj4g.png" /></figure><p>where the first term is the new observation’s own noise and the second is our uncertainty about the coefficients. Assuming normality, the standardised prediction error follows tₙ₋₍ₚ₊₁₎, so with roughly 95% probability</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FN0EMRN5SVToQ-LeJ64ong.png" /></figure><p>This is a different animal from a confidence interval. There, the interval covered a fixed unknown parameter; here it covers a <em>future observation</em>, which stays random even if you knew the coefficients exactly, because it carries its own error term. “This person will sleep 9 hours, and with 95% probability between 8.5 and 9.5” — that is a predictive statement, and linear regression hands it to you almost for free. Not every method does, and if you are trying to learn something about the world, knowing how much to trust a prediction matters more than the prediction itself. (A note on the word, since it collides with other usage: throughout this course, <em>inference</em> means statistical inference — not what the term means when people talk about running a trained model.)</p><h4>Extension one: predictors that are not numbers</h4><p>Everything so far assumed the features are real numbers. Plenty are not. What sport do you play — tennis, judo, swimming, none? That is not a number and cannot go into X as it stands.</p><p>Take a concrete setup: predicting credit-card <em>balance</em> from six quantitative variables and four qualitative ones — <em>own</em> (do you own your home?), <em>student</em>, <em>status</em> (marital status), and <em>region</em> (north, west or south zone).</p><p>For a two-level variable, define a <strong>dummy</strong>: xᵢ = 1 if person i owns a home and 0 otherwise. A property of a person has become a number that fits in the matrix, and the interpretation is neat. The model yᵢ = β₀ + β₁ xᵢ + εᵢ now reads as two cases: β₀ + β₁ plus noise for owners, β₀ plus noise for everyone else. So β₁ is exactly the average impact of home ownership on the balance.</p><p>On the book’s data, betâ₀ = 509.80 is the average balance for non-owners, and owners come in at 509.80 + 19.73 = 529.53. Tidy — but the p-value on that difference is not small, so there is no evidence the gap is real.</p><p>More than two categories needs more than one dummy: one fewer than the number of categories. For <em>region</em>, define xᵢ₁ = 1 for the south zone and xᵢ₂ = 1 for the west zone, and the model reads as three cases — β₀ + β₁ for south, β₀ + β₂ for west, plain β₀ for north.</p><p>The category left without a dummy is the <strong>baseline</strong>. Here that is the north zone: β₀ is its reference level, and the other coefficients measure each category’s differential <em>relative to it</em>. On the book’s data, betâ₀ = 531.00, with -18.69 for south and -12.50 for west, both relative to north, and neither individually significant.</p><p>But asking whether region matters <em>at all</em> is not two t tests. It is the joint hypothesis that both coefficients are zero — an F test — and here it returns a p-value of 0.96. Nothing to reject; no evidence that region affects the balance.</p><p>One question remains: why leave a category out? Adding features unnecessarily is a fair objection, but the real problem is worse. If everyone belongs to exactly one category, the dummies for all categories always sum to one — which is exactly the intercept column. The matrix loses full rank, XᵀX stops being invertible, and the formula we built everything on has nothing to compute. This is <strong>collinearity</strong>, and the exact version here is the easy case to avoid. Near-collinearity — features almost, but not quite, linear combinations of others — causes trouble too, and waits in the next lecture.</p><h4>Extension two: interactions and curves</h4><p>The additive model says the effect of each feature is the same regardless of the others. In the advertising case, sales = β₀ + β₁ × TV + β₂ × radio + ε implies that spending one more unit on TV always changes sales by β₁, no matter what radio is doing.</p><p>The data disagree. When the budget is split between TV and radio, the additive model <em>under</em>-predicts sales; when it is concentrated in one channel, it <em>over</em>-predicts. That is the signature of an <strong>interaction</strong>: the return on each channel depends on the other — plausible enough, since channels reaching overlapping audiences should have diminishing joint returns, while one reaching a fresh audience is worth more.</p><p>The fix is to add a multiplicative term as a new feature:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*C2yu5gGnnIU-1bnWQR35Xw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*p6jfmbcJdcmbiUCUpo68bg.png" /><figcaption>Table 3: The interaction model. The product term is as significant as the main effects, which says the two channels genuinely depend on each other rather than acting independently.</figcaption></figure><p>And the improvement is not merely statistical: R² climbs from 89.7% to 96.8%. The interaction buys real predictive power.</p><p>Interpreting it takes a small derivative. Since the partial derivative of sales with respect to TV is now β₁ + β₃ × radio, an extra 1000 dollars in TV is worth 19 + 1.1 × radio in sales, and symmetrically 1000 dollars more in radio is worth 29 + 1.1 × TV. The return on each channel grows with what you already spend on the other.</p><p>A rule comes with interactions, the <strong>hierarchy principle</strong>: whenever you include a product of two predictors, include the individual predictors too, even if their own coefficients are not significant. Without them the interaction coefficient means something else — a model with only <em>student</em> × <em>own</em> and no separate terms would have that coefficient comparing student homeowners against everyone else combined, rather than isolating the interaction.</p><p>Interactions also work between a quantitative and a qualitative predictor. Predict <em>balance</em> from <em>income</em> and the student dummy, first without the product term student × income and then with it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/232/1*4RTZV0c6wy4DklLa-u2TtQ.png" /><figcaption>Figure 4: Without an interaction, the two fitted lines are forced to be parallel. The upper red line is students, the lower black one non-students; the vertical gap between them is the student coefficient, and income has identical effect for both groups.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/233/1*1DwW7ofXZb9KiPe718O1mA.png" /><figcaption>Figure 5: With the interaction, the slopes are free to differ. The red student line is noticeably flatter than the black non-student line, so the two converge as income rises — extra income lifts a non-student’s balance more than a student’s.</figcaption></figure><p>Algebraically, the non-student line is betâ₀ + betâ₁ × income, and without the interaction the student line is the same slope shifted by betâ₂. With it, the student slope becomes betâ₁ + betâ₃ — the effect of income can now be weaker or stronger depending on the group. A far more flexible model for the price of one extra column.</p><p><strong>Linear in the parameters, not in the features</strong></p><p>The last extension goes after the assumption that has been nagging since the start: why should anything be linear? Nothing forces us to stay linear in the <em>features</em>. We can model</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SlglwK9suS7kEDzKi9hPbA.png" /></figure><p>by treating powers of X as additional features. Same machinery, bigger matrix.</p><p>The <em>Auto</em> data make the case. Plot fuel economy against engine horsepower and the linear fit is visibly wrong: below the data at low horsepower, above it in the middle, below again at the high end. Adding a quadratic term,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vOF1immH_-fpuH0DnuEXWQ.png" /></figure><p>bends the fit to follow the shape of the cloud.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/511/1*EVbl93OkSWy-f7fT2lcTaw.png" /><figcaption>Figure 6: Miles per gallon against horsepower, with three fits. The orange straight line misses the curvature and keeps falling past the data at the right edge; the blue degree-2 curve tracks the cloud closely; the dark green degree-5 curve follows the training points even more tightly and then swings sharply upward at both ends, where there is barely any data to justify it.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*W8I3oGb1AVPbi_UnqU-OXQ.png" /><figcaption>Table 4: The quadratic fit on the Auto data. Both terms are highly significant, which is the numerical version of what the figure already shows — the curvature is real, not decoration.</figcaption></figure><p>That degree-5 curve is a warning, though: it fits the training data more closely and starts to wiggle, the visual signature of overfitting, which takes us straight back to the trade-off between bias and variance.</p><p>The general point is bigger than polynomials, and it is the one worth carrying out of this lecture:</p><blockquote>“Linear” in linear regression means linear in the parameters, not in the features. Any transformation of the features — powers, logarithms, products — keeps the problem a linear regression with a larger design matrix.</blockquote><p>Which has a striking consequence: expand a feature in a rich enough basis of powers and you can approximate any reasonable function arbitrarily well. The model is far more expressive than its name lets on.</p><h4>Conclusion</h4><p>The through-line of this lecture is that a model simple enough to understand completely is a model you can push. We derived the coefficients in closed form, found their means and variances, added a normality assumption and got distributions, confidence intervals and hypothesis tests out of it. We rewrote everything in matrices, watched a projection appear, and proved via Gauss–Markov that this estimator is the best among linear unbiased ones. Then we paid our chi-squared debt using nothing but the geometry of that projection, built the F test on top of it, and showed that categories, interactions and curvature all fit inside the same framework.</p><p>The ideas that survive are worth naming: least squares is a projection; degrees of freedom count the dimensions it leaves over; a t test asks about one coefficient, an F test about several; significance is association, never causation; and linearity is a claim about parameters, not features.</p><p>What is left over is the bill. We assumed the design matrix has full rank and that the errors are independent, normal, and share a common variance — and we saw hints, in the widening spread of the first scatterplot and the belly in the residual plot, that reality does not always cooperate. Those assumptions and what to do when they fail are the subject of the next lecture, along with the problems that come with them: correlated errors, heteroscedasticity, leverage points, and collinearity.</p><p><em>Thanks for reading! If you found this useful, consider following for more articles on machine learning and statistical modelling.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f3bff91a1a4c" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Foundations of Statistical Learning: From Rules to Data, and What Makes an Estimate Good]]></title>
            <link>https://medium.com/@matnogueira/foundations-of-statistical-learning-from-rules-to-data-and-what-makes-an-estimate-good-5c0758114be3?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/5c0758114be3</guid>
            <category><![CDATA[mathematical-modeling]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Wed, 09 Sep 2026 19:10:16 GMT</pubDate>
            <atom:updated>2026-09-09T20:18:34.636Z</atom:updated>
            <content:encoded><![CDATA[<blockquote><em>Machine learning starts with a single inversion — stop writing the rules, learn them from the answers — and the whole first lecture is the story of what that inversion costs and how we pay for it.</em></blockquote><p><em>Every image and example was taken from the following sources:</em></p><ul><li><a href="https://www.youtube.com/watch?v=dLLw6QjADjo"><em>Lecture 1 — Introdução ao Curso / Noções gerais de aprendizado — IMPA Summer Course</em></a></li><li><a href="https://w3.impa.br/~pauloo/teaching/ml/"><em>Machine Learning 2026, Prof. Paulo Orenstein</em></a></li></ul><p><strong>Introduction</strong></p><p>These notes cover Lecture 1 of the <strong>IMPA (Institute for Pure and Applied Mathematics) Summer Course — Machine Learning 2026</strong> (Paulo Orenstein), one article per lecture.</p><p>This is the first article in the series, so there is nothing to read before it. Everything below is <strong>self-contained</strong>: the only background assumed is comfort with expectations, variance, and basic calculus.</p><p>The lecture runs one long argument from beginning to end. It starts with a paradigm shift — the move from writing rules to learning them — then makes that goal mathematically precise as estimating a target function <em>*f*</em>. From there it asks what we do with an estimate, how the methods that produce one split into two families, what we give up when we make them more flexible, how we tell a good estimate from a bad one, and what exactly the error we measure is made of. It closes by showing that the whole structure survives when the thing we want to predict is a category instead of a number.</p><p><strong>From rules to data: the paradigm shift</strong></p><p>For most of computing history, the arrangement was simple. A programmer writes rules. The program receives data. Applying the rules to the data produces answers.</p><p>You are relying on this right now. When you click an icon on your desktop and the program opens, it opens because someone wrote the rule connecting that click — the data — to that action — the answer. The same is true of a Breakout-style game, where every behaviour is spelled out by hand: if the ball collides with a brick, the brick disappears and the ball reverses direction.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/502/1*jxknrj0M0QpvPf66eJhX5w.png" /><figcaption><em>Figure 1: The rule is literally in the source: on collision, remove the brick and flip the ball’s vertical direction. Nothing about the game’s behaviour was learned — someone sat down and enumerated the cases.</em></figcaption></figure><p>This works beautifully until you hit a task where you know how to do something but cannot say how you do it. Driving a car, for instance. When do you turn? How do you react to a pigeon in the road, a pedestrian stepping off the curb, rain on the windshield? You do all of this competently and you cannot write it down as a finite list of conditions. Or take an easier-sounding task: deciding whether a photo contains a cat. Two ears? Plenty of things have two ears, and some cats in some photos show one. Whiskers? Sometimes invisible. Every feature you propose comes with exceptions, and the exceptions come with exceptions.</p><p>Machine learning inverts the arrangement. Instead of supplying rules and data to get answers, we supply <strong>data and answers</strong>, and the program’s job is to produce the rules.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/628/1*4sbmXsOE1iA4MVG73tkeOg.png" /><figcaption><em>Figure 2: The same three ingredients, rearranged. Rules and data go into traditional programming and answers come out; answers and data go into machine learning and the rules come out.</em></figcaption></figure><p>Stated formally, and this is the sentence the entire course unpacks: we have data X, we have answers Y, and we want to learn a function f such that</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/343/1*xQb4J4vcSbdk8KwcLiEvJw.png" /></figure><p>In words: find the rule that turns our inputs into our answers.</p><p>That single reformulation is what put machine learning everywhere. Consider satellite imagery, where each pixel carries four spectral bands. Those bands are X; the predicted land use for that pixel is Y. Nobody hand-codes the spectral signature of farmland versus water — the labels teach it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/839/1*4A_6TkrU4zdB3XMabuHNRA.png" /><figcaption><em>Figure 3: Four spectral bands of the same terrain are the input. Bottom middle is the true land usage, bottom right the prediction — close enough that the differences take a moment to find, and produced by a rule no person wrote.</em></figcaption></figure><p>Recommendation works the same way. Deciding what film to suggest next used to mean asking someone with taste; now a learning algorithm reads your history and decides.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1004/1*0gVIJw-Iv2j9ev2d3Himag.png" /><figcaption><em>Figure 4: Every row here — “Continue Watching”, “Trending Now” — is a learned rule at work: your viewing history is X, the ranked suggestions are Y.</em></figcaption></figure><p>The same framing also covers questions that are not really about prediction at all — relating demographic factors like age, calendar year, and education level to salary, for instance, where what we want is the shape of the relationship rather than a forecast.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/218/1*1wTUNSugvay6Cj5ChfSC8w.png" /><figcaption><em>Figure 5: Wage against age. The blue curve climbs steeply through the twenties and thirties, flattens in middle age, and turns down near retirement — the shape itself is the object of interest, not any single prediction from it.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/217/1*37LnhSQsjZ8hQyywkfJ3BQ.png" /><figcaption><em>Figure 6: Against calendar year the fit is a straight line with a gentle upward slope — a much weaker effect than age, but a real one.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/217/1*DAIVADan-Sym1n7zKBh6yQ.png" /><figcaption><em>Figure 7: Wage by education level, as five boxplots. The median rises with every step up in education, and so does the spread.</em></figcaption></figure><p>And it stretches all the way to generation. Text-to-image systems take a description and return a picture that matches it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cozj7I90pZy9tTaB2UuJsA.png" /><figcaption><em>Figure 8: Three images and the sentences that produced them — a Dalí portrait with a robotic half face, a shiba inu in a beret and black turtleneck, a corgi’s head as an exploding nebula.</em></figcaption></figure><p>Chatbots go further still, explaining why a meme about GPUs and VRAM is funny, even though what the algorithm actually receives is a grid of zeros and ones with no visual understanding attached to them.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/425/1*aG7j4PTnNC5PqkrmfPMCBA.png" /><figcaption><em>Figure 9: Asked “Why is that image funny?”, the model works out that the joke is the size difference between the GPU and its VRAM — from pixels alone.</em></figcaption></figure><p>Prediction, recommendation, inference, generation, conversation — the applications look wildly different, and every one of them is a version of learning f from X and Y.</p><p><strong>What is </strong><em>f</em><strong>? The target as a conditional expectation</strong></p><p>Saying “learn f such that f(X) ≈ Y” is a goal, not a definition. If we want to do mathematics with it, we need to know precisely what f <em>is</em>.</p><p>Take a concrete problem the lecture returns to repeatedly: predicting product sales from advertising spend across three channels. Here Y is sales — the <strong>response</strong>, or target. The predictors are X₁ for TV budget, X₂ for radio, X₃ for newspaper; together they form the <strong>feature vector</strong> X.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/177/1*tnH8zQvkt0VNnHnCgQNyig.png" /><figcaption><em>Figure 10: Sales against TV spend. The red points climb clearly along the blue fitted line — of the three channels, this is the strongest relationship.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/177/1*x4uEHhgqY-4RI_W3VJHr8Q.png" /><figcaption><em>Figure 11: Against radio spend the line still rises, but the points scatter much more widely around it.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/177/1*z4HjB6t27L95d01p3qSkIA.png" /><figcaption><em>Figure 12: Against newspaper spend the cloud is nearly shapeless and the fitted line is almost flat — whatever effect exists here is faint.</em></figcaption></figure><p>The working hypothesis about how the data are generated is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/454/1*VbfVgMKvw8rmbtuDT-6T6A.png" /></figure><p>where f is an unknown function and ε is independent noise with mean zero. Sales are driven by advertising through some fixed but unknown mechanism, plus randomness we cannot account for. The goal is to discover f.</p><p>A picture of exactly that decomposition, in one dimension, makes the pieces concrete.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/303/1*IRjXxo_EVgO9sz_yq9oHwg.png" /><figcaption><em>Figure 13: Income against years of education. The blue curve is the true f, the red points are the observations, and each black segment is one realisation of ε — the gap between what the mechanism produced and what was recorded.</em></figcaption></figure><p>Now take conditional expectations on both sides of the model, given X:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*j1z2SfUWJkAQDcg9paQ4VA.png" /></figure><p>Two facts do all the work. Once we condition on X, the quantity f(X) is no longer random — it is a fixed number — so its conditional expectation is itself. And the noise has mean zero, so its conditional expectation vanishes. What remains is a clean characterisation:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/625/1*JwlEuSa62kHuXUw0t3a73w.png" /></figure><p>In words: the function we are chasing is the average value of the response among all cases sharing that particular input. With three predictors it reads f(x₁, x₂, x₃) = 𝔼[Y | X₁ = x₁, X₂ = x₂, X₃ = x₃] — the mean sales among all campaigns with exactly that budget split.</p><blockquote>The target function is not an arbitrary curve through the data. It is the conditional mean of the response given the inputs.</blockquote><p>There is a second route to the same destination, and it is worth taking because it does not require the additive-noise hypothesis at all. Suppose we make no assumption about how Y was generated, and simply decide that a good predictor is one with small squared error. Then among all possible functions g, the one minimising expected squared error is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zlKfXVzt_4Gh9Ov8wuEpWw.png" /></figure><p>The argument is a short calculation. Expanding the square gives</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BffKp3lsH_DN5QMJ7HHqcA.png" /></figure><p>which is a quadratic in the number g(x). Differentiate with respect to g(x), set the derivative to zero, and out comes g*(x) = 𝔼[Y | X = x].</p><p>So the conditional mean is the best predictor under squared error whether you arrive at it by assuming Y = f(X) + ε or by simply choosing squared error as your criterion of success. Two different starting points, one answer.</p><p>In one dimension your eye can trace that curve straight off the scatter plot. The formalism earns its keep the moment there are more variables than a plot can hold — and that is the ordinary case, not the exception.</p><p>And now the catch that shapes everything after it. We do not know the distribution of Y given X. If we did, we would compute 𝔼[Y | X = x] and be finished. The target function is perfectly well defined and completely inaccessible; every method in this course is a strategy for estimating it from a finite sample.</p><p><strong>Prediction and inference: two reasons to want </strong><em>f</em></p><p>Before building estimators, it is worth asking what we intend to do with one. Write f̂ for our estimate of f. There are two distinct motivations, and they pull in different directions.</p><p>The first is <strong>prediction</strong>. Given a new input X, we compute ŷ = f̂(X) and use it. Spend 100 on TV, 20 on radio, nothing on newspaper — how much will we sell? Here f̂ is a machine for producing numbers, and nobody needs to look inside it.</p><p>The second is <strong>inference</strong>. Here f̂ is not a machine but an object of study, and the questions are about its structure. Which features actually matter? Is there evidence that f is linear? Is money spent on TV more profitable than money spent on radio? Does newspaper advertising do anything at all? These are second-order questions relative to prediction — they compare and explain effects instead of producing a forecast.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lnTjFptvPIkeZha89I0jdQ.png" /></figure><p>One clarification, since the word is overloaded: “inference” here means asking second-order questions about a fitted f̂. It is not the LLM sense of the word — running a trained model forward to produce output — which the course discusses separately later.</p><p>The distinction matters because methods are not neutral between these goals. A method that predicts superbly may tell you nothing about which features drove the prediction, and that trade-off has a section of its own below.</p><p><strong>Two families of methods: parametric and non-parametric</strong></p><p>Whatever the motivation, f̂ has to be learned from a finite sample: training data {(x₁, y₁), …, (xₙ, yₙ)}, where each xᵢ = (xᵢ₁, …, xᵢₚ)ᵀ collects p features for observation i.</p><p>The lecture offers a useful analogy for what this learning looks like. Nobody teaches a child what a tree is by supplying a definition — no rule set about bark, branching structure, and minimum height. You point at things and say “tree”, “not a tree”, over and over, and eventually the child gets it. Learning algorithms are in the same position: they see labelled examples and produce a rule.</p><p>There are two broad strategies for doing so. <strong>Parametric methods</strong> make a strong hypothesis about the form f takes. <strong>Non-parametric methods</strong> make no explicit hypothesis about its form at all. If the parametric hypothesis is approximately valid, parametric methods tend to win. With a lot of data, non-parametric methods can win instead, since no structure has to be imposed and the data alone decide the shape of the function.</p><p><strong>The parametric route: linear models</strong></p><p>The canonical parametric assumption is linearity:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rGvO-ebadpKO6Kn25ixCjQ.png" /></figure><p>with unknown coefficients β₀, …, βₚ ∈ ℝ. Look at what this buys us. Estimating an arbitrary function means searching an infinite-dimensional space; estimating this one means finding p+1 numbers. Prediction is then</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/833/1*DWtYowRT3-aRFLjt2pQuPQ.png" /></figure><p>Linear models are easy to interpret and easy to extend. A straight-line fit f̂(X) = betâ₀ + betâ₁ X may already be reasonable; adding a betâ₂ X² term can fit better still.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/527/1*xHvoMll6hay1vy9LFq1ddQ.png" /><figcaption><em>Figure 14: The linear hypothesis. The prediction at the query point x = 4 — the filled dot on the red line — comes from two estimated numbers and nothing else.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/529/1*cUxtcEa39_mol7yIWgWBvA.png" /><figcaption><em>Figure 15: The same data with a quadratic term added. The fit now bends to follow the dip near x = 3, and the prediction at x = 4 shifts accordingly.</em></figcaption></figure><p>But there is an obvious temptation here, and it is a trap. If X² helps, why not X³, X⁴, X⁵? Keep going and the curve will eventually pass exactly through every training point — and that, as we will see shortly, is precisely what we do not want.</p><p>The limits of the linear hypothesis show up clearly in a simulated example where the truth is known. Take a surface generating income from two variables, `income = f(education, seniority) + ε`.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/520/1*3HU30Av_VcBAgIVKY8n6Fw.png" /><figcaption><em>Figure 16: The true f, visible only because the data are simulated. Income rises with education along a pronounced S-shaped ridge, while seniority tilts the surface much more gently. The red points are the observations, each hanging off the surface by its own error.</em></figcaption></figure><p>Fit a linear regression plane to data from this surface, and the fit gets the broad story right: more education raises income, and seniority matters less. What it cannot do is bend.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/524/1*zTKunIFx0fWgOaxu72ZqjA.png" /><figcaption><em>Figure 17: The fitted plane. It captures the direction of both effects and flattens the ridge completely — exactly the failure you should expect when a flat model meets a curved truth.</em></figcaption></figure><p><strong>The non-parametric route: local averaging</strong></p><p>The other strategy starts from the characterisation we derived earlier. If f(x) = 𝔼[Y | X = x] is a population average, then approximate it with a sample average — over the observations near x:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/732/1*ZB4kPNvIwpOp3QrOPMpSOg.png" /></figure><p>where N(x) is a neighbourhood of x. To predict at a point, look at the training points nearby and average their responses. No functional form is assumed anywhere.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/669/1*U6DypvUgQNmqnmLYwjiXcA.png" /><figcaption><em>Figure 18: The same query point x = 4, now handled locally. The dashed red lines bound the neighbourhood, the green dot is the average of the responses inside it, and the green curve traces that average across the whole range.</em></figcaption></figure><p>This works well when the number of features p is small and the number of observations n is large — say p ≤ 4 and n ≥ 1000. When p is large it performs poorly, a failure we will return to in later lectures.</p><p>More sophisticated non-parametric methods follow the same philosophy. A thin-plate spline imposes no pre-specified form for f either, and because the data decide the shape, it can capture what the plane could not.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/567/1*-JbSfDXIqO7Smn2_w66pCg.png" /><figcaption><em>Figure 19: The spline fit to the same income data. The ridge is back, and the surface passes close to nearly every red point — recovered from the data alone, with no one telling the method that a ridge was there.</em></figcaption></figure><p>Freedom has a price, though, and here it is the neighbourhood size. Make the neighbourhood too wide and every prediction approaches the global average: the fitted curve flattens out and stops responding to x at all. Make it too narrow and the curve clings to individual data points, wobbling up and down, until in the extreme it interpolates every point perfectly.</p><p>That last sentence should sound familiar. It is the same failure as the runaway polynomial, reached from the opposite direction. Both families can destroy themselves with excess flexibility, which is exactly the trade-off the lecture turns to next.</p><p><strong>Flexibility, interpretability, and what we give up</strong></p><p>The lecture names three trade-offs explicitly, and they are related but not identical.</p><p>The first is <strong>fair fit versus under-fit or over-fit</strong>: how do we know when a fit is good? The second is <strong>prediction accuracy versus interpretability</strong>: linear models are simple to interpret, thin-plate splines are not. The third is <strong>parsimony versus black box</strong>: a preference for simple models with few variables over complex models with many.</p><p>The second of these is not a minor technical footnote. Modern large language models are extraordinarily capable and correspondingly hard to interpret or control precisely — even the people building such systems may be unable to guarantee that a specific behaviour will change. “I can’t”, as the honest answer goes.</p><p>Compare that to linear regression, where interpretation is nearly mechanical. Want to know what happens to predicted salary if years of education increase slightly? The coefficient β₁ is the answer, directly and quantitatively. Ask the same question of a spline or a nearest-neighbours estimate and there is no coefficient to read — you have to probe the fitted function and reason about what it does locally.</p><p>Parsimony has a similar flavour. In a genetic panel used to predict cancer risk, thousands of variables are measured and only some are believed to be genuinely relevant. Finding which ones matter is already hard; it gets harder as the model gets more complex. A model with few variables is not merely more elegant — it is more answerable.</p><p>Putting methods on a plane with flexibility running left to right and interpretability running bottom to top produces a fairly consistent ordering.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/731/1*V15AY-WkWP-iAmIUNUcA2w.png" /><figcaption><em>Figure 20: Subset Selection and Lasso sit in the interpretable, inflexible corner, with Least Squares just below them. Generalized Additive Models and Trees occupy the middle. Bagging, Boosting, Support Vector Machines and Deep Learning fall to the flexible, opaque corner — the diagonal runs almost unbroken.</em></figcaption></figure><p>The relationship is roughly inverse, and it explains the shape of the course itself. The first half stays in the linear-model world — with extensions like splines and generalized linear models — precisely because that world remains interpretable. The second half moves into the high-flexibility, state-of-the-art methods that are much harder to understand from the inside.</p><p>Which leaves the first trade-off still open: how do we tell a good fit from a bad one?</p><p><strong>Measuring accuracy: training MSE, test MSE, and overfitting</strong></p><p>For a real-valued response, quality is measured by mean squared error,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/751/1*gLnWsa5BZosvrn-YbWGHiw.png" /></figure><p>the average squared gap between what happens and what we predicted. We want it small.</p><p>We cannot compute it. The expectation is over the distribution of Y given X, which is the very thing we do not know. The obvious workaround is to approximate it with the data we have — the training set Tr = {(xᵢ, yᵢ)}ᵢ₌₁ⁿ:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/869/1*lIUrLl0cFbMAHMl1BUxWCA.png" /></figure><p>And this is where it goes wrong. Minimising the training MSE on its own leads straight to overfitting: a function that passes exactly through every training point drives it to zero, a perfect score, while generalising terribly. The polynomial with too many powers and the neighbourhood shrunk too small both get there.</p><p>Return to the child learning what a tree is. How would you check that the learning actually happened, rather than five specific pictures being memorised? You would not show the same five pictures again. You would find a tree the child has never seen and ask.</p><p>That is the whole idea of a test set. Hold out data Te = {(xᵢ, yᵢ)}ᵢ₌ₙ₊₁ᵐ, never used in fitting, and evaluate on it:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*erIDWG1jDTY_SuXO0Zq7gQ.png" /></figure><blockquote>Training error measures memorisation. Test error measures learning.</blockquote><p>The behaviour of the two errors as flexibility increases is the central picture of the lecture. Take a fixed true f and fit three models of increasing flexibility — call them orange, blue, and green.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/320/1*JiMSlnFtPm9j-kQlGny_PA.png" /><figcaption><em>Figure 21: The black curve is the truth. The orange straight line is too rigid to follow it; the green curve chases individual points and invents wiggles that are not there; the light blue curve sits almost on top of the truth.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/320/1*V94_vel56riM1Ckuq2K4Zw.png" /><figcaption><em>Figure 22: The same three fits scored, with flexibility on the horizontal axis. Grey is training error, falling forever; red is test error, U-shaped. The three squares mark orange, blue and green — and the blue square sits at the bottom of the red curve.</em></figcaption></figure><p>Read the three fits off those curves. Orange is inflexible and has high error on both training and test data: it underfits, and no amount of held-out data will rescue it because the model class simply cannot represent the truth. Green nearly interpolates, so its training error is tiny and its test error is large: it overfits. Blue does well on both and is the one to choose — and note that the training curve alone would never have told you that, since it keeps improving all the way to green.</p><p>How much flexibility is right depends on something we cannot see: the true f. When f is smooth, an inflexible model close to a straight line already does well, and adding flexibility buys almost nothing.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/312/1*uedJ5CSdVuh886uDKaP9tw.png" /><figcaption><em>Figure 23: A nearly straight truth in black. The orange linear fit lies right along it, while the green flexible curve oscillates around it — extra flexibility spent on noise.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/312/1*oL0f7r8uz2Z_uoq8a6SOOw.png" /><figcaption><em>Figure 24: The corresponding errors. The red test curve is already near its minimum at the orange and blue squares and climbs steeply afterwards — flexibility is pure cost here.</em></figcaption></figure><p>When f is less smooth — wiggly, with structure at a finer scale — the story reverses. The inflexible linear model performs poorly, and more flexible fits cut test error substantially at comparatively small cost.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/309/1*cHVG_ml2AnYyAKqtFhAAbQ.png" /><figcaption><em>Figure 25: A truth with real structure — a drop, a plateau, another drop. The orange straight line cuts across all of it, while the flexible fits follow the black curve closely enough to be hard to distinguish from it.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/309/1*qNgpNrPuGolk5-BwZqiwcg.png" /><figcaption><em>Figure 26: Here the red test curve plunges as flexibility increases and stays low, and the orange square sits far above the rest — the opposite verdict from the smooth case, from the same procedure.</em></figcaption></figure><p>So the right amount of flexibility depends on how complex the true f actually is, which is never known in advance. That is not a defeat — it is the reason model selection exists as a discipline, and the reason test data are not optional.</p><p><strong>What the error is made of: bias, variance, and the irreducible part</strong></p><p>The U-shaped test curve is an empirical fact. The next question is why it is shaped that way, and the answer comes from decomposing the error exactly.</p><p>Fix the setup. We have f̂ trained on training data, and a new point (x*, y*) with y* = f(x*) + ε*. The trick is to add and subtract 𝔼[f̂(x*)] — the average prediction our method would make at x* across different training sets — inside the square. Everything falls out into three pieces:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dZRiu1m9Qxp3obPijxh-xQ.png" /></figure><p>where</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qaMUM5kYjpysp0oHTp3Xjw.png" /></figure><p>A word on what the expectation runs over, because it is easy to get this wrong. It is over y* and over the randomness in the training responses yᵢ — that is, over ε₁, …, εₙ, ε*. The feature values xᵢ and x* are held fixed throughout this course.</p><p>Now read the three terms.</p><p>𝕍[ε] is the <strong>irreducible error</strong>. It does not depend on f̂ in any way. Choose a better method, gather more data, think harder — you still pay it. It reflects the intrinsic difficulty of the problem, the part of Y that X simply does not determine.</p><p>The other two terms form the <strong>reducible error</strong>, and these are the ones our choices affect.</p><p><strong>Variance</strong>, 𝕍[f̂(x*)], measures how much the prediction would change if we had drawn a different training set. A method that sticks closely to its training data has high variance: shift the data slightly and the fitted function moves a lot.</p><p><strong>Bias</strong>, bias(f̂(x*)), measures the error from approximating f with an f̂ that structurally cannot capture its nuances. A linear method estimating a genuinely non-linear f has high bias — no training set will fix it, because the plane cannot bend.</p><blockquote>Flexibility does not reduce error. It moves error from bias into variance.</blockquote><p>That is the mechanism behind the U-shape. More flexible methods have lower bias and higher variance; less flexible methods have higher bias and lower variance. The test curve falls while bias reduction dominates and rises once variance takes over. And it is why no single method is superior to every other — the right balance depends on the true f, which nobody gets to see.</p><p><strong>Classification: the Bayes classifier and k-nearest neighbours</strong></p><p>Everything so far assumed Y ∈ ℝ. Plenty of problems are not like that. Tomorrow’s weather is rain, cloudy, or sun. A tumour is one of several cancer types. A recommender picks one of 328 films. These are <strong>qualitative</strong> responses, and their categories generally have no natural ordering — there is no sense in which cloudy is halfway between rain and sun. The lecture works in the binary case, Y ∈ {yes, no}, which we identify with Y ∈ {0, 1}.</p><p>Mean squared error stops being meaningful here, and the reason is worth stating plainly: the coding is arbitrary. Label the classes {0, 1} and you get one error magnitude; label them {0, 10} and you get a different one, for the identical set of mistakes. A measure that depends on an arbitrary choice of labels is not measuring the problem.</p><p>What does make sense is counting mistakes. The error rate is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/398/1*HJOnKv0UWuke9ThAhozdkw.png" /></figure><p>the fraction of observations classified incorrectly. The same train/test split applies as before: the test error rate</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/546/1*kaA-PqH1wBDhgMqTKa2gMw.png" /></figure><p>approximates the population quantity</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/789/1*8KnDVNRurF5q8CjjqrwLMA.png" /></figure><p>the probability of misclassifying a new point.</p><p>Now we can ask the question that produced the conditional expectation in the regression case: what is the best possible rule? Conditioning on X = x,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fHh-Qyd_xhicaSrSIlA37g.png" /></figure><p>so minimising the error rate is exactly the same as maximising the probability of being right. If we knew the distribution of Y given X, the ideal rule would be</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1023/1*XVgN2hnUrC_5rmuhzsAjVQ.png" /></figure><p>that is: predict 1 when P[Y = 1 | X = x] &gt; 0.5, and 0 otherwise. This is the <strong>Bayes classifier</strong>, and it occupies the same position in classification that the conditional expectation occupies in regression — theoretically optimal, generalising cleanly to more than two classes, and completely inaccessible, because we do not know Y | X.</p><p>Its error rate is</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/817/1*WBM6MfxhXj5bHYa70P5BRw.png" /></figure><p>which is the irreducible error of the classification problem. Even the best possible classifier misclassifies at this rate, because the label carries intrinsic randomness given X.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/414/1*BJ7L4JEF-FacGn3Y_Gb6eA.png" /><figcaption><em>Figure 27: Two classes, orange and blue, with the Bayes boundary as the dashed purple curve and the shaded background showing how it partitions the plane. Note the orange points stranded deep in blue territory — misclassified by the optimal rule itself. That is irreducible error, drawn.</em></figcaption></figure><p>Inaccessible targets call for estimates, and the estimate here is the classification cousin of local averaging. Replace the unknown P[Y = j | X = x] with the proportion of nearby training points in class j:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wvY8TE3JtDavLZBgoaeKMg.png" /></figure><p>where N holds the k training points closest to x. Which is to say: take a majority vote among the k nearest neighbours.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/465/1*9fwZ_iOKQGVXn7w6JT4J2g.png" /><figcaption><em>Figure 28: On the left, the neighbourhood around the query point (the cross) holds two blue neighbours and one orange, giving an estimated probability of 2/3 for blue. On the right, running that vote at every point of the plane produces a decision boundary.</em></figcaption></figure><p>It works remarkably well. With k = 10 on the lecture’s simulated example, the kNN boundary closely resembles the Bayes boundary — a rule built from nothing but local vote counts recovering the optimal rule’s shape.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/378/1*M0ncEaX7W6QOPG9EQ6nWJA.png" /><figcaption><em>Figure 29: The solid black kNN boundary at k = 10 against the dashed purple Bayes boundary. It is rougher, but it turns where the optimal boundary turns.</em></figcaption></figure><p>And the choice of k reproduces exactly the trade-off we have been circling all along. With k = 1, every region of the plane is decided by a single nearest point: the boundary becomes wildly irregular and unstable, which is high variance and overfitting. With k = 100, the vote is taken over so wide an area that the boundary smooths out and misses the true curvature — high bias, underfitting.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/710/1*WFJ1WmHlYwWSxSeitjdWyQ.png" /><figcaption><em>Figure 30: k = 1 on the left, k = 100 on the right, with the dashed purple Bayes boundary in both. The left boundary is jagged and encircles individual points; the right one is a smooth sweep that ignores the curve it should be following.</em></figcaption></figure><p>The parameter has a different name and the loss has a different form, but the tension is the one from the bias-variance decomposition, unchanged.</p><p><strong>Conclusion</strong></p><p>The lecture covers a lot of ground and it is really one continuous argument.</p><p>Machine learning inverts classical programming: rather than writing rules that turn data into answers, we supply data and answers and learn the rules. Made precise, the rule we are after is the conditional expectation f(x) = 𝔼[Y | X = x] — best predictor under squared error, whether justified by an additive-noise model or by the choice of loss alone — and it is unknowable, so it must be estimated. We estimate it either by assuming a form and fitting parameters, or by declining to assume and letting local data decide, and either strategy can be pushed until it merely memorises the training set. Which is why accuracy is judged on held-out data, where the test error curve is U-shaped, and why that shape has an exact explanation: expected error at a point is irreducible noise plus squared bias plus variance, with flexibility trading the second against the third. When the response is a category rather than a number, the loss becomes the error rate, the optimal rule becomes the Bayes classifier, kNN estimates it from data, and the choice of k recreates the same trade-off.</p><p>Two ideas underlie the whole course from here. The thing we want is a conditional feature of the distribution of Y given X — an expectation for regression, a maximiser for classification — and it is never directly available. And every method for approximating it sits somewhere on a spectrum trading structure against flexibility, bias against variance, prediction against interpretation.</p><p>The lectures ahead spend their first half in the interpretable, linear-based world, extending it with splines and generalized linear models, before moving to the flexible and far more opaque methods that define the current state of the art.</p><p><em>Thanks for reading! If you found this useful, consider following for more articles on machine learning and statistical modelling.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5c0758114be3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Time Series EDA — a visual exploration of seasonality]]></title>
            <link>https://medium.com/@matnogueira/time-series-eda-a-visual-exploration-of-seasonality-8385d08f9a7d?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/8385d08f9a7d</guid>
            <category><![CDATA[seasonality]]></category>
            <category><![CDATA[exploratory-data-analysis]]></category>
            <category><![CDATA[data-visualization]]></category>
            <category><![CDATA[time-series-analysis]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Wed, 03 Jun 2026 18:31:56 GMT</pubDate>
            <atom:updated>2026-06-03T18:31:56.869Z</atom:updated>
            <content:encoded><![CDATA[<h3>Time Series EDA — a visual exploration of seasonality</h3><p><em>Using ten years of Rio de Janeiro temperatures to hunt for daily, weekly, and annual patterns — one chart at a time.</em></p><h3><strong>Introduction</strong></h3><p>There’s a temptation in time series analysis to jump straight to decomposition, modelling, and forecasting. I’ve done it. The problem is that if you skip the exploratory work — or rush it with a single time-series plot at one resolution — you can easily miss entire seasonal structures hiding in plain sight. As my first time series professor, Cris, once put it: for seasonal series, get the seasonal structure right and even the most naive model can surprise you.</p><p>This article is a visual walkthrough of that exploration. We use ten years of 2 m air temperature in Rio de Janeiro <a href="https://open-meteo.com/en/docs/historical-weather-api">2015–2024, Open-Meteo ERA5 — [Historical Weather API docs</a>) as a running example. Rio is instructive: the physics are clean, the signal is strong, and we have the same variable at three granularities — hourly, daily, and monthly — which means we can go looking for <strong>daily</strong>, <strong>weekly</strong>, and <strong>annual</strong> seasonality with the right tools for each.</p><p>The goal is not a gallery of charts. Each figure is a different way of asking the same few questions: does temperature repeat <strong>within the day</strong> (across hours), <strong>within the week</strong> (across weekdays), and <strong>within the year</strong> (across months and seasons)? We stay visual throughout; formal tests and models come later.</p><h3><strong>Multiple Seasonalities: A Quick Framing</strong></h3><p>First things first, <strong>what is seasonality?</strong> Let me give you two answers — one intuitive, one more precise.</p><p>Intuitively, seasonality is just a pattern that repeats on a predictable calendar rhythm: hotter summers, quieter Sundays, busier Decembers. More precisely, a time series is seasonal when it exhibits periodic fluctuations with a fixed and known period <em>m</em> — the number of observations in one full repetition of the pattern. Both definitions point to the same idea: structure you can anticipate, if you know where to look. It is important to say that periodic pattern does not mean a deterministic pattern. The seasonal component itself can, and commonly is, stochastic — aka random — but the period <em>m</em> must be fixed.</p><p>In <em>Forecasting: Principles and Practice</em> (3rd ed.), Hyndman and Athanasopoulos describe <strong>multiple seasonal patterns</strong> as the presence of more than one repeating seasonal structure in the same series — and they note it is routine in high-frequency data. Their electricity-demand example from Australia shows all three at once: lower use overnight, different weekday vs. weekend behaviour, and higher demand in summer and winter. Surface temperature in Rio is a different variable, but the logic is the same. We should look for <strong>daily</strong>, <strong>weekly</strong>, and <strong>annual</strong> seasonality, and we should expect some to be strong and others simply absent.</p><p>One naming point matters before we go further. In FPP’s sense, <strong>daily seasonality</strong> does <em>not</em> mean “the whole series wiggles every calendar day in the long-run plot.” It means the pattern <strong>across hours within a day</strong> — cool before sunrise, warm in early afternoon — that repeats day after day. That is the profile you can only see clearly when you work with hourly data or hour-of-day summaries.</p><p>The <strong>seasonal period</strong> <em>m</em> counts how many observations fit in one full repeat of a pattern. Crucially, <em>m</em> depends on the granularity of the series — we are not changing the physics when we switch between CSVs, only changing how many data points fall inside one seasonal period:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*mfbGg00V2o8iXevw97Eang.png" /></figure><h4><strong>The dataset</strong></h4><p>Three series from the same ERA5 reanalysis, all measuring `temperature_2m_c`:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5V2zFNP7443jMgvxRA5DEA.png" /></figure><p>We move from wide views of the full series toward plots that isolate each seasonal scale. Heatmaps show <strong>means</strong> across two calendar dimensions; boxplots add <strong>spread</strong> by hour, weekday, and month; profile plots show the typical seasonal shape and how much it varies from year to year or day to day.</p><h3><strong>Setup and Reproducibility</strong></h3><p>All the code and data download scripts live in the companion repository on GitHub: <a href="https://github.com/MathNog/MathNogMediumArticles/tree/main/TimeSeriesEDA/SeasonalityGraphs">SeasonalityGraphs </a>project folder. The README walks you through the environment setup, how to fetch the Open-Meteo ERA5 data, and how to regenerate all 18 figures in one command.</p><h3><strong>First Look: Three Resolutions</strong></h3><p>The natural starting point is to plot the entire series at three granularities — hourly on top, daily in the middle, monthly at the bottom.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/879/1*eZkEmOBA2v1J0diM6cqjRw@2x.png" /><figcaption><strong>Figure 1.</strong> The same ten years at three levels of aggregation. The hourly panel is dense and almost unreadable; the daily panel shows ten clean warm-cool swings; the monthly panel is a smooth seasonal skeleton.</figcaption></figure><p><strong>Annual seasonality</strong> jumps out immediately. On the daily series (<em>m</em> = 365), you can count ten warm–cool swings — austral summers (December–February) high, June–August low. The monthly series (<em>m</em> = 12) strips almost everything else away and leaves that annual rhythm alone. You can see that even though the repetition is clear, the seasonal pattern is not deterministic; it is stochastic. This means that every summer is warmer then every winter, but how much warmer changes over the years.</p><p><strong>Daily seasonality</strong> (<em>m</em> = 24, hourly data only) does <strong>not</strong> show up here. The top panel looks like textured noise at this zoom; you cannot read the hour-of-day profile from a decade-long hourly trace. That is the main lesson of Fig. 01: a single time-series plot at full length is well-tuned for <strong>annual</strong> structure, and poorly suited for everything else. To investigate <strong>daily</strong> seasonality you need plots that explicitly aggregate by hour — heatmaps, boxplots by hour, and hourly profiles. Let’s build those next.</p><h3><strong>Two Calendar Dimensions at Once</strong></h3><p>A line plot shows <em>when</em> temperature moves. A heatmap shows <strong>two</strong> calendar axes simultaneously: the mean temperature in each cell. On hourly data, that is where <strong>annual</strong> and <strong>daily</strong> seasonality appear side by side — and where you start to notice that their interaction is not trivial.</p><h4><strong>Hour × Month — Fig. 02</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/753/1*hMw1zoV6tHQ3WFX-6FmqPA@2x.png" /><figcaption><strong>Figure 2.</strong> Hottest cells: late morning to early afternoon (hours 11–14) in Jan-Feb and Dec. Coolest: pre-dawn hours in July and August. Read horizontally for annual seasonality; read vertically for the daily cycle.</figcaption></figure><p>Read along a horizontal band at a fixed hour: temperature rises and falls with the month — <strong>annual seasonality</strong> on hourly data (here collapsed to month-of-year for readability). Read up a column at a fixed month: cool before sunrise, peak around noon–2 pm, cooler in the evening — <strong>daily seasonality</strong> (<em>m</em> = 24).</p><p>Now compare January and July. In January, the gap between the 6 am row and the noon row is large; in July, it is noticeably smaller. The <strong>shape of the daily cycle</strong> is not the same in every month — hour and month interact in a way that a simple additive decomposition will not capture perfectly. Worth keeping in mind when you move to modelling.</p><h4><strong>Hour × Weekday — Fig. 03</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/753/1*u0lq_KxVCYRgeWkGgzrYCw@2x.png" /><figcaption><strong>Figure 3.</strong> Seven parallel columns showing the same daily profile every day of the week. There is nothing to find here, and that is already informative.</figcaption></figure><p>We include <strong>weekly seasonality</strong> (<em>m</em> = 168 on hourly data) for completeness, but the answer is essentially foregone: there is no physical reason Sunday should be systematically warmer than Wednesday. The atmosphere does not follow the calendar week. The heatmap matches that expectation — all seven columns show the same gradient from cool pre-dawn to warm midday. Nothing shifts from Monday to Sunday. <strong>Weekly seasonality is absent</strong>, and we will see the same flat story repeated in every subsequent plot that touches weekdays.</p><h3><strong>Seasonal Levels and Spread by Calendar Feature</strong></h3><p>Heatmaps show <strong>means</strong> only. A warm average at noon could mean every day is consistently warm at noon, or it could hide a mix of very hot and mild days. Boxplots let us read both the typical <strong>level</strong> (median) and the <strong>spread</strong> (IQR, whiskers) by calendar category — the same three seasonal questions, now with an extra layer of information about variability.</p><h4><strong>By Hour of Day — Fig. 04</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w8V5d9pfnLEwTi8VVQ_AiA@2x.png" /><figcaption><strong>Figure 4.</strong> Medians rise from ~22 C in the early hours to 26–27 C at midday, then fall. Midday boxes are also visibly wider, so afternoons are hotter and more variable.</figcaption></figure><p><strong>Daily seasonality</strong> is strong and easy to read. Early-morning hours (2–6 am) have tight boxes — consistently cool. Midday hours (11–15) have wider IQRs and longer upper whiskers — not only warmer on average, but more variable from day to day. The spread is itself a feature of the daily cycle, not just the level. Remenber that those boxplots are showing the hourly distribution of the full years. One interesting extra step could be to divide this plot into 4 seasons to answer the question: does the daily variability changes across seasons? Perhaps during the summer the days are consistenly warmer. Perhaps winter temperatures changes a lot during the day.</p><h4><strong>By Weekday — Fig. 05</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/888/1*Cd_ni3c6elJb4UUj6GIC2Q@2x.png" /><figcaption><strong>Figure 5.</strong> Seven nearly identical boxes. No weekday effect.</figcaption></figure><p>The expected null: <strong>no weekly seasonality</strong>. All seven days sit on top of each other. A weekday dummy in a temperature model would add nothing.</p><h4><strong>By Calendar Month — Fig. 06</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/888/1*7Mzy2TGqVTQy3ysQJYhTXA@2x.png" /><figcaption><strong>Figure 6.</strong> Jan-Feb highest (~26–27 C median); Jul-Aug lowest (~21 C). A ~5–6 C swing from summer peak to winter trough, with wider boxes in summer.</figcaption></figure><p>The median steps smoothly through the year — classic southern-hemisphere <strong>annual</strong> pattern. Summer months have wider IQRs than mid-winter; Rio is not only hotter in summer but also more variable day to day. Winter months (especially July–August) are narrower and more predictable. That asymmetry in spread matters: it shows up later in the profile plots too.</p><h3><strong>Typical Seasonal Profiles</strong></h3><p>Now we plot the <strong>average seasonal shape</strong> for each scale and overlay many realisations — one line per year, or per day — along with a P10–P90 envelope and a red mean line.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*SbOhSlKQpZItVPfVGicVSA.png" /></figure><p>A wide envelope means the seasonal pattern is stable in <em>shape</em> but not in <em>level</em> from one year or day to the next.</p><h4><strong>Annual Profile by Month — Fig. 07</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/888/1*7nf2tyy39xB-U-o1gOdmtw@2x.png" /><figcaption><strong>Figure 7.</strong> Ten lines (one per year): warm Jan-Feb, trough in July, recovery toward December. The bundle is tight in winter and slightly wider at the summer peak.</figcaption></figure><p>Built from hourly data aggregated to mean temperature by calendar month. This is a clean picture of <strong>annual seasonality</strong> — <em>m</em> = 12 at monthly resolution. The ten years are nearly parallel: same timing, similar amplitude. The summer peak is where years diverge most, consistent with the boxplot in Fig. 06. Notice that the spread of the P10-P90 interval follows perfectly the spread of the boxplots in the previous figure (look at March and October for a clear comparison).</p><h4><strong>Daily, Weekly, and Annual Profiles — Fig. 08</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*iQc2uKl8U1iI3nGExGDisg@2x.png" /><figcaption><strong>Figure 8.</strong> Three panels side by side: the daily pattern (strong), the weekly pattern (flat), and the annual pattern on daily data (ten overlapping years).</figcaption></figure><p><strong>Top panel — daily seasonality (</strong><em>m</em><strong> = 24):</strong> Mean temperature rises from ~22°C pre-dawn to ~26–27°C between 11 am and 2 pm, then falls. The envelope is tight at 6 am and fans out by noon (nearly 15°C between P10 and P90) — many days share the same <em>timing</em> of the peak even when they disagree on how hot the afternoon gets.</p><p><strong>Middle panel — weekly seasonality:</strong> The mean is a flat line. The envelope is two horizontal bands. No day of the week stands out. This panel is the formal version of what we already called obvious.</p><p><strong>Bottom panel — annual seasonality (</strong><em>m</em><strong> = 365 on daily data):</strong> Ten years on the day-of-year axis follow the same broad arc — hot at the year edges (austral summer), cool around days 190–210 (winter) — but they cross more than the monthly profile in Fig. 07 does, because daily values carry more within-year weather noise. Year-to-year differences become hard to separate from day-to-day variability; still, every year follows the same gross shape. Some years run hotter or cooler in particular summers or winters, but the seasonal clock stays put. If you wish to explore long term patterns, such as temperature changes over the year, maybe a high frequency data is not ideal, since it may introduce “noise” from other high frequency patterns.</p><h3><strong>Does the Annual Pattern Repeat Across Years?</strong></h3><p>The bottom panel of Fig. 08 already shows the annual structure on daily data. Here we zoom in with a cleaner view — one point per month, one line per year — to get a sharper picture of year-to-year consistency.</p><h4><strong>Monthly Resolution — Fig. 09</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/889/1*QY7irZvGH8nC7qU1u7-JAQ@2x.png" /><figcaption><strong>Figure 9.</strong> Ten years, one point per month. Same V-shape every year; summers spread more, winters cluster tightly.</figcaption></figure><p>With monthly aggregation (<em>m</em> = 12), the picture is clean. All ten years follow the same <strong>annual</strong> rhythm. Winters cluster closely; Jan–Feb separate more across years (~4°C between the warmest and coolest in this window). The divergence is mainly in <strong>amplitude</strong> at the summer peak — not in <em>when</em> the trough arrives. July or early August stays the coldest slice across all years.</p><h3><strong>Zooming Inside Calendar Strata</strong></h3><p>Subseries plots fix one calendar bucket — a month, or a weekday — and show the series through time inside that stratum. They answer a natural follow-up question: once we know the global <strong>annual</strong> or <strong>weekly</strong> structure, what does a single slice actually look like up close?</p><h4><strong>Twelve Months — Fig. 10</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*f4R3FkEgpmIQTAKkUW3SQQ@2x.png" /><figcaption><strong>Figure 10.</strong> One panel per month: daily values from 2015–2024 with the cross-year mean as a red dashed line. January is clearly warmer than July.</figcaption></figure><p>Each panel is a slice of <strong>annual seasonality</strong>: January’s mean sits well above July’s. Within a given month, points scatter around the dashed mean without a clear decade-long slope in most panels — but January and other summer months span a noticeable range between years. In this daily-based view, that spread is already clear: the summer months are visibly wider year to year than winter, and January varies by roughly 4°C across the 2015–2024 window. Modelling “January in Rio” as a fixed seasonal level plus noise would miss real interannual movement in the warm season.</p><h4><strong>Seven Weekdays — Fig. 11</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WhjmdHL6XM6vaVX4WmwRpA@2x.png" /><figcaption><strong>Figure 11.</strong> Seven panels of daily averages by weekday, with identical red means and the same annual ups and downs in every panel.</figcaption></figure><p>The clearest restatement of the obvious: Monday, Wednesday, and Saturday share the same mean level and the same decade of <strong>annual</strong> ups and downs. <strong>Weekly seasonality</strong> has nothing to add here. Calendar weekday dummies would be dead weight for this variable.</p><h3><strong>What We Learned</strong></h3><p>Rio’s 2 m temperature from 2015–2024 is a clean example of <strong>multiple seasonalities</strong> with very unequal strength.</p><p><strong>Annual seasonality</strong> dominates the narrative whenever we step back from hourly noise — roughly 5–6°C from the winter median to the summer median, stable in <em>timing</em> across years but with meaningful spread in hot months (up to ~4°C between years in Jan–Feb). On daily data <em>m</em> = 365; on monthly data <em>m</em> = 12.</p><p><strong>Daily seasonality</strong> is equally real but <strong>invisible on a long hourly plot</strong> (Fig. 01). It appears once we aggregate by hour: a ~4–5°C swing from pre-dawn to afternoon in the mean profile, with a wider envelope at midday and a larger daily amplitude in summer than in winter (Figs. 02, 04, 08). On hourly data <em>m</em> = 24.</p><p><strong>Weekly seasonality</strong> is absent — as physics would predict. Heatmaps, boxplots, profiles, and subseries all tell the same flat story. A <em>m</em> = 168 or <em>m</em> = 7 term would not earn a place in a model here.</p><p>Spread matters too: afternoons and summer months are more variable, not just warmer. Hour and month interact slightly in the daily shape (Fig. 02). None of this replaces formal inference, but it narrows what a forecaster should plan for: at least <strong>daily</strong> and <strong>annual</strong> seasonal structure on hourly data, no <strong>weekly</strong> term, and interannual variation in summer levels that is worth modelling explicitly.</p><blockquote><em>Investigating multiple seasonalities takes multiple visualization techniques. A plain time-series plot of the full series is the one view most likely to hide high-frequency structure — and the visual noise in dense hourly data can make us overlook daily or weekly patterns altogether.</em></blockquote><h4><strong>Next steps</strong></h4><p>This article stays exploratory and graphical. Natural follow-ups — aligned with FPP3 Chapter 12 on complex seasonality — include:</p><ul><li><strong>MSTL / multiple-seasonal decomposition</strong> on hourly data to separate daily and annual components simultaneously</li><li><strong>ACF at seasonal lags</strong> — 24, 168, and 8766 for hourly series; 7 and 365 for daily</li><li><strong>Dynamic harmonic regression</strong> (Fourier terms) or <strong>TBATS</strong> for multiple seasonal periods</li></ul><p>The next piece in this series moves from “we can see the structure” to “we can estimate and forecast it.”</p><ul><li>Code and data:</li></ul><p><a href="https://github.com/MathNog/MathNogMediumArticles/tree/main/TimeSeriesEDA/SeasonalityGraphs">MathNogMediumArticles/TimeSeriesEDA/SeasonalityGraphs at main · MathNog/MathNogMediumArticles</a></p><ul><li>Data: Open-Meteo ERA5</li></ul><p><a href="https://open-meteo.com/en/docs/historical-weather-api">🏛️ Historical Weather API | Open-Meteo.com</a></p><ul><li>Forecasting Principles and Practice</li></ul><p><a href="https://otexts.com/fpp3/">Forecasting: Principles and Practice (3rd ed)</a></p><p><em>Thanks for reading! If you found this useful, consider following for more articles on time series analysis and forecasting.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8385d08f9a7d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What Does It Mean to Model a Time Series?]]></title>
            <link>https://medium.com/@matnogueira/what-does-it-mean-to-model-a-time-series-b1ca114773e3?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/b1ca114773e3</guid>
            <category><![CDATA[time-series-forecasting]]></category>
            <category><![CDATA[mathematical-modeling]]></category>
            <category><![CDATA[time-series-analysis]]></category>
            <category><![CDATA[data-science]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Sun, 24 May 2026 13:31:12 GMT</pubDate>
            <atom:updated>2026-05-24T13:31:12.409Z</atom:updated>
            <content:encoded><![CDATA[<h3><strong>What Does It Mean to Model Something? A Times Series example</strong></h3><p><em>Before learning which model to use, it helps to understand what a model actually is.</em></p><h3><strong>The World Is Complicated</strong></h3><p>The world is complicated — and we can try to interpret reality in many ways.</p><p>One approach is to take a slice of reality and represent it through a set of assumptions expressed in the language of mathematics. Think about it: if we want to understand how the average daily temperature of a city evolves, we can suppose that today’s temperature shouldn’t differ dramatically from yesterday’s. Nearby days should carry information about the next day. But we also know that average temperatures shift with the seasons, so to predict tomorrow’s temperature well, it’s also informative to look at what temperatures looked like at this time <em>last year</em>. That means we might need to reach back at least 12 months into the past.</p><p>This kind of reasoning — laying down assumptions, translating them into equations, then checking if they were reasonable — is exactly what modelling is.</p><p>This article is about the foundations. Not <em>which</em> model you should use, but <em>what a model is</em>, <em>why the random error term matters more than people give it credit for</em>, and <em>how to tell if the model you chose was a good idea</em>. By the end, you’ll be able to look at a model equation and understand what every piece of it is doing.</p><p>Let’s dive in 🚀</p><h3><strong>What Is a Time Series?</strong></h3><p>Let me give you two answers — one intuitive, one slightly more precise.</p><p><strong>Intuitively</strong>, a time series is a numerical measurement (temperature, price, number of passengers, electricity demand…) recorded at regular time intervals (every minute, every hour, every month…) that tends to exhibit some degree of randomness.</p><p><strong>More precisely</strong>, a time series is a realisation of a discrete-time stochastic process with a continuous state space. If that sounds like a mouthful, don’t worry — the intuitive definition will carry us through this article. The formal version just says that the sequence of values you observe is one possible outcome drawn from a random process that unfolds in time.</p><p>One of the most iconic examples is the <strong>AirPassengers</strong> series: monthly counts of international airline passengers from 1949 to 1960. It shows up in practically every introductory time series course, and for good reason — it is simple, clean, and has two features that make it interesting: an upward trend and a repeating seasonal pattern.</p><pre>import pandas as pd<br>import matplotlib.pyplot as plt<br>import matplotlib.dates as mdates<br>df = pd.read_csv(&quot;data/airline.csv&quot;, parse_dates=[&quot;date&quot;])<br>df = df.sort_values(&quot;date&quot;).reset_index(drop=True)<br>fig, ax = plt.subplots(figsize=(9, 4))<br>ax.plot(df[&quot;date&quot;], df[&quot;passengers&quot;], color=&quot;#2B2B2B&quot;, linewidth=1.6)<br>ax.set_title(&quot;Monthly International Airline Passengers (1949–1960)&quot;)<br>ax.set_ylabel(&quot;Passengers (thousands)&quot;)<br>ax.xaxis.set_major_formatter(mdates.DateFormatter(&quot;%Y&quot;))<br>plt.tight_layout()<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wD643BO6PlyZBgh1qlOaYA.png" /><figcaption><em>Figure 1: Monthly international airline passengers. The upward trend and seasonal swings are impossible to miss.</em></figcaption></figure><p>We’ll come back to this series throughout the article.</p><h3><strong>What Does It Mean to Model a Time Series?</strong></h3><p>Modelling a time series means finding a function that, given the past values of the series (and possibly other inputs), produces a useful approximation of the current or future value. Our goal is to describe, through one or more equations, how the series behaves given its own history:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/229/1*xtQCd3AjDCvtdlaJGOQkyA@2x.png" /></figure><p>Here, yₜ is the value at time <em>t</em>, <em>f </em>is the functional form we choose, and θ is the vector of unknown parameters the model needs to be estimated.</p><p>But — and this matters — it would be arrogant to assume any model could perfectly capture reality. So we add a random error term:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/267/1*8XfRQ66GVvKeYln3MdwfuA@2x.png" /></figure><p>This εₜ is not just a cosmetic addition. It is the part of reality the model will not (and should not be expected to) explain. It is what separates a <em>forecasting method </em>from a <em>statistical model</em>.</p><p>Once we write down εₜ, we have to make assumptions about it. The most important: E[εₜ] = 0 (zero mean). This says the model shouldn’t be systematically wrong — it shouldn’t consistently overestimate or underestimate. Other common assumptions include no autocorrelation (the errors at different time points are uncorrelated) and homoscedasticity (constant variance over time). For now, zero mean is the one we’ll keep an eye on.</p><blockquote><em>A model without an explicitly defined error term is just a forecasting heuristic. The error term is what makes it a statistical model.</em></blockquote><p>Because εₜ is, by assumption, random and structureless, it should look like white noise — no patterns, no trend, no seasonality. This becomes the key diagnostic tool we’ll use at the end.</p><h3><strong>A Ladder of Models</strong></h3><p>Now that we have the general equation, let’s climb a ladder of increasingly rich functional forms — each one a different assumption about how the past informs the future.</p><h4><strong>The Random Walk</strong></h4><p>The simplest possible model says that the best guess for today is just yesterday’s value:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/109/1*ybcHtXsitbopc3cFNHZzrQ@2x.png" /></figure><p>This is the <strong>Random Walk</strong> — arguably the most famous model in time series analysis. It shows up in finance, physics, and biology. It makes no attempt to explain <em>why</em> the series moves; it just says movement is random around wherever we were.</p><h4><strong>A Moving Average of the Past</strong></h4><p>One step up: what if the best guess is the average of the last three observations?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/224/1*VuS0aWW_p314io1RNRa_Qg@2x.png" /></figure><p>This is more stable than the Random Walk (outliers get averaged out), but it still has no parameters to estimate — the weights are fixed at 1/3 each.</p><h4><strong>The AR(p) Model</strong></h4><p>Now let’s let the data decide how much each past value matters. Instead of fixed equal weights, we estimate a coefficient for each lag:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/208/1*sKZsLBXkX_hcuIUHze8_og@2x.png" /></figure><p>This is the <strong>AR(p)</strong> model — Autoregressive of order <em>p</em> — and it is one of the most important models in classical time series. The β coefficients are unknown and must be <em>estimated from the data</em>. In machine learning language, that’s what people call “training the model.”</p><p>The two simulations below illustrate how the choice of functional form shapes the behaviour of the series — even when both are driven by identical noise.</p><pre>import numpy as np<br>import matplotlib.pyplot as plt<br><br>np.random.seed(42)<br>n_total = 500   # simulate long enough to burn in initial conditions<br>n_plot = 200    # plot only the last segment<br>phi = 0.85<br><br>eps = np.random.normal(0, 1, n_total)<br>rw = np.zeros(n_total)<br>ar1 = np.zeros(n_total)<br>rw[0] = eps[0]<br>ar1[0] = eps[0]<br><br>for t in range(1, n_total):<br>    rw[t] = rw[t - 1] + eps[t]              # y_t = y_{t-1} + eps_t<br>    ar1[t] = phi * ar1[t - 1] + eps[t]      # y_t = 0.85 * y_{t-1} + eps_t<br><br>rw = rw[-n_plot:]<br>ar1 = ar1[-n_plot:]<br><br>fig, axes = plt.subplots(1, 2, figsize=(11, 4))<br>axes[0].plot(rw, color=&quot;#2B2B2B&quot;, linewidth=1.4)<br>axes[0].set_title(&quot;Random Walk\n$y_t = y_{t-1} + \\varepsilon_t$&quot;)<br>axes[1].plot(ar1, color=&quot;#2B7BB9&quot;, linewidth=1.4)<br>axes[1].set_title(&quot;AR(1)\n$y_t = 0.85\\,y_{t-1} + \\varepsilon_t$&quot;)<br>for ax in axes:<br>    ax.set_xlabel(&quot;Time&quot;)<br>plt.tight_layout()<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/818/1*Buwl4D8vgvjhkpxLetmpng@2x.png" /><figcaption><em>Figure 2: Same noise sequence, two different functional forms. The AR(1) with φ=0.85 mean-reverts; the Random Walk drifts freely.</em></figcaption></figure><p>This is the key insight behind the <em>ladder </em>metaphor: each model is just a different choice of <strong><em>f</em></strong>. The more structure we build in, the more assumptions we’re making — and the more we can be wrong.</p><p>The landscape of available models is vast. To give you a sense of the space:</p><p>- <strong>Classical statistical models</strong> — SARIMA family (MA, AR, ARMA, ARIMA, SARIMA), Unobserved Components models, Exponential Smoothing (ETS)</p><p>- <strong>Machine learning models</strong> — Linear/Ridge/Lasso regression, SVR, Random Forests, Gradient Boosting (XGBoost, LightGBM, CatBoost)</p><p>- <strong>Deep learning models</strong> — RNN, LSTM, GRU, CNN, Transformers</p><h3><strong>How Do You Choose a Model?</strong></h3><p>That choice depends mainly on two things: <strong>how the series behaves</strong>, and <strong>what the problem demands</strong>.</p><p>The behaviour of the series is uncovered through exploratory data analysis. Plot the series, look for trend and seasonality, check whether it looks stationary, inspect autocorrelation — the usual toolkit. We’ll dig deeper into that side of things in future articles in this series.</p><p>The problem context shapes how complex and interpretable the model should be. If you need fast, repeated re-estimation on a tight compute budget, simpler classical models are often the right tool. The same goes when you need to explain <em>why</em> a forecast looks the way it does. If the application only cares about forecast accuracy and you have enough data to train it, a non-interpretable black-box from the ML or deep learning shelf can be a strong option — as long as you’re comfortable trading transparency for flexibility.</p><p>There is no single winner in the list above — only models that fit the data and the job better or worse.</p><p>For the rest of this article, we’ll stay in the classical territory and look at one family: <strong>ETS</strong>. If you wish to dive into time series modelling and forecasting the best approach is to follow Rob Hyndman free book <a href="https://otexts.com/fpppy/">Forecasting Principles and Practice</a>.</p><h3><strong>The ETS Family</strong></h3><p>The <strong>ETS</strong> family — standing for <strong>Error, Trend, Seasonality</strong> — grew out of <em>exponential smoothing</em> methods developed in the late 1950s (Brown, Holt, Winters). As described in <a href="https://otexts.com/fpppy/08-exponential-smoothing.html"><em>Forecasting: Principles and Practice, the Pythonic Way</em> Chapter 8</a>, exponential smoothing produces forecasts as weighted averages of past observations, where the weights decay exponentially — more recent observations count more.</p><p>What makes the ETS family powerful is its ability to combine three components in different ways:</p><p>- <strong>Error (E)</strong>: additive (A) or multiplicative (M)</p><p>- <strong>Trend (T)</strong>: none (N), additive (A), or multiplicative (M)</p><p>- <strong>Seasonality (S)</strong>: none (N), additive (A), or multiplicative (M)</p><p>Each combination gives a different model. The taxonomy below shows all possible combinations:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/591/1*2xunUyMKlxx6UBX2koKmFQ@2x.png" /><figcaption><em>Figure 3: The ETS model taxonomy. Each cell is a different model. “A” = additive, “M” = multiplicative, “N” = none.</em></figcaption></figure><p>A few members of this family have famous names:</p><p>- <strong>ETS(A,N,N)</strong> — Simple Exponential Smoothing: no trend, no seasonality</p><p>- <strong>ETS(A,A,N)</strong> — Holt’s linear method: additive trend, no seasonality</p><p>- <strong>ETS(A,A,A) </strong>— Holt-Winters additive: additive trend and additive seasonality</p><p>Look back at Figure 1. The airline series has an obvious upward trend and clear seasonal swings every 12 months. Intuitively, the ETS(A,A,A) should handle that well. The ETS(A,N,N) — which knows nothing about trends or seasons — should struggle.</p><p>Let’s see this in practice. We fit both models with <strong>statsmodels</strong>’ <a href="https://www.statsmodels.org/devel/generated/statsmodels.tsa.exponential_smoothing.ets.ETSModel.html">ETSModel</a>, which implements the state-space ETS framework (see the <a href="https://www.statsmodels.org/devel/examples/notebooks/generated/ets.html)">official ETS examples</a>) and estimates smoothing parameters plus initial states by <strong>maximum likelihood</strong>:</p><pre>import pandas as pd<br>from statsmodels.tsa.exponential_smoothing.ets import ETSModel<br><br>df = pd.read_csv(&quot;data/airline.csv&quot;, parse_dates=[&quot;date&quot;])<br>y = df[&quot;passengers&quot;]<br><br># ETS(A,N,N): additive errors, no trend, no seasonality<br>fit_ann = ETSModel(<br>    y, error=&quot;add&quot;, trend=None, seasonal=None<br>).fit(maxiter=10000, disp=False)<br><br># ETS(A,A,A): additive trend and additive seasonality (period 12)<br>fit_aaa = ETSModel(<br>    y,<br>    error=&quot;add&quot;,<br>    trend=&quot;add&quot;,<br>    seasonal=&quot;add&quot;,<br>    seasonal_periods=12,<br>).fit(maxiter=10000, disp=False)<br><br>fitted_ann = fit_ann.fittedvalues<br>res_ann = fit_ann.resid<br>fitted_aaa = fit_aaa.fittedvalues<br>res_aaa = fit_aaa.resid<br><br># Smoothing parameters (α, β, γ) — see fit.summary() for full output<br>params_ann = dict(zip(fit_ann.model.param_names, fit_ann.params))<br>params_aaa = dict(zip(fit_aaa.model.param_names, fit_aaa.params))</pre><p>The companion script `code/what_model_means_models.py` wraps the same calls in `ets_ann_fit()` and `ets_aaa_fit()` for the figures and CSV exports.</p><p>After fitting, we get:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/518/1*oyPK23S_QiPT6OZaM3MDqQ@2x.png" /></figure><p>The residual standard deviation is already telling us something: ETS(A,A,A) leaves much less unexplained noise than ETS(A,N,N). The fitted values make it even clearer. The fitted values are the outputs of the fitted model, given its inputs — aka the past values of the series and the estimated parameters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/444/1*SPNqC1rmmLrfdCqu8KrWng@2x.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/893/1*pWXg5dDXr4o2ZHjNolVBlw@2x.png" /><figcaption><em>Figure 4: ETS(A,N,N) (left) is clearly missing the trend and seasonal structure. ETS(A,A,A) (right) follows the data closely.</em></figcaption></figure><p>The visual speaks for itself — the well-specified model hugs the data; the misspecified one trails behind, forever chasing a trend it never learned.</p><h3><strong>How Do We Know If the Model Is Good? Residual Diagnostics</strong></h3><p>Now we return to εₜ. Once a model is estimated, we can compute the <strong>residuals</strong>:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/472/1*PapO8TLQgzVzaiRH2f6gqg@2x.png" /></figure><p>These are the realisations of εₜ under our estimated parameters — what the model got wrong at each time step. And here’s the key idea: <strong>if the model was well specified, the residuals should look like random noise</strong>.</p><p>Remember our assumptions about εₜ? They were:</p><p>1. Zero mean — the model is unbiased</p><p>2. No temporal structure — no patterns left unexplained</p><p>We can check both visually by simply plotting the residuals over time. If we see patterns — a trend, seasonal waves, oscillations — we know the model left structure on the table. Below, `res_ann` and `res_aaa` come from `fit_ann.resid` and `fit_aaa.resid` after the statsmodels fits above.</p><pre>fig, axes = plt.subplots(2, 1, figsize=(10, 6), sharex=True)<br><br>for ax, residuals, label, color in zip(<br>    axes,<br>    [res_ann, res_aaa],<br>    [&quot;ETS(A,N,N) residuals&quot;, &quot;ETS(A,A,A) residuals&quot;],<br>    [&quot;#E05C2B&quot;, &quot;#2B7BB9&quot;],<br>):<br>    ax.plot(df[&quot;date&quot;], residuals, color=color, linewidth=1.3)<br>    ax.axhline(0, color=&quot;#2B2B2B&quot;, linewidth=0.9, linestyle=&quot;--&quot;)<br>    mean_val = residuals.mean()<br>    ax.axhline(mean_val, color=color, linewidth=1.1, linestyle=&quot;:&quot;,<br>               label=f&quot;mean = {mean_val:.1f}&quot;)<br>    ax.set_title(label)<br>    ax.legend()<br><br>plt.tight_layout()<br>plt.show()</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/742/1*kmSg3fSkdhlMEy4XGjlTcw@2x.png" /><figcaption><em>Figure 5: Top — ETS(A,N,N) residuals show a clear seasonal wave pattern: the model systematically over- and underestimates at the same months each year. Bottom — ETS(A,A,A) residuals fluctuate around zero with no visible structure. That’s what we want.</em></figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/818/1*5fZJQ94PUU6FLnTd0fEAIA@2x.png" /><figcaption><em>Figure 6: The dashed line is zero; the solid line and annotation mark the sample mean. ETS(A,N,N) (left) is shifted away from zero; ETS(A,A,A) (right) is centred near zero.</em></figcaption></figure><p>The ETS(A,N,N) residuals are practically screaming seasonality. Peaks and troughs repeat year after year — clear evidence the model missed something important. The ETS(A,A,A) residuals, on the other hand, look much more like random noise. Their mean is close to zero (≈0.0 vs. ≈2.2 for the misspecified model), and no obvious temporal pattern emerges.</p><p>This is residual diagnostics in its simplest form. More formal tools exist — autocorrelation functions (ACF/PACF), Ljung-Box tests, heteroscedasticity checks — but even a visual inspection of the residual time plot can tell you a great deal.</p><h3><strong>Putting It All Together</strong></h3><p>Let me summarise what we’ve built:</p><p>Modelling a time series means <strong>choosing the functional form of <em>f</em></strong> — which lags to include, how to combine them, whether to account for trend and seasonality — and then <strong>estimating any unknown parameters θ</strong> from the data (here via statsmodels’ likelihood-based `ETSModel.fit`). Once estimated, the model’s assumptions live in the residuals: if εₜ was assumed to be structureless, the residuals should look structureless too.</p><p>The Random Walk is the simplest possible f. The AR(p) is a flexible linear combination of past values. The ETS family goes further, decomposing the series into error, trend, and seasonal components that interact — either additively or multiplicatively.</p><p>Choosing the right model depends on two things: the behaviour of the series (visible in exploratory data analysis) and the context of the problem (how much interpretability you need, how fast re-estimation must be, how much data is available for training). There is no universally best model — only models that are more or less compatible with the data and the task at hand.</p><p>Future articles in this series will cover the specific models, time series EDA and eventually the machine learning approaches that make fewer assumptions about the functional form of f. Stay tuned 🥁</p><p><em>All code in this article is available in my </em><a href="https://github.com/MathNog/MathNogMediumArticles"><em>GitHub account</em></a><em>. Feel free to run everything locally — install dependencies from `requirements.txt` (NumPy, pandas, matplotlib, statsmodels).</em></p><p><em>If you enjoyed this article, follow for more on time series, forecasting, and mathematical modelling. And if something here helped you understand something that was once murky — that’s exactly why I write these.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b1ca114773e3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Julia for Time Series: ScoreDrivenModels.jl]]></title>
            <link>https://medium.com/@matnogueira/julia-for-time-series-920ede193d93?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/920ede193d93</guid>
            <category><![CDATA[score-driven-models]]></category>
            <category><![CDATA[time-series-analysis]]></category>
            <category><![CDATA[time-series-forecasting]]></category>
            <category><![CDATA[julia-language]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Mon, 06 Oct 2025 20:11:29 GMT</pubDate>
            <atom:updated>2026-05-24T13:49:03.751Z</atom:updated>
            <content:encoded><![CDATA[<h3>What will you learn?</h3><p>In this short article, you will learn the basics of a super flexible and general time series model framework called Score Driven Models. They were developed simultaneously by Harvey [2013] and Creal et al. [2013]. Known by different names — generalized autoregressive score (GAS), dynamic conditional score models, or simply score-driven models, they offer several advantages for time series modeling, as described below. Firstly, these models can handle non-Gaussian time series, where the conditional probability distribution is not normal. This capability is crucial for cases where the normal distribution is inadequate, such as wind speed series (which only take positive values) and financial return series (which exhibit excess kurtosis). Count data, such as the number of items sold in a store, also benefit from this flexibility, as they require discrete distributions. The ability to model non-normal data makes GAS models highly applicable across various domains.</p><p>In this article, we will use a Natural Inflow Energy time series from Brazil, which is naturally non-gaussian, to exemplify the use of this type of model with an open-source package written in Julia called <a href="https://lampspuc.github.io/ScoreDrivenModels.jl/latest/">ScoreDrivenModels.jl</a>.</p><p>In order to keep this article short, I will not dive into the full length of the theory behind score driven models. To do so, I strongly recommend diving into the following resources:</p><ul><li><a href="https://www.gasmodel.com/index.htm">Gas Model Website</a> with a lot of published papers and material regarding score-driven models;</li><li><a href="https://www.cambridge.org/br/universitypress/subjects/economics/econometrics-statistics-and-mathematical-economics/dynamic-models-volatility-and-heavy-tails-applications-financial-and-economic-time-series?format=PB&amp;isbn=9781107630024">Harvey original paper</a> with the original description of the Dynamic Conditional Score model;</li><li><a href="https://doi.org/10.1002/jae.1279">Cral et al. original paper</a> with the original implementation of the GAS(p, q) model.</li><li><a href="https://proceedings.science/sbpo/sbpo-2024/trabalhos/non-gaussian-score-driven-models-with-non-linear-unobserved-components-combinati?lang=pt-br">My undergraduate final project</a>, submitted as a complete paper at the SBPO (Brazilian Symposium on Operations Research)</li></ul><h3><strong>What is a Score Driven Model?</strong></h3><p>The best way to introduce a score-driven model is to define the fundamental steps that we need to follow to model a time series with this framework. Since they are a probabilistic model, out first step is to define a conditional probability distribution to be followed by the time series. Note that this step is super general! We can choose any distribution that we think will best describe the nature of the time series. Gaussian, Lognormal, Student-t, F, Gamma, and so on.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/382/1*mZZQIFyz6auEuW7gD51zyg.png" /><figcaption>Conditional probability distribution definition for our time series</figcaption></figure><p>This definition is saying the out time series <em>y </em>follows a conditional probability distribution of both the past observations of the series, static parameters and time-varying parameters (which are of crucial importance to this framework). One this distribution is defined, we need to choose how the time-varying parameters will evolve over time, since they are (who would guess?) time-varying. There are two main options: (a) an ARMA(p, q) process, following Creal et al., giving origin to a GAS(p,q) model; or (b) an unobserved components framework, following Harvey’s proposition. We will choose the first one, since it is the one implemented inside the <a href="https://lampspuc.github.io/ScoreDrivenModels.jl/latest//">ScoreDrivenModels.jl</a> package.</p><p>The GAS(p,q) model says that the time-varying parameters follow an ARMA(p,q) process. Yes! the same from the ARIMA family! Actually, the GAS(p,q) model generalizes both ARMA and GARCH models. Since we are imposing an ARMA(p, q) process in the time-varying parameters, this makes the GAS(p,q) framework inappropriate for non-stationary time series. This is a core point of this methodology! The unobserved components framework, on the other hand, is appropriate for non-stationary time series! Let’s define an ARMA(p, q) process to the time-varying parameters dynamic.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/705/1*8S659dS5h9N8OZm1y9jbWw.png" /><figcaption>GAS(p,q) dynamic for the time-varying parameters of the chosen distribution</figcaption></figure><p>Wait a minute! There is a new symbol in this definition which we have not presented yet: <em>s</em>. This is, indeed, the core of the proposed framework. It is the <em>standardized score</em> of the time series. Let’s define, first, who is the score: it is the first derivative of the log-likelihood function with respect to the parameter of interest. This has some intuition: is says how much improvement we have on the likelihood of our model if we change the parameter of interest (mean, variance, and so on). It is the score that drives the dymanic of the score-driven model!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/436/1*FtylJDMG_LxwR-vpFyWN7w.png" /><figcaption>Score definition</figcaption></figure><p>Finally, we can define the standardized score, which is simply a scaled version of the score obtained by a (highly non trivial) multiplication. I will not dive into the properties of this scaled version, but it is a key element of the framework.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/840/1*tXvWgZT6Eg55U0A9x9E7wg.png" /><figcaption>Fisher information and standardized score definitions</figcaption></figure><p>The last piece of theory I would like to talk about is the model’s estimation. As usual, it is done with maximum likelihood estimation, in which we aim to find the optimal parameters that maximize the likelihood of the model given the observed time series. This is particularly straightforward, provided an efficient nonlinear optimization algorithm is used, requiring only the implementation of the updating function and the evaluation of the likelihood function at a particular optimization point.</p><p>That’s it for theory! Let’s talk about the ScoreDrivenModels.jl package.</p><h3><strong>The ScoreDrivenModels.jl package</strong></h3><p>This package, developed by our laboratory <a href="https://www.lamps.ind.puc-rio.br/en/">LAMPS</a> implements score driven models of type GAS(p,q) for a variety of available distributions with 1 or more time-varying parameters.</p><p>Take a look into the <a href="https://lampspuc.github.io/ScoreDrivenModels.jl/latest/">package documentation</a> to a full report of it’s functionality! You can also dive into the <a href="https://arxiv.org/abs/2008.05506">package’s article</a>.</p><p>The table below summarizes the available distributions where the identity scale means d=0, inverse scale means d=-1 and inverse square means d=0.5.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/937/1*_xJk249cpdKt-rbn1H1wHA.png" /><figcaption>Available distributions</figcaption></figure><p>Moreover, the package also allows <a href="https://lampspuc.github.io/ScoreDrivenModels.jl/latest/manual/#Implementing-a-new-distribution-1">for new distributions to be implemented</a> with pull requests. All one have to do is to define some values such as the score, Fisher information and link functions to the desired distribution.</p><p>There is a number of available optimizers to fit the model with MLE, such as Nelder Mead, LBFGS and IPNewton. However, there is a <strong>fundamental trick!</strong></p><p>Those optimizers are unconstrained algorithms, which means that they do not guarantee any type of restrictions of the distribution parameters. This is a huge problem, since variance, for instance, can never be negative!</p><p>The trick to guarantee this type of restrictions is through link functions, which are described in the package documentation, <a href="https://lampspuc.github.io/ScoreDrivenModels.jl/latest//manual/#Recursion-1">here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/662/1*4gt1uCjrdh26cKmBAKk96A.png" /><figcaption>Link functions for GAS(p,q) models</figcaption></figure><p>That’s enough for plain text and mathematical equations. Let’s dive into the action!</p><h3>Modeling a non-gaussian time series with ScoreDrivenModels.jl</h3><p>First of all, we need the following packages:</p><pre>using Dates<br>using Plots<br>using DelimitedFiles<br>using Distributions<br>using ScoreDrivenModels</pre><h4><strong>Loading a time series</strong></h4><p>This is a monthly time series of the natural inflow energy of the Northeast region of Brazil, available inside the package.</p><p>I have downloaded it from the package repo and saved it in the `data` folder in my <a href="https://github.com/MathNog/MathNogMediumArticles">GitHub repository</a>. The following code loads the time series and divide into train and test sets.</p><pre>dates = collect(Date(1961):Month(1):Date(2000, 12));<br>y = vec(readdlm(&quot;data/nie_northeastern.csv&quot;));<br><br>H  = 60<br>T  = 240<br>last_obs  = length(y) - H<br>first_obs = last_obs - T<br><br>y_train     = y[first_obs:last_obs];<br>y_test      = y[last_obs+1:end];<br><br>dates_train = dates[first_obs:last_obs];<br>dates_test  = dates[last_obs+1:end];</pre><p>We can also plot the time series to visually inspect it!</p><pre>plot(dates_train, y_train, label = &quot;Train&quot;)<br>plot!(dates_test, y_test, label = &quot;Test&quot;) <br>plot!(title=&quot;Brazil Northeastern Natural Inflow Energy&quot;, xlabel=&quot;Date&quot;, ylabel=&quot;Value&quot;)<br>plot!(xformatter = x -&gt; Dates.format(Date(Dates.UTD(x)), &quot;yyyy&quot;))</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*dUNaFrZw3rUPKK4bj0AhrQ.png" /><figcaption>Our non-gaussian time series.</figcaption></figure><p>As we can clearly see, the time series has no visual trend, mut a strong seasonal pattern. Moreover, this series does not follow a gaussian distribution, because it is impossible to have negative inflow!</p><h4><strong>Defining the model</strong></h4><p>We define the model using the <em>ScoreDrivenModel</em> function from the package. The function takes the following arguments:</p><p>- <em>p_lags</em>: The lags of the autoregressive part of the model.</p><p>- <em>q_lags</em>: The lags of the moving average part of the model.</p><p>- <em>dist</em>: The distribution of the model.</p><p>- <em>d</em>: The scale for the standardized socre of the model.</p><p>- <em>time_varying_params</em>: The parameters of the model that are time-varying.</p><p>This is a crucial part of the modeling process, as it is when, based on the time-series features, we define the model’s structure and the parameters that we want to estimate.</p><p>Since our time series is of monthly frequency and it is a time series of natural inflow energy, we expect to have a strong seasonal pattern (as shown in the previous plot). Because of that, we will add a seasonal lag of 12 months in both the autoregressive and moving average parts of the model.</p><p>Moreover, since natural inflow can never be negative, it is natural to use a distribution that is always positive, such as the log-normal distribution, from the Distributions.jl package. It is important to say that there is no technical reason not to use a Normal distribution. The model would be fitted and simulated without any issues, but we would possibly generate negative scenarios, which would not make sense in this context!</p><p>Finally, we will fit a Log-normal GAS model with both parameters as time varying, that is, both will follow an ARMA process with autoregressive and moving average lags of 1 and 12 months.</p><pre>p_lags  = [1, 12]<br>q_lags  = [1, 12]<br>distrib = Distributions.LogNormal<br>d       = 0.0<br><br>gas = ScoreDrivenModel(p_lags, q_lags, distrib, d; time_varying_params = [1, 2]);</pre><h4><strong>Estimating and Simulating the model</strong></h4><p>We can fit and forecast the model with two simple functions! Notice that we are doing a probabilistic forecast with 1000 scenarios!</p><pre>fit_gas = fit!(gas, y_train);<br>forecast_gas = forecast(y_train, gas, test_months; S=1000);</pre><p>Once the model is fitted and forecasted, we can visualize it’s results!</p><p>First, the fitted parameters.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/527/1*YOQ3TFgD6UW2Hrx0qf48JA.png" /><figcaption>Fitted parameters for the GAS(p,q) model</figcaption></figure><p>We can also plot the fitted mean and both point and probabilistic forecasts.</p><pre>point_forecast = forecast_gas.observation_forecast;<br>scenarios      = forecast_gas.observation_scenarios;<br>quantiles_obs  = forecast_gas.observation_quantiles;<br>fitted_values  = fitted_mean(gas, y_train);</pre><pre>plot(dates_train, y_train, label = &quot;Train Values&quot;, color = :black)<br>plot!(dates_train, fitted_values, label = &quot;Fitted Values&quot;, color = :blue)<br>plot!(dates_test, scenarios, label = &quot;&quot;, color = :lightgray)<br>plot!(dates_test, y_test, label = &quot;Test Values&quot;, color = :black)<br>plot!(dates_test, point_forecast, label = &quot;Point Forecast&quot;, color = :red)<br>title!(&quot;Natural Inflow Energy Simulation from LogNormal GAS&quot;)<br>plot!(xformatter = x -&gt; Dates.format(Date(Dates.UTD(x)), &quot;yyyy&quot;),<br>    legendcolumns = 4, legend = :outerbottom)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Hu9MGiFeV5bD3htFxj4muQ.png" /><figcaption>GAS(p, q) fitted mean, point forecast and probabilistic forecasts.</figcaption></figure><p>Even thought this may seen enough to present a time series modeling and forecasting package, there are some super interesting and powerful functionalities implemented that must be talked about!</p><h4>Fitted and forecasted parameters</h4><p>First, we can visualize each parameter fitted values and probabilistic forecast! Yes, even thought variance is, essentially, unobserved, we can visualize it with this package as follows.</p><pre>params_fitted = score_driven_recursion(gas, y_train)<br>params_point_forecast = forecast_gas.parameter_forecast; #H x P<br>params_scenarios      = forecast_gas.parameter_scenarios; #H x P x S</pre><pre>for i in 1:size(params_fitted, 2)<br> plot(dates_train, params_fitted[2:end, i], label = &quot;Fitted Parameters&quot;, color = :blue)<br> plot!(dates_test, params_scenarios[:, i, :], label = &quot;&quot;, color = :lightgray)<br> plot!(dates_test, params_point_forecast[:, i], label = &quot;Point Forecast&quot;, color = :red)<br>    title!(&quot;Parameter $i for LogNormal GAS&quot;)<br>end</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*5hwKlhvAxls63DbmLCTJBQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/700/1*XCfWiwJgNCHOgFcTCeRNEA.png" /><figcaption>Mean (parameter 1) and variance (parameter 2) fitted and simulated by the GAS(p,q) model</figcaption></figure><h4>Residuals! Always check the residuals!</h4><p>One crucial step in any time-series model workflow is to check the residuals of the model. Features like zero mean, homoscedasticity and lack or auto-correlation are essential to validate the fitted model and to allow ourselves to look into (and trust!) the out of sample forecasts. Even though the package provides a residuals diagnostics plot, it is not perfect, and I highly encourage you to implement your own diagnostics. We can access the simple one provided by the following code.</p><pre>plot(fit_gas, size=(1000, 1000), title = &quot;Residuals Diagnostics&quot;)</pre><p>Which generates this result.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/999/1*6FwFRh7w4asFjArNQJFwjQ.png" /><figcaption>Residuals plot, ACF Plot, Histogram and QQ-Plot for residuals diagnostics</figcaption></figure><p>We can also compute both Pearson and Quantile Residuals automatically with the package so that we are able to, at least, proceed with a visual inspection. Allow me to simply paste those residuals definitions here, without a lot of explanation…</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/665/1*CfGs6ZHCeuw0DOI40ZBRwg.png" /><figcaption>Quantile residuals definition</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/650/1*RNGPCp0Vvvsot-n0u61a3A.png" /><figcaption>Pearson residuals definition</figcaption></figure><pre>q_residuals   = quantile_residuals(y_train, gas);<br>std_residuals = pearson_residuals(y_train, gas);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*s6rs8jooZLCf_CgzrVP4hA.png" /></figure><h4>Built-in Time Series Cross Validation</h4><p>There is a built-in function for time series cross validation, in which we simply define the forecast horizon and the first timestamp index to fit the model. This function automatically computes the number of windows to fit and forecast the model, while returning different accuracy metrics across all windows and horizons. This is an excellent feature to access the proposed model consistency thought different sets of training and testing, improving our confidence in the model results.</p><pre>first_index = 100<br>gas_cv = cross_validation(gas, y_train, H, first_index);</pre><pre>cv_abs_errors = gas_cv.abs_errors<br>cv_mean_crps  = gas_cv.mean_crps<br>cv_mae        =  gas_cv.mae<br><br>H, W = size(cv_abs_errors)</pre><pre>plot(1:H, cv_mean_crps, dims=2, label=&quot;Mean CRPS&quot;)<br>plot!(1:H, cv_mae, label=&quot;MAE&quot;)<br>plot!(xlabel = &quot;Window&quot;, ylabel = &quot;Error&quot;, title = &quot;Mean Absolute Error, Mean CRPS and MAE across all $W windows by $H steps ahead&quot;,<br>titlefontsize = 11)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*jLW2GIoFN33Bg9-aRsNGPA.png" /><figcaption>Mean CRPS and MAE across all windows for each forecast horizon</figcaption></figure><p>Thanks for reading this far! I hope this tutorial helped you to understand the core power and flexibility of <em>ScoreDrivenModels </em>to model and forecast non-gaussian (and gaussian, why not?) time series in Julia. Feel free to use the package yourself and let us know about your experience!</p><p>All the code used here is 100% written by me and available on my GitHub. The text, on the other hand, was written with the help of AI — because good tooling helps with writing too 😉</p><p>If you liked this package, be sure to check out the other time series tools we’ve built at LAMPSPUC. And stay tuned — more tutorials are on the way!</p><p><a href="https://github.com/MathNog/MathNogMediumArticles">GitHub - MathNog/MathNogMediumArticles: Public Repository for my Medium</a></p><h3>Who am I?</h3><p>Hey there! I’m Matheus Nogueira, a Brazilian master’s student in <strong>Operations Research </strong>at <strong>LAMPS</strong>, an Optimization and Statistics Lab at <strong>PUC-Rio</strong>. I did my undergrad in <strong>Computer Engineering</strong>, also at PUC-Rio, in the Informatics’ Department.</p><p>Most of my work for the last years is about making sense of time series — mainly trying to predict the future with forecasting and simulation and I would love to share some interesting tools and concepts with you.</p><h3>What’s LAMPS?</h3><p><strong>LAMPS </strong>is a research lab that mixes optimization, statistics, and machine learning — with people from both Electrical and Industrial Engineering at PUC-Rio. Besides academic research, we also work with companies and develop open-source packages, mostly in Julia.</p><p>If you’re into time series, you’ll probably like our GitHub. We’ve got packages for ARIMA, state space models, score driven models, and more.</p><p><a href="https://github.com/LAMPSPUC">Laboratory of Applied Mathematical Programming and Statistics (LAMPS PUC-Rio)</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=920ede193d93" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Julia for Time Series: StateSpaceModels.jl]]></title>
            <link>https://medium.com/@matnogueira/julia-for-time-series-statespacemodels-jl-296af2034517?source=rss-e77622360fa3------2</link>
            <guid isPermaLink="false">https://medium.com/p/296af2034517</guid>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[state-space-model]]></category>
            <category><![CDATA[julia-language]]></category>
            <category><![CDATA[time-series-forecasting]]></category>
            <dc:creator><![CDATA[Matheus Nogueira]]></dc:creator>
            <pubDate>Mon, 07 Jul 2025 13:33:24 GMT</pubDate>
            <atom:updated>2025-07-14T22:10:27.490Z</atom:updated>
            <content:encoded><![CDATA[<h3>Time Series Modeling and Forecasting with StateSpaceModels in Julia</h3><h4><strong>Who am I?</strong></h4><p>Hey there! I’m Matheus Nogueira, a Brazilian master’s student in <strong>Operations Research </strong>at <strong>LAMPS</strong>, an Optimization and Statistics Lab at <strong>PUC-Rio</strong>. I did my undergrad in <strong>Computer Engineering</strong>, also at PUC-Rio, in the Informatics’ Department.</p><p>Most of my work for the last years is about making sense of time series — mainly trying to predict the future with forecasting and simulation and I would love to share some interesting tools and concepts with you.</p><h4><strong>What’s LAMPS?</strong></h4><p><strong>LAMPS </strong>is a research lab that mixes optimization, statistics, and machine learning — with people from both Electrical and Industrial Engineering at PUC-Rio. Besides academic research, we also work with companies and develop open-source packages, mostly in Julia.</p><p>If you’re into time series, you’ll probably like our GitHub. We’ve got packages for ARIMA, state space models, score driven models, and more.</p><p><a href="https://github.com/LAMPSPUC">Laboratory of Applied Mathematical Programming and Statistics (LAMPS PUC-Rio)</a></p><h4><strong>Why this article?</strong></h4><p>State space models are awesome. They’re flexible, powerful, intuitive, and cover a huge range of time series models — from the good old ARIMA to structural models and dynamic regression with exogenous variables.</p><p>In this article, I’ll walk you through how to use StateSpaceModels.jl, a package we’ve built at LAMPS to make modeling and forecasting with state space models easy and fast.</p><p><a href="https://lampspuc.github.io/StateSpaceModels.jl/latest/">Home · StateSpaceModels.jl</a></p><p>We’ll do a hands-on tutorial, with some theory here and there to help you understand what’s going on. By the end, you should be able to run your own forecasting project in Julia using `StateSpaceModels.jl` — from start to finish.</p><p>Feel free to follow along with the available code at my GitHub.</p><p><a href="https://github.com/MathNog/MathNogMediumArticles">https://github.com/MathNog/MathNogMediumArticles</a></p><p>Let’s dive in 🚀</p><h4><strong>Setting things up</strong></h4><p>Before we jump into modeling, let’s get our Julia environment ready.</p><p>Here are the packages you’ll need:</p><ul><li>StateSpaceModels.jl — for building and estimating the models,</li><li>DataFrames — to handle our time series data,</li><li>CSV — to read datasets from files (even though we’ll load most stuff from built-in datasets),</li><li>Plots — for, well… plotting stuff,</li><li>Statistics.jl — for simple descriptive statictics (aka, taking the mean of a vector).</li></ul><p>You can install everything by running:</p><pre>import Pkg<br>Pkg.add(&quot;StateSpaceModels&quot;)<br>Pkg.add(&quot;DataFrames&quot;)<br>Pkg.add(&quot;CSV&quot;)<br>Pkg.add(&quot;Plots&quot;)<br>Pkg.add(&quot;Statistcs)</pre><p>If you’re working inside a project (which is a good idea for reproducibility), don’t forget to activate and instantiate the environment:</p><pre>Pkg.activate(&quot;.&quot;)<br>Pkg.instantiate()</pre><p>Then, just load the packages…</p><pre>using StateSpaceModels, DataFrames, CSV, Plots, Statistics</pre><p>If you’re following along using the notebook version of this tutorial, we also have some helper functions in a separate file:</p><pre>include(&quot;statespacemodels_functions.jl&quot;)</pre><h4><strong>Loading AirLinePassengers Dataset</strong></h4><p>Before jumping into model fitting, let’s take a look at a classic time series: the number of monthly airline passengers in the UK from 1949 to 1960. Fortunately, the package already has some built-in datasets.</p><p>Let’s load the dataset:</p><pre>airline = CSV.File(StateSpaceModels.AIR_PASSENGERS) |&gt; DataFrame;<br>airline.passengers = Float64.(airline.passengers);</pre><h4><strong>Visual inspection</strong></h4><p>This dataset is probably one of the first things you see in any time series course — and for a good reason. It’s clean, has a strong upward <strong>trend</strong>, obvious <strong>seasonality</strong>, and some subtle challenges that make it perfect for learning.</p><p>But we’re not just going to throw a SARIMA at it and call it a day. What makes StateSpaceModels.jl really shine is its ability to handle <strong>structural models</strong>, where we decompose the series into unobserved components like <strong>trend </strong>and<strong> seasonality</strong>.</p><p>This is perfect for building models that are interpretable <em>and</em> flexible — and that’s exactly what we’ll explore soon.</p><p>First, let’s take a look into the series.</p><pre>title = &quot;Airline Passengers&quot;<br>x_label = &quot;Month&quot;<br>y_label = &quot;Passengers&quot;<br>test_length_airline = 12*3<br>plot_time_series(log_airline, &quot;month&quot;, &quot;passengers&quot;, title, x_label, y_label; test_length = test_length_airline)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Ws_0PC127P5MIjuamYjluw.png" /><figcaption>Airline montly time series.</figcaption></figure><p>When you plot the original series, you’ll notice that the seasonal ups and downs get <em>bigger</em> over time. This means the variance of the seasonal component isn’t constant — it grows over time. That’s a problem for many models that assume the variance is stable (a.k.a. <em>homoscedastic</em>).</p><p>To deal with this, we apply a simple but powerful trick: <strong>log-transform the data</strong>. Taking the logarithm helps <strong>stabilize the unconditional variance,</strong> which makes it easier for our models to learn the structure of the series — especially the seasonal part. If, however, we have evidence of <strong>conditional heteroscedasticity,</strong> the log itself is not sufficient to handle it.</p><p>Here’s how we load and log-transform the dataset and plot the transformed series. See how the ups and downs seems to have the same amplitude over time?</p><pre>log_airline = deepcopy(airline);<br>log_airline.passengers = log.(log_airline.passengers);<br><br>title = &quot;Airline Log-Passengers&quot;<br>x_label = &quot;Month&quot;<br>y_label = &quot;Log(Passengers)&quot;<br>test_length_airline = 12*3<br>plot_time_series(airline, &quot;month&quot;, &quot;passengers&quot;, title, x_label, y_label; test_length = test_length_airline)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*Fa_nXB-heUiliFiTqSSuWw.png" /><figcaption>Log(Airline) montly time series.</figcaption></figure><h4><strong>Train-test split</strong></h4><p>As in any good machine learning workflow, we shouldn’t train our model on all the data and then judge its performance on the same data. That’s cheating.</p><p>To really know how well our model is doing, we need to test it on data it hasn’t seen before — data that wasn’t used during estimation. That’s why we split our dataset into two parts:</p><p>- a <strong>training set</strong>, to fit the model,</p><p>- and a <strong>test set</strong>, to evaluate its forecasting accuracy.</p><p>In time series, this split has to respect the order of the data — no shuffling allowed! We simply cut the last N observations to serve as our test set and keep the rest for training. The images above have already shown the test set for our tutorial.</p><pre>airline_train, airline_test = time_series_split(log_airline, test_length_airline);</pre><h4><strong>A quick dive into structural time series models</strong></h4><p>At the heart of StateSpaceModels.jl is the <strong>state space representation</strong> of time series models — a flexible way to write models that evolve over time. Some classic time series models, such as the ARIMA family and Exponential Smoothing (ETS) family can be written in this form. The state space framework and the Kalman Filter as strongly related, but this is a topic for another time. For this tutorial, we can simply see the Kalman Filter as a black-box method that allows us to fit a model that has a state space form.</p><p>The only theory that I wish to present is the general state space representation as a system of observation and state equations. For any given model, if we can write it in this format, we can say that this model has a state space representation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/567/1*ooFTG9BuyykOfa4v-dTUeQ.png" /><figcaption>General State Space Representation.</figcaption></figure><p>One particularly useful class is the <strong>structural time series models</strong>, where we explicitly model components like <strong>trend</strong>, <strong>seasonality</strong>, <strong>cycles</strong>, and <strong>noise</strong>.</p><p>The general form of a structural model is:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/318/1*iwNQ5KzWdJjzr4cHnH2xgQ.png" /><figcaption>Observations equation from a structural model.</figcaption></figure><p>This is called the <strong>observation equation</strong>— it links the hidden structure of the model to the actual data we observe. In this case, the time series data is written as a sum of <strong>trend/level, seasonality, cycle </strong>and <strong>noise</strong> <strong>components</strong>.</p><p>Each of the components evolves over time according to its own <strong>state equation</strong>. For example, the <strong>Basic Structural Model</strong> (BSM), which we’ll use a lot here, is made of one observation equation and 3 state equations, for level, slope and seasonality. Given the BSM, the state would be a vector composed of the level, slope and seasonality components.</p><h4><strong>Basic Structural Model equations</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/543/1*bxYGTW6q7aWwiJ_EC7m3Pw.png" /><figcaption>Basic Structural Model equations.</figcaption></figure><p>The BSM state equations are composed of a random walk with stochastic drift (<em>a.k.a local linear trend model)</em> and a stochastic seasonality component.</p><p>This setup allows each component to evolve in time with its own randomness — making the model highly flexible.</p><p>When we fit a model, we obtain fitted values for each component variance, the observation equation variance, and each component itself.</p><p>The package supports several types of models that can all be written in <strong>state space form</strong>, including: SARIMA, ETS, DAR, BasicStructual, BasicStructuralExplanatory, LinearRegression and some naive models as well.</p><p>We’ll mostly focus on the BasicStructural model here — but once you get the hang of it, switching to other types is straightforward.</p><p>We have two ways to define a BSM model in StateSpaceModels.jl. First, with the UnobservedComponents class, where we can specify each component. Notice that, since our time series has a monthly frequency, it’s seasonality period is 12 (months).</p><pre>ssm_airline = StateSpaceModels.UnobservedComponents(airline_train.passengers;<br>                                                    trend = &quot;local linear trend&quot;,<br>                                                    seasonal = &quot;stochastic 12&quot;,<br>                                                    cycle = &quot;no&quot;);</pre><p>We could also use the BasicStructural class.</p><pre>seasonal_period = 12<br>ssm_airline = StateSpaceModels.BasicStructural(airline_train.passengers, seasonal_);</pre><h4><strong>Fitting the model</strong></h4><p>Once we’ve defined our model, fitting it is super straightforward. The `fit!` function takes care of estimating the parameters — namely, the variances of the components: irregular, trend, slope, and seasonal as well as the initial states— using maximum likelihood and the Kalman.</p><p>If any of these variances are estimated as <strong>zero</strong>, it means the corresponding component is <strong>deterministic</strong>, not stochastic. In other words, the model believes that component doesn’t need randomness to explain the data.</p><p>Here’s how we do it:</p><pre>StateSpaceModels.fit!(ssm_airline);</pre><p>We can print the fitted results nicely with:</p><pre>print_results(ssm_airline);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/922/1*YTtbv1IXQAg7jDKDLDPmHw.png" /><figcaption>Airline BSM fitted parameters, namely the variances from each equation.</figcaption></figure><p>This shows us the estimated variances, standard errors, and p-values for each component. In the output above, we can see that both the <strong>slope</strong> and <strong>seasonal</strong> variances were estimated as zero. At first glance, this suggests these components might be deterministic. But let’s not rush — we should always look at the <strong>p-values </strong>of the related <strong>z-test </strong>that tests if each parameter is statistically equal to zero.</p><p>The p-values associated with the irregular, trend and seasonality variances are all smaller than 0.01, which means that the null-hypothesis of each z-test (which says that the given parameter is equal to zero) can be strongly rejected. That being said, even though the fitted variance of the seasonal component was zero, the t-test says that we have strong evidence that it is not, so that this component is actually stochastic. The slope component, on the other hand, seems deterministic, since the p-value from the t-test is 1, which means that we do not have evidence to conclude that it’s value is not zero.</p><h4><strong>Model diagnostics</strong></h4><p>A crucial step after fitting any model is to check the <strong>residuals</strong> — and in the state space framework, we do this by analyzing the <strong>standardized innovations</strong>.</p><p>These innovations are the fitted values of the error terms — especially the one from the <strong>observation equation</strong>. In other words, they represent what the model couldn’t explain, scaled by the uncertainty at each point.</p><p>To get them, we run the Kalman filter and extract the innovations:</p><pre>filter_airline = StateSpaceModels.kalman_filter(ssm_airline)<br>std_inov = StateSpaceModels.get_standard_innovations(filter_airline);<br>std_residuals = StateSpaceModels.get_standard_residuals(filter_airline);</pre><p>We can plot the standard innovations and the standard residuals to see that they are the same thing.</p><pre>plot(airline_train.month, std_inov, title = &quot;Standardized Innovations&quot;, label = &quot;&quot;, xlabel = &quot;Month&quot;, ylabel = &quot;Standardized Innovations&quot;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*B7YYJhn-5Jf2JdHboAyN1w.png" /><figcaption>Airline BSM Starndard Innovations.</figcaption></figure><pre>plot(airline_train.month, std_residuals, title = &quot;Standardized Residuals&quot;, label = &quot;&quot;, xlabel = &quot;Month&quot;, ylabel = &quot;Standardized Residuals&quot;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*R6lYWMv0wOA8rgXtL20l6g.png" /><figcaption>Airline BSM Starndard Residuals.</figcaption></figure><h4>Diagnostics Plot</h4><p>The BSM model (and most state space models) assumes that the residuals are: <strong>uncorrelated over time </strong>and<strong> normally distributed</strong> (i.e., errors are Gaussian white noise).</p><p>We can easily check these assumptions with a built-in function called <em>plotdiagnostics</em>:</p><pre>p_diag = StateSpaceModels.plotdiagnostics(kalman_filter(ssm_airline), size = (1000, 600), <br>                                        plot_title = &quot;BSM Airline Std Innovations Diagnostics&quot;,<br>                                        title = [&quot;Residuals&quot; &quot;ACF&quot; &quot;Histogram&quot; &quot;Q-Q Plot&quot;])</pre><p>This will give us:</p><ul><li>A <strong>time series plot</strong> of the residuals,</li><li>The <strong>autocorrelation function (ACF)</strong>,</li><li>A <strong>histogram</strong> to check symmetry and outliers,</li><li>A <strong>Q-Q plot</strong> to visually assess normality</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*28pi1yKic0asxYHaitgCvQ.png" /><figcaption>Airline BSM diagnostics for the standardized residuals.</figcaption></figure><h4><strong>Getting the predictive, filtered and smoothed states</strong></h4><p>One of the best things about using structural state space models is that we don’t just get a black-box prediction — we actually <strong>see the inner structure</strong> of the time series.</p><p>In particular, we can access the <strong>latent states</strong> estimated by the Kalman filter, which give us the values (and uncertainty) of each hidden component: trend, slope, seasonality, etc.</p><p>But first: what exactly is a <em>state</em>?</p><p>In this context, the <strong>state</strong> is the collection of unobserved variables that drive the dynamics of the series. For the Basic Structural Model, that includes the level, slope, and seasonal effects.</p><p>We can estimate these states in three different ways:</p><p>- <strong>Predictive states: </strong>Estimated before seeing the current data point:</p><p>- <strong>Filtered states: </strong>Estimated <em>after</em> seeing the current data point</p><p>- <strong>Smoothed states: </strong>Estimated using <em>all </em>the data (past and future)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/533/1*Avl546zVAxmOQmHgGmPPRQ.png" /><figcaption>States definition in the state space framework.</figcaption></figure><p>You can get each one with:</p><pre>predictive_states = StateSpaceModels.get_predictive_state(ssm_airline);<br>filtered_states   = StateSpaceModels.get_filtered_state(ssm_airline);<br>smoothed_states   = StateSpaceModels.get_smoothed_state(ssm_airline);</pre><p>From the estimated states, we can extract the actual time series components — like the smoothed trend, filtered slope, or predictive seasonality:</p><pre>smoothed_level, smoothed_slope, smoothed_seasonality       = get_components_from_states(smoothed_states);<br>filtered_level, filtered_slope, filtered_seasonality       = get_components_from_states(filtered_states);<br>predictive_level, predictive_slope, predictive_seasonality = get_components_from_states(predictive_states);</pre><p>Why is this important?</p><p>Because now we can <strong>study the time series itself</strong>, not just predict it with a black-box model (yes, neural networks, I am talking to you).</p><p>Using the filtered and smoothed components, we can answer questions like:</p><ul><li>How did the trend evolve over time?</li><li>Was there a sudden shift in growth?</li><li>Did seasonality change or stay stable?</li><li>When did the series deviate from its usual behavior?</li></ul><p>These insights are especially useful in fields like economics, energy, retail, or any context where <strong>understanding the structure</strong> is as important as forecasting the future.</p><p>There is a built-in method to plot the smoothed components, which can be used as follows:</p><pre>ks = StateSpaceModels.kalman_smoother(ssm_airline)<br>plot(ssm_airline, ks)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*ExeOhkhV1UOM_sCcqYTk0g.png" /><figcaption>Smoothed components obtained from the kalman smoother applied to the fitted BSM.</figcaption></figure><p>If we want to see the other components, we should do it by hand, as it is done below.</p><p>A curious mind may be asking itself why the initial values from the slope component seems <em>unstable </em>in the beginning of the series. This usually happens with models fitted with the Kalman filter, which needs the first observations to “warm up”. This is so common that in the following functions, I have chosen to exclude the first 12 observations from all components. This is not cheating if you know what you are doing!</p><pre>plot_components(airline_train.month, predictive_level, predictive_slope, predictive_seasonality, &quot;Predictive&quot;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*uZhpC5xRnPY8gZHTXt5ccw.png" /><figcaption>Predictive components obtained from the fitted BSM.</figcaption></figure><pre>p_filtered = plot_components(airline_train.month, filtered_level, filtered_slope, filtered_seasonality, &quot;Filtered&quot;)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/900/1*TKPHNW1_ptFhkniRnoU4GA.png" /><figcaption>Filtered components obtained from the fitted BSM.</figcaption></figure><p>It would be important to guarantee that the output we saw earlier, with the fitted variances, correspond to nature of the plotted components. Since the fitted BSM is a <strong>predictive</strong> model, we must analyse the predictive components. We have some intresting results! First, it is clear that the level/trend component is stochastic. The slope component is tricky. We expected a deterministic component, since it&#39;s variance was estimated as zero. However, the plot itself does no show this result and one could argue that it is stochastic. However, I would argue that even though the component is changing over time, is scale, in comparison to the series itself, is irrelevant! Tha is an interesting discussion to show that interpreting a model is not black and white, there are grey areas! The seasonal component, although was fitted as stochastic, is almost deterministic!</p><h4><strong>Out-of-sample forecast and simulation</strong></h4><p>Alright, now for the main event <strong>🥁 </strong><br>As a forecaster guy™, this is where things get exciting: <strong>how do we make forecasts</strong> from a fitted state space model?</p><p>Not just point forecasts — we also want <strong>probabilistic scenarios</strong>, because in real life, we care about uncertainty too.</p><p>Luckily, StateSpaceModels.jl makes this super easy. You only need three functions:</p><p>- <em>forecast</em>: gives you the distribution of the future states, namely the expected value and the covariances.<br>- <em>simulate_scenarios</em>: samples possible future paths from the fitted predictive distribution.<br>- <em>forecast_expected_value</em>: gives you the expected value (mean) of the forecast.</p><p>Let’s run them:</p><pre>S = 1000<br>scenarios_airline      = simulate_scenarios(ssm_airline, test_length_airline, S);<br>forecast_airline       = forecast(ssm_airline, test_length_airline);<br>expected_value_airline = forecast_expected_value(forecast_airline);</pre><p>Now you have:</p><ul><li>forecast_airline: full probabilistic forecast object (including variances).</li><li>scenarios_airline: a bunch of random paths your series might follow.</li><li>expected_value_airline: the most likely forecast path (mean prediction).</li></ul><p>This is where structural models shine: they don’t just spit out a number — they give you a full distribution, respecting all the structure you’ve captured (trend, seasonality, uncertainty, etc.).</p><p>Next up, let’s visualize those forecasts and compare them to the actual test data with the following function (available in my github). It seems quite accurate doesn&#39;t it?</p><pre>plot_forecast(airline_train, airline_test, vec(expected_value_airline), scenarios_airline[:, 1, :], &quot;Airline Passengers&quot;, &quot;month&quot;, &quot;passengers&quot;, &quot;Month&quot;, &quot;Passengers&quot;);</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*93bnzTy1aUCaeZAAgX-7Ig.png" /><figcaption>36 steps ahead forecast and simulation for the log-airline series.</figcaption></figure><p>An astute observer may notice that this is not the original series. What use do we have for the expected log-passengers values? None! We should always recover the original series scale after forecasting. However, we wont do it here…</p><h4>Model comparison</h4><p>Since there is a number of available models in the package, let&#39;s fit and forecast an AutoArima and AutoETS models (yes, there is even automatic forecasting modeling in the package!) to compare their forecasting accuraccy.</p><p>They are quite simple to use.</p><p>First, we need to define and fit the models (in this case there is no need to explicitly call the <em>fit! </em>method).</p><pre>auto_arima_airline = StateSpaceModels.auto_arima(airline_train.passengers; seasonal = 12);<br>auto_ets_airline   = StateSpaceModels.auto_ets(airline_train.passengers; seasonal = 12);</pre><p>After that, we can compute the forecasts and simulations.</p><pre>scenarios_arima_airline       = simulate_scenarios(auto_arima_airline, test_length_airline, S);<br>expected_value_arima_airline  = forecast_expected_value(forecast(auto_arima_airline, test_length_airline));<br><br>scenarios_ets_airline        = simulate_scenarios(auto_ets_airline, test_length_airline, S);<br>expected_value_ets_airline   = forecast_expected_value(forecast(auto_ets_airline, test_length_airline));</pre><p>And compute the mean MAE and CRPS across the full horizon.</p><pre>mean_bsm_crps = mean(StateSpaceModels.evaluate_crps(airline_test.passengers, scenarios_airline[:, 1, :]));<br>mean_bsm_mae  = mean(StateSpaceModels.evaluate_abs_error(airline_test.passengers, vec(expected_value_airline)));<br><br>mean_arima_crps = mean(StateSpaceModels.evaluate_crps(airline_test.passengers, scenarios_arima_airline[:, 1, :]));<br>mean_arima_mae  = mean(StateSpaceModels.evaluate_abs_error(airline_test.passengers, vec(expected_value_arima_airline)));<br><br>mean_ets_crps = mean(StateSpaceModels.evaluate_crps(airline_test.passengers, scenarios_ets_airline[:, 1, :]));<br>mean_ets_mae  = mean(StateSpaceModels.evaluate_abs_error(airline_test.passengers, vec(expected_value_ets_airline)));<br><br>DataFrame(<br>    Model = [&quot;BSM&quot;, &quot;ARIMA&quot;, &quot;ETS&quot;],<br>    CRPS = [mean_bsm_crps, mean_arima_crps, mean_ets_crps],<br>    MAE = [mean_bsm_mae, mean_arima_mae, mean_ets_mae]<br>)</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/410/1*YHSrB0Ks4BOTQ-VuPu_TYg.png" /><figcaption>Mean MAE and CRPS for all models.</figcaption></figure><p>We can see that, for this test-set, the BSM was the worst one with respect to the probabilistic metric (CRPS), while being the second best with respect to the point forecast metric (MAE).</p><h4><strong>Cross-validation</strong></h4><p>Let’s wrap things up with a super useful tool: <strong>cross-validation</strong>.</p><p>If you come from machine learning, you probably know this idea already — we split the data into multiple training and testing segments, fit the model on each training chunk, and evaluate how well it performs on the corresponding test chunk.</p><p>But in <strong>time series</strong>, we can’t just randomly shuffle data around. Time matters.</p><p>That’s why we use <strong>rolling</strong> or <strong>expanding window</strong> cross-validation. We train the model on a moving window of past data, then test it on a fixed-size future window. Repeat this several times and average the results.</p><p>This gives you a much more <strong>robust estimate of forecasting performance</strong> — especially when your series changes over time.</p><p>The image below, taken from another Medium article, illustrates a similar concept, in which each window incorporates new observations, namely the expanding window cross validation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/602/1*40Ogi1Dr1Uab8SKwhm8DFA.png" /><figcaption>Time series cross validation with expanding windows.</figcaption></figure><p><a href="https://medium.com/@soumyachess1496/cross-validation-in-time-series-566ae4981ce4">Cross Validation in Time Series</a></p><p>In StateSpaceModels.jl, it’s as easy as:</p><pre>start_idx = 50<br>cv = StateSpaceModels.cross_validation(ssm_airline, test_length_airline, start_idx);</pre><p>where <em>start_idx</em> represents the first observation of the first window to fit the model.</p><p>This function returns mean MAE and CRPS metrics from all windows by step ahead. This is an interesting plot because it enables us to see how the model forecast performance evolve along the forecast horizon. As we can see below, the forecast accuracy decreases over time. Fair enought! Further in the future, more difficult to foreacst!</p><pre>plot_cv = plot(cv, &quot;Basic Stuctural Model&quot;, size = (1000, 600))</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*RGYTD8pNAjr1qhEGxw7L9g.png" /><figcaption>BSM MAE and CRPS metrics over the forecast horizon.</figcaption></figure><p>Thanks for reading this far! I hope this tutorial helped you understand how powerful and intuitive `StateSpaceModels.jl` can be when it comes to modeling, interpreting, and forecasting time series in Julia. We went from loading data to fitting structural models, diagnosing residuals, extracting components, forecasting future values, and even cross-validating — all within one clean and consistent framework. There are even mode features available in the package that must be checked out!</p><p>Please refer to the article in Arxiv for more information regarding the package.</p><p><a href="https://arxiv.org/abs/1908.01757">StateSpaceModels.jl: a Julia Package for Time-Series Analysis in a State-Space Framework</a></p><p>All the code used here is 100% written by me and available on my GitHub. The text, on the other hand, was written with the help of AI — because good tooling helps with writing too 😉</p><p>If you liked this package, be sure to check out the other time series tools we’ve built at LAMPSPUC. And stay tuned — more tutorials are on the way!</p><p><a href="https://github.com/MathNog/MathNogMediumArticles">https://github.com/MathNog/MathNogMediumArticles</a></p><p><a href="https://github.com/LAMPSPUC">Laboratory of Applied Mathematical Programming and Statistics (LAMPS PUC-Rio)</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=296af2034517" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>