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

KTH JupyterHub Open In Colab Binder

General

The purpose of regression is to model the relationship between a dependent variable (output) and one or more independent variables (inputs). It is one of the most common techniques in statistical modeling, used both to understand and to predict the dependent variable.

Independent and Dependent Variables

A Linear Regression Model

With a single independent variable xix_i, the simplest model is a straight line, f(xi)=axi+bf(x_i) = a x_i + b, where aa is the slope of the line and bb is the intercept.

We can write this model in a more general form:

f(xi′)=xi′Tβf(\mathbf{x}_i') = \mathbf{x}_i'^T \boldsymbol{\beta}

Where:

The same form covers several independent variables: then xi′=(1,xiT)T\mathbf{x}_i' = (1, \mathbf{x}_i^T)^T, and β\boldsymbol{\beta} holds the intercept and one slope per variable.

Residuals

In regression, the residual for each data point is the difference between the observed value yiy_i and the value predicted by the model f(xi)f(\mathbf{x}_i). The residual for the ii-th point can be written as:

ei=yi−f(xi)e_i = y_i - f(\mathbf{x}_i)

Residuals give us a way to evaluate how well the model fits the data. A residual close to zero means that the model’s prediction is close to the observed value, while a residual far from zero indicates a larger error.

Notebook Cell
Source
<Figure size 800x600 with 1 Axes>

In the figure above, the vertical blue lines are the residuals: they show how much of each data point the model f(x)f(\mathbf{x}) leaves unexplained.

Least Squares Estimation

To fit a function f(x)f(\mathbf{x}) to the data points {(xi,yi)}i\{(\mathbf{x}_i, y_i)\}_i, we minimize the sum of squared errors (SSE):

LSSE=∑i=1Nei2=∑i=1N(yi−f(xi))2\mathcal{L}_{\text{SSE}}=\sum_{i=1}^N e_i^2 = \sum_{i=1}^N (y_i - f(\mathbf{x}_i) )^2

This process is known as least squares. L\mathcal{L} is known as a loss function. The loss measures how poorly a model fits the data, and different regression lines have different losses. By minimizing the loss function, we find the function that best fits the data according to the selected loss function.

If the errors eie_i are independent and normally distributed with the same variance, the maximum likelihood estimation (MLE) is equivalent to least squares minimization, making this approach optimal under these assumptions.

Minimizing the SSE gives the same parameters as minimizing the more commonly reported mean squared error (MSE), which divides the SSE by the number of data points NN:

LMSE=1N∑i=1Nei2=1N∑i=1N(yi−f(xi))2\mathcal{L}_{\text{MSE}}=\frac{1}{N}\sum_{i=1}^N e_i^2 = \frac{1}{N}\sum_{i=1}^N (y_i - f(\mathbf{x}_i) )^2

Fit a Linear Model to Data

A general pattern in machine learning is to use an optimizer to minimize a loss function. Here, we define the SSE loss explicitly and minimize it with the scipy.optimize.minimize function. Nothing in this procedure is specific to straight lines, so later in this chapter we use the same code to fit non-linear models. Try changing the number of data points N in the code, and see how the fitted slope and intercept change.

Optimized slope: 1.9385466095021187, Optimized intercept: -0.5014420349612432
<Figure size 640x480 with 1 Axes>

Notes on Optimization

By default, scipy.optimize.minimize uses the BFGS algorithm, a refinement of gradient descent, to find an optimal set of parameters (a,b)(a, b). Gradient descent is an iterative algorithm for finding the minimum of a function; here, the function is the loss, and we search for the parameters that minimize it.

Concept of Gradient Descent

The basic idea of gradient descent is to start from a guess of the parameters, and then repeatedly change them by a small step in the direction that decreases the loss. That direction is given by the derivative of the loss or, when the model has several parameters, by its gradient. We first look at a model with a single parameter, where the ordinary derivative is enough, and then extend the same code to two parameters.

Gradient Descent with One Parameter

If we fix the intercept at b=0b = 0, the model becomes a line through the origin, f(x)=axf(x) = a x, with the slope aa as its only parameter. The loss is then a function of aa alone,

L(a)=∑i=1N(yi−axi)2,\mathcal{L}(a) = \sum_{i=1}^N (y_i - a x_i)^2 ,

and its derivative with respect to aa is

dLda=−2∑i=1Nxi(yi−axi).\frac{d\mathcal{L}}{da} = -2 \sum_{i=1}^N x_i (y_i - a x_i) .

The derivative is the slope of the loss curve at the current value of aa. If it is positive, the loss increases with aa, so we should decrease aa. If it is negative, we should increase aa. Gradient descent therefore takes a step against the derivative,

anew=a−ηdLda,a_{\text{new}} = a - \eta \frac{d\mathcal{L}}{da} ,

where the learning rate η\eta is a small positive number that sets the step size. Near the minimum, the loss curve flattens and the derivative approaches zero, so the steps become smaller.

Below, we write a general gradient_descent function, which takes a function that calculates the derivative, a starting guess, the learning rate and the number of steps. We use it to fit the slope, starting from a=0a = 0.

<Figure size 1200x500 with 2 Axes>
After 20 steps: slope 1.261
Minimum of the loss: slope 1.265

In the left panel, gradient descent starts at a=0a = 0, where the loss curve is steep, so the first step is large. The steps then shrink as the curve flattens towards the minimum. In the right panel, the line turns towards the data with each step. Because it is forced through the origin, it cannot follow the data very well, as the data were generated with an intercept of -0.5.

Try changing the learning rate. With learning_rate = 0.07, the steps overshoot the minimum and jump back and forth across it, and with learning_rate = 0.09, every step lands further away from the minimum than the previous one.

Gradient Descent with Two Parameters

If we also let the intercept vary, we get back the full model f(x)=ax+bf(x) = a x + b, whose loss L(a,b)\mathcal{L}(a, b) depends on two parameters. Instead of a single derivative, we now have one partial derivative per parameter. The partial derivative with respect to aa is the ordinary derivative with respect to aa, calculated as if bb were a constant, and vice versa:

∂L∂a=−2∑i=1Nxi(yi−(axi+b)),∂L∂b=−2∑i=1N(yi−(axi+b)).\frac{\partial \mathcal{L}}{\partial a} = -2 \sum_{i=1}^N x_i \left( y_i - (a x_i + b) \right) , \qquad \frac{\partial \mathcal{L}}{\partial b} = -2 \sum_{i=1}^N \left( y_i - (a x_i + b) \right) .

With b=0b = 0, the first of them is the derivative from the one-parameter case. Together, the partial derivatives form the gradient, a vector that points in the direction in which the loss increases most steeply:

∇L(a,b)=[∂L∂a∂L∂b].\nabla \mathcal{L}(a, b) = \begin{bmatrix} \frac{\partial \mathcal{L}}{\partial a} \\ \frac{\partial \mathcal{L}}{\partial b} \end{bmatrix} .

Gradient descent takes a step against the gradient, which means that each parameter is updated with its own partial derivative:

[anewbnew]=[ab]−η[∂L∂a∂L∂b].\begin{bmatrix} a_{\text{new}} \\ b_{\text{new}} \end{bmatrix} = \begin{bmatrix} a \\ b \end{bmatrix} - \eta \begin{bmatrix} \frac{\partial \mathcal{L}}{\partial a} \\ \frac{\partial \mathcal{L}}{\partial b} \end{bmatrix} .

The same holds for any number of parameters. If we collect the parameters in a vector β\boldsymbol{\beta}, the update rule is

βnew=β−η∇L(β).\boldsymbol{\beta}_{\text{new}} = \boldsymbol{\beta} - \eta \nabla \mathcal{L}(\boldsymbol{\beta}) .

In the code, only the loss and its derivative change: the derivative becomes a gradient function that returns both partial derivatives, and the starting guess becomes a vector (a,b)=(0,0)(a, b) = (0, 0). The gradient_descent function is the same as before. We draw its steps on a contour plot of the loss as a function of aa and bb.

<Figure size 1000x600 with 2 Axes>
After 100 steps: slope 1.903, intercept -0.462
Minimum of the loss: slope 1.951, intercept -0.491

After 100 steps, gradient descent is still some distance from the minimum: the loss forms a long, narrow valley, and the steps shrink as they follow its nearly flat floor.

Scikit-learn

Defining a loss function explicitly and handing it to a general-purpose optimizer is a good way to explain how machine learning works, but it is not how linear regression is usually done in practice. The least-squares problem has an analytical solution, which software packages use instead. In practice, we use a package such as scikit-learn, one of the most popular machine learning libraries in Python, which implements a wide range of supervised and unsupervised methods.

Linear Regression using Scikit-learn

Below, we fit the same data as above with scikit-learn’s LinearRegression class.

<Figure size 640x480 with 1 Axes>

Regression with Basis Functions

So far our model has been a straight line, but many relationships are curved. This section shows that the same recipe, choosing a model and minimizing its loss, also fits curved relationships. The trick is to turn each input xx into several new features, such as xx, x2x^2 and x3x^3, and to fit a weighted sum of these features. The model is still linear in its parameters, so the fitting does not change; only the features do. The functions that create the features are called basis functions.

Polynomial Basis Functions

A common choice is polynomial basis functions: from each input xx we create the terms x2x^2, x3x^3, and so on, which let the model capture curved patterns. Instead of fitting a linear model y=ax+by = a x + b, we then fit a polynomial of degree nn. This transforms the input space into a higher-dimensional space where the relationship between xx and yy may be linear, even if it appears non-linear in the original space. Below, we fit a polynomial of degree 7, which has 8 parameters, to 50 data points. Try changing the number of data points N and the degree of the polynomial degree in the code, and see how the fit changes.

<Figure size 640x480 with 1 Axes>

Optional: Gaussian Basis Functions

This last part of the chapter is optional reading. Another choice is Gaussian basis functions, also known as radial basis functions (RBF). Each one is a bell-shaped bump around a center, so a weighted sum of them can follow localized, non-linear behavior in the data. Below, we use 11 Gaussian bases and let the optimizer fit not only their weights but also their centers and their common width. That makes the model non-linear in its parameters, but it is still fitted in exactly the same way, by minimizing the SSE.

<Figure size 640x480 with 1 Axes>

With many basis functions, such flexible models can fit the noise in the data as well as the underlying pattern. The next chapter introduces regularization, which controls model complexity and so counteracts such overfitting.

What you should know after reading this chapter

You should be able to:

  1. Explain what regression is used for, and identify the dependent variable and the independent variables in a regression problem.

  2. Write a linear model as f(xi′)=xi′Tβf(\mathbf{x}_i') = \mathbf{x}_i'^T \boldsymbol{\beta}, and explain how adding a constant 1 to the input vector handles the intercept.

  3. Define a residual. Calculate the residuals, the sum of squared errors (SSE) and the mean squared error (MSE) for a small dataset by hand.

  4. Explain least squares as minimizing a loss function, and state under which assumption about the errors it is the same as maximum likelihood estimation.

  5. Describe how gradient descent minimizes a loss: how the derivative of the loss gives the step for a single parameter, how the gradient extends this to several parameters, the update rule βnew=β−η∇L(β)\boldsymbol{\beta}_{\text{new}} = \boldsymbol{\beta} - \eta \nabla \mathcal{L}(\boldsymbol{\beta}), and the role of the learning rate η\eta.

  6. Explain how polynomial basis functions let a regression model fit non-linear relationships, while the fitting procedure (minimizing a loss) stays the same.