Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Regression analysis: least squares and regularisation

Heinrich Heine University Düsseldorf

Once a dataset has been prepared well enough (missing data replaced, suitably rescaled, new features defined), you can venture into a regression analysis. A regression describes the relationship between two variables. One of them is treated as depending on the other, and the dependence is made explicit as a functional relationship – a mathematical map that computes, approximately, the values of the one variable from the values of the other.

An example, before we fix the vocabulary. In the Iris dataset we suspect a relationship between the length and the width of a petal: longer petals ought to be wider as well. We look at the cloud of points and draw a straight line through it – at first entirely by eye.

<Figure size 800x500 with 1 Axes>

What we read off is the rule of thumb

predicted_petal_width = petal_length * 0.5 - 1

and with that, the example already contains every ingredient of a regression:

  1. There is a function that computes the target variable from the explanatory variable – here x↦0.5 x−1x \mapsto 0.5\,x - 1.

  2. The function has parameters, here the numbers 0.5 and -1, and we obtained them from the data, not from theory.

  3. The prediction is not exact. For every single flower something is left over, and we have to keep track of it.

The third point is why a regression is not simply a function. And the second immediately raises the question of whether 0.5 and -1 really are the best numbers – the rest of this chapter answers it. (To anticipate: they are not. The method of least squares finds 0.416 and -0.363 and pushes the mean squared error down from 0.167 to 0.042.)

A regression model consists of:

  • regressors xix_i, that is, a (kk-variate) ‘independent’ variable, the explanatory variable (here kk is the number of regressors, not the number of clusters as in k-means)

  • a regressand yiy_i, that is, a (univariate) ‘dependent’ variable, the target variable

  • parameters β1,…,βk\beta_1,\dots,\beta_k

  • error terms ϵi\epsilon_i

  • Yi=f(Xi,β)+ϵiY_i = f(X_i, \beta) + \epsilon_i

For concrete data (xi,yi)(x_i,y_i) and a given regression model, yi−f(xi,β)y_i - f(x_i, \beta) is also called the ii-th residual.

Some fixed notation will help here. A sample consists of instances x1,…,xNx_1,\dots,x_N together with the associated values of the target variable y1,…,yNy_1,\dots,y_N, which in machine learning are called labels. If the regressor is multidimensional we write xijx_{ij} for the value of the jj-th feature at the ii-th instance – the first index counts the rows of the table, the second the columns. The model’s prediction is

yipred:=f(xi;β),y_i^{pred} := f(x_i; \beta),

so it depends on the estimated parameters and changes with them. The residuals are then exactly the differences yi−yipredy_i - y_i^{pred}, and so they are all we ever see of the error terms: the error term ϵ\epsilon is a random variable we never observe; the residuals are its instances, once we have committed to a β\beta. That is why you check a regression model by looking at its residuals – they ought to look like a sample from the distribution you assumed for ϵ\epsilon.

A regression analysis consists of setting up a regression model and then estimating the function ff that fits the data best – the one with the smallest residuals.

Particular regression methods differ in the kind of function ff they permit. In linear regression, ff is linear in the parameters β\beta.

Least squares

The least squares estimator is a maximum likelihood estimator if the error terms are assumed to be normally distributed (the model for this is at the end of the section Linear regression).

In general, β0\beta_0 can be computed with the formula

β0=mean(y)−β1mean(x).\beta_0 = \texttt{mean}(y) - \beta_1\texttt{mean}(x).

So to determine β1\beta_1 first in the general case, you shift the data by their means, replacing xix_i by xi′=xi−mean(x)x_i' = x_i - \texttt{mean}(x) and yiy_i by yi′=yi−mean(y)y_i' = y_i - \texttt{mean}(y). That yields the formula

β1=∑i=1N(xi−mean(x))(yi−mean(y))∑i=1N(xi−mean(x))2\beta_1 = \dfrac{\sum_{i=1}^N (x_i - \texttt{mean}(x))(y_i - \texttt{mean}(y))}{\sum_{i=1}^N (x_i - \texttt{mean}(x))^2}

Divide numerator and denominator by NN and you are looking at two quantities we already know: on top, the empirical covariance of xx and yy; underneath, the empirical variance of xx (both in the nn-version, see the remark For data in the chapter Covariance). The slope of the line of best fit is therefore

β1=Cov⁡(x,y)σ2(x),β0=mean(y)−β1 mean(x).\beta_1 = \dfrac{\operatorname{Cov}(x,y)}{\sigma^2(x)}, \qquad \beta_0 = \texttt{mean}(y) - \beta_1\,\texttt{mean}(x).

This is the form that is quickest to compute by hand, and it also says something: the slope is the covariance, normalised by the spread of the regressor. Compare it with the correlation coefficient ρ=Cov⁡(x,y)/(σ(x)σ(y))\rho = \operatorname{Cov}(x,y) / (\sigma(x)\sigma(y)) from the chapter Covariance and you get β1=ρ σ(y)/σ(x)\beta_1 = \rho\,\sigma(y)/\sigma(x) – correlation and regression slope differ only by the ratio of the two standard deviations.

line equation is y = 8.912806*x + -6.929693
<Figure size 1000x1000 with 1 Axes>

The method of least squares can also be run directly in Scipy (so there is no need to remember the formulas or the derivation):

From the Scipy documentation:

scipy.optimize.curve_fit

Use non-linear least squares to fit a function, f, to data.

Assumes ydata = f(xdata, *params) + eps.

<Figure size 1000x1000 with 1 Axes>

Note that this is not the direction of maximal variance – that too minimises distances, but the distances of the data points (x,y)(x,y) to the line. Linear regression minimises not the distances of the data points to the line but the residuals, which are the distances along the yy-axis. The plot above shows this: the green bars run parallel to the yy-axis, not perpendicular to the line (which is what the distance to the line would be).

This also makes clear that regression is an asymmetric method: regressor and regressand cannot be swapped without changing the model. When searching for the line along the direction of maximal variance, by contrast, the xx- and yy-coordinates are on an equal footing and the solution is invariant under rotations.

Linear regression

The second definition looks more general, but it reduces to the first. Append a constant one to the regressors and the bias to the weights:

X+:=(1,X1,…,Xk),w+:=(w0,w1,…,wk),(w+)TX+=wTX+w0.X_+ := (1, X_1, \dots, X_k), \qquad w_+ := (w_0, w_1, \dots, w_k), \qquad (w_+)^T X_+ = w^T X + w_0.

This is the usual tacit convention: for x=(a,b)x = (a,b) we then have f(x,w)=wTx=(w0,w1,w2)⋅(1,a,b)=w0+w1a+w2bf(x,w) = w^T x = (w_0,w_1,w_2)\cdot(1,a,b) = w_0 + w_1 a + w_2 b. The w0w_0 term (denoted β0\beta_0 earlier) is also called the bias term (intercept). It has nothing to do with the bias of an estimator from the previous chapter; the word simply has two meanings in the machine learning literature.

A simple linear regression can be carried out with Scipy.stats:

Slope: 8.912806
Intercept: -6.929693
Correlation coefficient: 0.959514
p-value: 0.00000000000000000000000000046030
Standard error of the slope: 0.377630

The five return values are worth a moment. slope is the slope β1\beta_1 and intercept is the intercept β0\beta_0, that is, the bias – those are the two parameters we were after. r is the correlation coefficient between xx and yy (see Covariance and dependence); its square r2r^2 is the proportion of the variance of yy that the model explains, and it turns up again further down as the coefficient of determination R2R^2. p belongs to a test that Scipy ran along the way, unasked (more on that in a moment). And se, despite the name, is not a squared error but the standard error of the slope estimate: the estimated standard deviation of slope if you were to repeat the whole experiment with a fresh sample. As a rule of thumb, the true slope lies within slope ± 2⋅\pm\ 2 \cdot se with a probability of about 95 %95\,\%.

Scipy has also run a test for us straight away: a two-sided test with the null hypothesis that the slope slope equals 0. Since the pp-value is considerably smaller than α=0.05\alpha = 0.05, we reject the hypothesis and assume a slope ≠0\neq 0. (For now we simply take this as given, without going further into statistical tests – or we ignore this part of the Scipy.stats output.)

Before we write this down in general, here is an example in which the trick is immediately obvious. The Iris dataset gives us the petal length X1X_1 and the petal width X2X_2, and we would like to predict the sepal length YY. Now YY presumably depends less on length or width individually than on the area of the petal – and that, up to a shape factor, is the product of the two. So we build a new regressor X3:=X1X2X_3 := X_1 X_2 and use it to compute a perfectly ordinary linear regression Y=w1X3+w0+ϵY = w_1 X_3 + w_0 + \epsilon.

Slope: 0.150646
Intercept: 4.970482
Correlation coefficient: 0.857300
Standard error of the slope: 0.007436
<Figure size 1000x600 with 1 Axes>

All we have done here is apply the function ϕ(X1,X2):=X1X2\phi(X_1, X_2) := X_1 X_2 to the regressors before the regression. The model stays linear – in the weights ww, and that is what counts.

For comparison: the petal length alone gives r=0.872r = 0.872, a little more than the area does. A basis function expansion is not automatically the better regressor just because it is more complicated – it is a hypothesis about the data, and whether it helps is an empirical question. Such preprocessing steps have a name of their own.

So in a linear regression only one thing is linear: the function ff, in the variables ww. This also lets us read the earlier tacit replacement of xx by (1,x)(1,x) as a basis function expansion, with ϕ(x)=(1,x)\phi(x) = (1,x).

Since a linear regression analysis is very easy to carry out, the central question in linear regression becomes how to set up the model – and in particular the basis function – from the data.

The case w=(w0,w1)w = (w_0, w_1) is called a simple linear regression, the more general case a multiple linear regression, because there the regressor is a multivariate independent variable XX. Scipy.stats’s linear regression can only do the simple kind!

So we now turn to multiple linear regression with Scikit-learn:

<Figure size 1000x1000 with 1 Axes>

From the Scikit-learn documentation:

sklearn.linear_model.LinearRegression

From the implementation point of view, this is just plain Ordinary Least Squares (scipy.linalg.lstsq) or Non Negative Least Squares (scipy.optimize.nnls) wrapped as a predictor object.

Seen this way, Scikit-learn is not ‘better’ than Scipy, just a layer on top of it.

One problem in choosing a basis function for a regression model is overfitting. With too many parameters available, the data can certainly be fitted very well, but the model does not generalise well. In practice this is checked by fitting on a training dataset and then evaluating on a test dataset.

<Figure size 1000x1000 with 1 Axes>
<Figure size 1000x1000 with 1 Axes>
<Figure size 1000x1000 with 1 Axes>

A linear regression model can also be formulated as

P(y∣X,θ)=N(wTX,σ2)P(y|X, \theta) = \mathcal{N}(w^T X, \sigma^2)

or, with a basis function expansion,

P(y∣X,θ)=N(wTϕ(X),σ2)P(y|X, \theta) = \mathcal{N}(w^T \phi(X), \sigma^2)

Here the θi\theta_i are the parameters of the normally distributed errors ϵi\epsilon_i.

An example application

We look at the Melbourne Housing Market dataset by Anthony Pino. It is licensed CC BY-NC-SA 4.0 and is therefore exempt from the licence of these notes: re-use only non-commercially and under the same terms.

To get started, we clean the data a little.

(34857, 21)
Index(['Suburb', 'Address', 'Rooms', 'Type', 'Price', 'Method', 'SellerG',
       'Date', 'Distance', 'Postcode', 'Bedroom2', 'Bathroom', 'Car',
       'Landsize', 'BuildingArea', 'YearBuilt', 'CouncilArea', 'Lattitude',
       'Longtitude', 'Regionname', 'Propertycount'],
      dtype='str')
Index(['Price', 'Distance', 'Bedroom2', 'Bathroom', 'Car', 'Landsize',
       'BuildingArea', 'Propertycount', 'Age'],
      dtype='str')
R^2 = 50.355458% of the variance is explained by the linear model

Before we interpret the coefficients, we check the model assumption that the error terms ϵi\epsilon_i scatter around 0 independently of xx: the residuals yi−f(xi,β)y_i - f(x_i, \beta), plotted against the prediction, ought to lie around 0 with no discernible pattern. A funnel means that the spread depends on the price; curvature, that a linear relationship is not enough.

<Figure size 640x480 with 1 Axes>
Loading...

Interpretation:

If one house is a year older than another, the associated price difference is about 6081 (Australian) dollars. If one house is a kilometre closer to the centre (which Melbourne calls the CBD) – that is, its Distance variable is smaller by 1 – the associated price difference is about 27377 dollars.

Categorical regressors: one-hot encoding

While cleaning the data we turned every text column into a category – and then quietly left them out of the model, since numerical_columns holds only the float columns. Yet Type (h = house, u = unit, that is a flat, t = townhouse) ought to have something to say about the price.

Simply setting h=1h = 1, u=2u = 2, t=3t = 3 would be wrong: Type is on a nominal scale, with no order and no equal spacings, and the regression would compute with both (see Descriptive statistics, section Level of measurement). Instead, every level gets a 0/10/1 column of its own, the one-hot encoding; pandas calls the columns dummies. One of the columns is left out (drop_first=True): whatever is neither u nor t is h, and the omitted level becomes the reference category against which the other coefficients are measured. If all three columns were kept, their sum would always be 1 – the same information as the intercept β0\beta_0 – and the parameters would no longer be uniquely determined (the dummy variable trap).

    Type_t  Type_u
2      0.0     0.0
4      0.0     0.0
6      0.0     0.0
11     0.0     0.0
14     0.0     0.0
R^2 without Type: 50.4%
R^2 with Type:    50.7%
Loading...

The two new coefficients read as a price difference relative to a house with otherwise equal features: in the model, a flat with the same area, the same age and the same distance to the centre is around 178 000178\,000 dollars cheaper, a townhouse around 67 00067\,000 dollars. That R2R^2 barely rises is, on reflection, no surprise: most of what Type reveals about the price already sits in BuildingArea, Landsize and Bedroom2 – flats are, above all, smaller. So the encoding did not invent the feature’s value; it only made it readable.

An ordinal feature (a condition “poor / medium / good”, say) could, by contrast, fairly have been encoded as 1,2,31, 2, 3 – there the order is real, and only the equal spacings are an assumption. For nominal features with very many levels (Suburb, say, with several hundred suburbs) one-hot encoding becomes unwieldily wide; then other ideas are needed, which go beyond this course.

To close this example application, two more pictures of linear regression in the variables distance and age – first one of them, then both:

<Figure size 640x480 with 1 Axes>
<Figure size 1000x1000 with 1 Axes>

Given how these data look, a polynomial regression would help here as well. We put together a pipeline to compute it.

<Figure size 1000x1000 with 1 Axes>

Regularisation

With only a few support points in the training data, overfitting becomes all the more likely. We look at this in a few examples:

<Figure size 1000x1000 with 3 Axes>

The trick of regularisation is not to fit the training data too well.

L2 regularisation, also known as ridge regression, minimises a penalty term P=α∑i=1nβi2P = \alpha \sum_{i=1}^n \beta_i^2 as an additional optimisation objective while searching for the best fit to the regression problem. Here α\alpha is a free parameter that sets the strength of the penalty.

The name L2 comes from PP being the square of the L2 norm.

The same idea of penalising large model weights goes by the name weight decay in deep learning (the course Introduction to Deep Learning).

There is also L1 regularisation, which uses the summand ∣βi∣|\beta_i| instead of βi2\beta_i^2 and so corresponds to the L1 norm; it is also known as lasso regularisation.

L1 and L2 balls, image by Wikipedia User Nicoguaro, CC BY 4.0
<Figure size 1000x1000 with 5 Axes>

The practical difference between L1 and L2: the L1 penalty sets coefficients to exactly 0 (those are the vertices of the L1 ball in the figure), so it selects regressors. With the polynomial terms x,x2,…,x8x, x^2, \dots, x^8 as regressors for the synthetic data above, you can see this directly:

Ridge coefficients: [10.6  -7.56  2.39 -1.92 -0.35  0.04 -0.72  0.36]
Lasso coefficients: [11.34 -8.76  0.71 -0.    0.   -0.    0.   -0.  ]
Exactly 0 under Lasso: 5 of 8 coefficients

Summary

A regression is a model Y=f(X,β)+ϵY = f(X,\beta) + \epsilon; linear means linear in the parameters, which is why basis functions and one-hot columns do not take us out of the model. The method of least squares yields β1=Cov⁡(x,y)/σ2(x)\beta_1 = \operatorname{Cov}(x,y)/\sigma^2(x), and it is maximum likelihood when the error terms are normally distributed; residual plots check that assumption. Regularisation (ridge, lasso) penalises large coefficients and is the remedy for overfitting when there are too many basis functions.

Video: Crash Course Statistics Regression