Régression linéaire

Une régression linéaire est un algorithme supervisé de regression.

Qu’est-ce que c’est

Regression


Fonction coût

Minimiser la fonction coût


1. Équation normale

La méthode des moindres carrés (ordinary least square [OLS] en anglais) permet de mesurer l’erreur entre les prédictions et les valeurs réelles. On peut trouver la valeur de A qui minimise cette erreur en résolvant l’équation:

\[\text{Équation normale: }\\ a = (X^T X)^{-1} ⋅ X^T y\]
preuve
python
# Add intercept
m  = len(X)
b  = np.ones((m,1))
Xb = np.concatenate([b, X], axis=1)

# Fit
tmp1 = Xb.T.dot(Xb)
tmp2 = Xb.T.dot(y)

'''
Matrix inverse is slow and introduces unnecessary error
Anytime you see the math written as: x = A^-1 * b
you instead want: x = np.linalg.solve(A, b)
'''
a = np.linalg.solve(tmp1, tmp2)
print('coefficient:', a)

# Predict
y_pred = np.dot(Xb, a)
import statsmodels.api as sm

# Add intercept
Xb = sm.add_constant(X)

# Fit
est = sm.OLS(y, Xb).fit()
print('coefficient: ', est.params)
est.summary()

# Predict
y_pred = est.predict(Xb)
from sklearn.linear_model import LinearRegression

model = LinearRegression()
model.fit(Xb, Y)

a = model.coef_
b = model.intercept_
print(a, b)

# Predict
y_pred = model.predict(Xb)

Linear Regression OLS.ipynb
8 ways to do linear regression and their speed

2. Gradient Descent

Une autre approche pour minimiser la fonction coût est le gradient descent.

  1. On initialise les coefficients A aléatoirement.
  2. On calcule la dérivée de la fonction coût (ce qui nous indique si en changeant légèrement A, le coût augmente ou diminue, et si c’est de beaucoup ou de peu).
  3. On met à jour A en soustrayant une fraction de la valeur obtenue. À la prochaine itération, l’erreur sera donc plus petite que précédemment.
  4. On répète les étapes 2 et 3, jusqu’à éventuellement converger vers la “vraie” valeur — c’est au développeur de décider quand s’arrêter
\[\text{Répéter jusqu'à convergence } \\ \begin{aligned} a &= a - \alpha \times J(a)' \\ &= a - \alpha \times \left[ \frac{1}{2n} \sum_{i=1}^n (a_i x_i - y_i )^2 \right]'\\ &= a - \alpha \times \frac{1}{n} \sum_{i=1}^n (a_i x_i - y_i ) \cdot x_i \end{aligned}\]

Notons que pour plus de simplicité, on minimise ici la moitié de la moyenne des erreurs, ce qui revient au même que minimiser la somme des erreurs mais nous évite de travailler avec des fractions.

python
def gradientDescent(X, y, theta, alpha, epochs):
    m = len(y)

    for i in range(epochs):
        gradient = (X.T.dot(X.dot(theta) - y)) / m
        theta   -= alpha * gradient

    return theta

b, a = gradientDescent(Xb, Y, theta=theta, alpha=0.01, epochs=5000)
print(b,a)

Linear Regression Gradient.ipynb

Gradient descent Gotchas


Analyser les résultats: exemple

Une usine de production de thé (qui met du thé en sachet) a parfois des sachets qui se cassent pendant le processus d’emballage et souhaite réduire ces défauts. À première vue, il semble que le nombre de sachets cassés augmente au fur et à mesure que le nombre d’arrêts de production augmente.

  1. Calculer la ligne de regression

    Ici, avec 0 arrêts, on s’attend à 6.3 défauts. Puis pour chaque arrêt supplémentaire, à 1.944 défauts supplémentaires.

    Avec 3 arrêts, on s’attend donc à (6.306 + 1.944*3 = 12.138) 12 défauts en moyenne.
    Notons qu’on ne peut effectuer des prédictions que sur la fourchette de données qu’on connaît: on ne pourra pas estimer le nombre de défauts pour 25 arrêts, puisqu’on a pas de données sur cet intervalle (mais de 0 à 9 uniquement).

  2. Vérifier r²
    La formule obtenue est la ligne la plus appropriée entre les variables indépendantes et la variable dépendante.

    Pour déterminer si cette relation est significative (si on peut vraiment se fier aux prédictions effectuées avec), on peut regarder la valeur p: si elle est inférieure à 0.05, on conclut qu’il existe une relation significative entre les variables.
    On peut également regarder r², si la valeur est élevée alors la ou les variable(s) indépendante(s) explique(nt) en grande partie la variable dépendante.

    python
     # sum of square of residuals
     ssr = np.sum((y_pred - y_actual)**2)
    
     #  total sum of squares
     sst = np.sum((y_actual - np.mean(y_actual))**2)
    
     # R2 score
     r2_score = 1 - (ssr/sst)
     
     from sklearn.metrics import r2_score
    
     r_square = r2_score(y_test, y_pred)
     
  3. Analyser les résidus
    Les résidus sont la différence entre les valeurs observées (Y) et les valeurs prédites (résultat de f(X)).

    • Les résidus devraient être normalement distribués.

    • S’il y a des valeurs extrêmes (comme ci-dessous), une solution possible est de les supprimer. Comparer le modèle obtenu avec et sans.

    • S’il s’agit d’un problème de non-linéarité, essayer une regression polynomiale.


6.306 + 1.944*3 = 12.138

12.138 - 21.45 = 9.238 12.138 + 21.45 = 15.038

Avec 3 arrêts, on produira entre 9 et 15 défauts 95% du temps. </pre>

Pour & Contre

Optimisations

Régularisation L2

(aka Regression Ridge)

La régularisation est une méthode permettant de réduire la variance d’un modèle (overfitting). Pour ce faire, on ajoute une pénalité à la fonction coût: la somme des carrés des coefficients fois λ — où λ est un hyperparamètre (ex 0.7). Plus la valeur de λ est élevée, plus la pénalité est importante, et plus on incite le modèle à trouver des coefficients proches de 0 et donc à être moins sensible aux petits changements de X.

\[\text{Regularisation L2}: \\ \frac{\lambda}{2n} \sum_{i=1}^n x_i^2 \\[15pt] \text{Fonction coût}: \\ (aX - y)^2 + L2\]

Régularisation L1

(aka Regression Lasso)

Même idée que L2, la seule différence étant que la pénalité est calculée avec la valeur absolue des coefficients.

\[\text{Regularisation L1}: \\ \frac{\lambda}{n} \sum_{i=1}^n | x_i |\]