Salta el contingut
 

Regressió lineal

Autor: Joan Puigcerver Ibáñez

Correu electrònic: j.puigcerveribanez@edu.gva.es

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

Regressió lineal

La regressió lineal és un mètode estadístic que intenta modelar la relació entre un conjunt de variables independents \(X\) (característiques o features) i una variable dependent \(Y\) (objectiu o target) mitjançant una funció lineal.

Quan només hi ha una variable independent, es parla de regressió lineal simple. En canvi, quan hi ha més d'una variable independent, es parla de regressió lineal múltiple.

Aquest model lineal es pot expressar com:

\[ Y = w_0 + w_1X_1 + w_2X_2 + \ldots + w_nX_n \]

on:

  • \(Y\) és la variable dependent (objectiu o target),
  • \(X_1, X_2, \ldots, X_n\) són les variables independents (característiques o features),
  • \(w_0, w_1, w_2, \ldots, w_n\) són els coeficients o pesos del model.

L'objectiu de la regressió lineal és trobar els valors dels coeficients que millor s'ajusten a les dades.

Una vegada s'ha obtingut el model, pot ser utilitzat per fer prediccions sobre noves dades.

Exemple de regressió lineal simple

Donades les dades:

\(X\) \(Y\)
0 5
1 15
3 35
7 75
20 205

Gràfica de les dades

Figura 1. Dades representades en un pla cartesià.

La regressió lineal tracta de trobar la recta que millor s'ajusta a les dades.

\[ Y = 10X + 5 \]

Gràfica de la regressió lineal

Figura 2. Recta de regressió lineal.

Mètriques de regressió

Les mètriques són mesures que ens permeten avaluar la qualitat d'un model de regressió, que determinen com de bé s'ajusta el model a les dades i com de precises són les prediccions.

Dades d'exemple

Real (\(Y\)) Predicció (\(\hat{Y}\))
50 52
60 58
70 68
80 85
import pandas as pd

Y = pd.Series([50, 60, 70, 80])
pred_Y = pd.Series([52, 58, 68, 85])

MAE – Error absolut mitjà

L'error absolut mitjà (Mean Absolute Error o MAE) és la mitjana de les diferències absolutes entre les prediccions (\(\hat{y}\)) i els valors reals (\(y\)).

La fórmula per calcular l'error absolut mitjà és:

\[
MAE = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i|
\]

Càlcul de l'error absolut mitjà

mae = np.abs(Y - pred_Y).mean()
print(f'MAE with pandas: {mae:.2f}')
from sklearn.metrics import mean_absolute_error

mae = mean_absolute_error(Y, pred_Y)
print(f'MAE with sklearn: {mae:.2f}')
MAE: 2.75

Quan més xicotet siga el MAE, millor serà el model. És fàcil d'interpretar ja que està expressat en les mateixes que la variable dependent.

MSE – Error quadràtic mitjà

L'error quadràtic mitjà (Mean Squared Error o MSE) és la mitjana de les diferències elevades al quadrat entre les prediccions (\(\hat{y}\)) i els valors reals (\(y\)).

La fórmula per calcular l'error quadràtic mitjà és:

\[
MSE = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2
\]

El MSE és més sensible als errors grans que el MAE, ja que eleva al quadrat les diferències. Això fa que els errors més grans tinguin un pes més gran en la mesura.

Càlcul de l'error quadràtic mitjà

mse = ((Y - pred_Y) ** 2).mean()
print(f'MSE with pandas: {mse:.2f}')
from sklearn.metrics import mean_squared_error

mse = mean_squared_error(Y, pred_Y)
print(f'MSE with sklearn: {mse:.2f}')
MSE: 9.25

RMSE – Arrel de l'error quadràtic mitjà

L'arrel de l'error quadràtic mitjà (Root Mean Squared Error o RMSE) és la arrel quadrada del MSE.

\[
RMSE = \sqrt{\frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2}
\]

Aquesta mètrica és equivalent al MSE però més fàcil d'interpretar, ja que expressada en les mateixes unitats que la variable dependent.

Càlcul de l'arrel de l'error quadràtic mitjà

rmse = np.sqrt(((Y - pred_Y) ** 2).mean())
print(f'RMSE with pandas: {rmse:.2f}')
from sklearn.metrics import root_mean_squared_error

rmse = root_mean_squared_error(Y, pred_Y)
print(f'RMSE with sklearn: {rmse:.2f}')
RMSE: 3.04

R² – Coeficient de determinació

El coeficient de determinació (\(R^2\)) és una mesura que indica com de bé s'ajusta el model als valors reals.

El coeficient de determinació pot prendre valors entre 0 i 1, sent 1 el millor valor possible. Un valor de 0 indica que el model no s'ajusta als valors reals.

El coeficient de determinació es pot calcular com:

\[
R^2 = 1 - \frac{\sum_{i=1}^{n} (y_i - \hat{y}_i)^2}{\sum_{i=1}^{n} (y_i - \bar{y})^2}
\]

On:

  • \(y_i\) són els valors reals.
  • \(\hat{y}_i\) són les prediccions del model.
  • \(\bar{y}\) és la mitjana dels valors reals.

Càlcul del coeficient de determinació \(R^2\)

r2 = 1 - ((Y - pred_Y) ** 2).sum() / ((Y - Y.mean()) ** 2).sum()
print(f'R^2 with pandas: {r2:.2f}')
from sklearn.metrics import r2_score

r2 = r2_score(Y, pred_Y)
print(f'R^2 with sklearn: {r2:.2f}')
R^2: 0.93

Models de regressió lineal

Els models de regressió són algoritmes que permeten trobar la recta que millor s'ajusta a les dades.

Utilitzant la llibreria scikit-learn podem crear models de regressió lineal de manera senzilla.

Dades d'exemple

Utilitzarem un conjunt de dades on es tracta de predir la nota d'un examen a partir de les hores d'estudi i nivell de motivació.

import pandas as pd

X_train = pd.DataFrame({
    'hores_estudiades': [1.57, 9.05, 7.95, 6.96, 3.32, 4.96, 3.62, 8.76, 1.18],
    'motivacio': [93.87, 74.47, 83.88, 51.84, 64.24, 98.59, 65.90, 67.05, 64.49],
})

Y_train = pd.Series(name="nota", data=[63.25, 60.65, 71.27, 50.62, 44.36, 73.50, 51.59, 61.29, 50.41])

X_test = pd.DataFrame({
    'hores_estudiades': [9.56, 7.59, 2.64, 6.51, 2.26, 4.30, 1.42, 1.88, 7.98],
    'motivacio': [81.82, 65.72, 81.67, 90.37, 94.80, 55.50, 75.54, 85.15, 61.98],
})
Y_test = pd.Series(name="nota", data=[71.18, 59.50, 60.22, 67.81, 63.62, 50.48, 46.32, 48.76, 62.21])

Creació del model

Per crear un model de regressió lineal amb scikit-learn podem utilitzar la classe LinearRegression.

Documentació

from sklearn.linear_model import LinearRegression

model = LinearRegression()

Entrenament del model

Per entrenar el model amb les dades, utilitzem el mètode fit de la classe LinearRegression.

model.fit(X_train, Y_train)

Coeficients del model

Un cop entrenat el model, podem obtenir els coeficients de la recta.

  • coef_: coeficients de les variables independents.
  • intercept_: terme independent de la recta.
print(f'Coeficients: {model.coef_}')
print(f'Intercept: {model.intercept_}')
Coeficients: [1.54284641 0.57396092]
Intercept: 8.0617676396745

Predicció amb el model

Per fer prediccions amb el model entrenat, utilitzem el mètode predict.

pred_Y_test = model.predict(X_test)

Avaluació del model

Per avaluar el model, podem utilitzar les mètriques de regressió.

from sklearn.metrics import (
    mean_absolute_error,
    mean_squared_error,
    root_mean_squared_error,
    r2_score,
)

mae = mean_absolute_error(Y_test, pred_Y_test)
mse = mean_squared_error(Y_test, pred_Y_test)
rmse = root_mean_squared_error(Y_test, pred_Y_test)
r2 = r2_score(Y_test, pred_Y_test)

print(f'MAE: {mae:.2f}')
print(f'MSE: {mse:.2f}')
print(f'RMSE: {rmse:.2f}')
print(f'R^2: {r2:.2f}')
MAE: 4.19
MSE: 27.56
RMSE: 5.25
R^2: 0.58

Visualització del model

Podem visualitzar el model de regressió lineal amb una gràfica.

import matplotlib.pyplot as plt
import numpy as np
# Coeficients i intercept
b0 = model.intercept_
b1, b2 = model.coef_

# Crear el gràfic 3D
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')

# Dades reals: scatter plot
ax.scatter(X_train['hores_estudiades'], X_train['motivacio'], Y_train, color='green', label='Conjunt d\'entrenament')
ax.scatter(X_test['hores_estudiades'], X_test['motivacio'], Y_test, color='blue', label='Conjunt de prova')

# Crear una graella per a les variables independents
x1_range = np.linspace(
    min(X_test['hores_estudiades'].min(), X_train['hores_estudiades'].min()),
    max(X_test['hores_estudiades'].max(), X_train['hores_estudiades'].max()),
    10,
)
x2_range = np.linspace(
    min(X_test['motivacio'].min(), X_train['motivacio'].min()),
    max(X_test['motivacio'].max(), X_train['motivacio'].max()),
    10,
)
x1_grid, x2_grid = np.meshgrid(x1_range, x2_range)

# Predicció dels valors de Y per a la graella (plane de regressió)
y_grid = b0 + b1 * x1_grid + b2 * x2_grid

# Superfície de la regressió
ax.plot_surface(x1_grid, x2_grid, y_grid, color='red', alpha=0.5)

# Etiquetes dels eixos
ax.set_xlabel('Hores Estudiades')
ax.set_ylabel('Motivació')
ax.set_zlabel('Nota Final')

# Títol i llegenda
ax.set_title('Gràfic 3D de la Regressió Lineal')
ax.legend()

plt.show()

Gràfica 3D del model de regressió lineal respecte al conjunt de test

Figura 3. Gràfica 3D del model de regressió lineal respecte al conjunt de test.

Codi font

metriques_regressio.py
#!/usr/bin/env python

# --8<-- [start:dades]
import pandas as pd
import numpy as np

Y = pd.Series([50, 60, 70, 80])
pred_Y = pd.Series([52, 58, 68, 85])
# --8<-- [end:dades]

# --8<-- [start:mae_pandas]
mae = np.abs(Y - pred_Y).mean()
print(f'MAE with pandas: {mae:.2f}')
# --8<-- [end:mae_pandas]

# --8<-- [start:mae_sklearn]
from sklearn.metrics import mean_absolute_error

mae = mean_absolute_error(Y, pred_Y)
print(f'MAE with sklearn: {mae:.2f}')
# --8<-- [end:mae_sklearn]

# --8<-- [start:mse_pandas]
mse = ((Y - pred_Y) ** 2).mean()
print(f'MSE with pandas: {mse:.2f}')
# --8<-- [end:mse_pandas]

# --8<-- [start:mse_sklearn]
from sklearn.metrics import mean_squared_error

mse = mean_squared_error(Y, pred_Y)
print(f'MSE with sklearn: {mse:.2f}')
# --8<-- [end:mse_sklearn]

# --8<-- [start:rmse_pandas]
rmse = np.sqrt(((Y - pred_Y) ** 2).mean())
print(f'RMSE with pandas: {rmse:.2f}')
# --8<-- [end:rmse_pandas]

# --8<-- [start:rmse_sklearn]
from sklearn.metrics import root_mean_squared_error

rmse = root_mean_squared_error(Y, pred_Y)
print(f'RMSE with sklearn: {rmse:.2f}')
# --8<-- [end:rmse_sklearn]

# --8<-- [start:r2_pandas]
r2 = 1 - ((Y - pred_Y) ** 2).sum() / ((Y - Y.mean()) ** 2).sum()
print(f'R^2 with pandas: {r2:.2f}')
# --8<-- [end:r2_pandas]

# --8<-- [start:r2_sklearn]
from sklearn.metrics import r2_score

r2 = r2_score(Y, pred_Y)
print(f'R^2 with sklearn: {r2:.2f}')
# --8<-- [end:r2_sklearn]
regressio_lineal.py
#!/usr/bin/env python

# --8<-- [start:dades]
import pandas as pd

X_train = pd.DataFrame({
    'hores_estudiades': [1.57, 9.05, 7.95, 6.96, 3.32, 4.96, 3.62, 8.76, 1.18],
    'motivacio': [93.87, 74.47, 83.88, 51.84, 64.24, 98.59, 65.90, 67.05, 64.49],
})

Y_train = pd.Series(name="nota", data=[63.25, 60.65, 71.27, 50.62, 44.36, 73.50, 51.59, 61.29, 50.41])

X_test = pd.DataFrame({
    'hores_estudiades': [9.56, 7.59, 2.64, 6.51, 2.26, 4.30, 1.42, 1.88, 7.98],
    'motivacio': [81.82, 65.72, 81.67, 90.37, 94.80, 55.50, 75.54, 85.15, 61.98],
})
Y_test = pd.Series(name="nota", data=[71.18, 59.50, 60.22, 67.81, 63.62, 50.48, 46.32, 48.76, 62.21])
# --8<-- [end:dades]


# --8<-- [start:creacio_model]
from sklearn.linear_model import LinearRegression

model = LinearRegression()
# --8<-- [end:creacio_model]

# --8<-- [start:fit]
model.fit(X_train, Y_train)
# --8<-- [end:fit]

# --8<-- [start:predict]
pred_Y_test = model.predict(X_test)
# --8<-- [end:predict]

# --8<-- [start:params]
print(f'Coeficients: {model.coef_}')
print(f'Intercept: {model.intercept_}')
# --8<-- [end:params]

# --8<-- [start:eval]
from sklearn.metrics import (
    mean_absolute_error,
    mean_squared_error,
    root_mean_squared_error,
    r2_score,
)

mae = mean_absolute_error(Y_test, pred_Y_test)
mse = mean_squared_error(Y_test, pred_Y_test)
rmse = root_mean_squared_error(Y_test, pred_Y_test)
r2 = r2_score(Y_test, pred_Y_test)

print(f'MAE: {mae:.2f}')
print(f'MSE: {mse:.2f}')
print(f'RMSE: {rmse:.2f}')
print(f'R^2: {r2:.2f}')
# --8<-- [end:eval]

# --8<-- [start:plot]
import matplotlib.pyplot as plt
import numpy as np
# Coeficients i intercept
b0 = model.intercept_
b1, b2 = model.coef_

# Crear el gràfic 3D
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')

# Dades reals: scatter plot
ax.scatter(X_train['hores_estudiades'], X_train['motivacio'], Y_train, color='green', label='Conjunt d\'entrenament')
ax.scatter(X_test['hores_estudiades'], X_test['motivacio'], Y_test, color='blue', label='Conjunt de prova')

# Crear una graella per a les variables independents
x1_range = np.linspace(
    min(X_test['hores_estudiades'].min(), X_train['hores_estudiades'].min()),
    max(X_test['hores_estudiades'].max(), X_train['hores_estudiades'].max()),
    10,
)
x2_range = np.linspace(
    min(X_test['motivacio'].min(), X_train['motivacio'].min()),
    max(X_test['motivacio'].max(), X_train['motivacio'].max()),
    10,
)
x1_grid, x2_grid = np.meshgrid(x1_range, x2_range)

# Predicció dels valors de Y per a la graella (plane de regressió)
y_grid = b0 + b1 * x1_grid + b2 * x2_grid

# Superfície de la regressió
ax.plot_surface(x1_grid, x2_grid, y_grid, color='red', alpha=0.5)

# Etiquetes dels eixos
ax.set_xlabel('Hores Estudiades')
ax.set_ylabel('Motivació')
ax.set_zlabel('Nota Final')

# Títol i llegenda
ax.set_title('Gràfic 3D de la Regressió Lineal')
ax.legend()

plt.show()
# --8<-- [end:plot]

Comentaris