Skip to content
Snippets Groups Projects
Commit ae066d46 authored by Alexandre Roulois's avatar Alexandre Roulois
Browse files

Linear regression w/ normal equation & gradient descent

parent 105e5e79
No related branches found
No related tags found
1 merge request!31Linear regression w/ normal equation & gradient descent
This diff is collapsed.
%% Cell type:markdown id:5a51118b-c70f-43b9-b8a4-44021fb2547b tags:
# Une droite de régression avec la méthode de descente de gradient
%% Cell type:markdown id:ed5517d4-621a-4ec3-8030-cf68f1ae4749 tags:
Dernièrement, nous avons calculé une droite de régression avec deux méthodes :
- les moindres carrés ;
- l’équation normale.
Une autre méthode, adaptable à des problèmes plus complexes que celui qui nous concerne, consiste à effectuer la recherche des meilleurs paramètres par tâtonnement en commençant par des valeurs aléatoires. Les paramètres s’ajustent au fur et à mesure que la fonction de coût, généralement l’erreur quadratique moyenne (MSE), diminue. On appelle cette méthode la descente de gradient.
%% Cell type:markdown id:888792c9-c1d0-44a4-bb8d-0d893f718874 tags:
## Pré-requis
%% Cell type:markdown id:2d4b6850-ee1c-4d02-8976-11823c22df8f tags:
Sans rentrer dans les détails, la descente de gradient nécessite trois paramètres :
- des valeurs aléatoires pour initialiser $\theta$ ($m$ et $b$) ;
- un nombre d’itérations suffisant pour converger ;
- un taux d’apprentissage, noté $\eta$ (*eta*), qui ne soit ni trop faible, afin d’éviter de ralentir l’entraînement, ni trop élevé, afin d’éviter le sur-entraînement.
En plus de ces paramètres, la descente de gradient ne peut se calculer que sur des **données standardisées**. Effectuons tout d’abord une copie des données sur le recensement des manchots en Antarctique :
%% Cell type:code id:e0d61329-50d8-46e2-8fd0-c25ce3cf26a8 tags:
``` python
import pandas as pd
import numpy as np
# csv file
df = pd.read_csv("./files/penguin-census.csv")
# data
coords = df.loc[:,["body_mass_g","flipper_length_mm"]]
coords = coords.dropna()
coords = coords.to_numpy()
# matrices with coords
X = np.c_[coords[:, 0]]
Y = np.c_[coords[:, 1]]
```
%% Cell type:markdown id:b774c6df-6275-4ff3-9578-9cbe72c91bd8 tags:
## Standardisation des données
%% Cell type:markdown id:caec2d0c-7d6e-4f38-bb27-7014ae6e9996 tags:
Avant tout, utilisons la classe `StandardScaler` pour centrer-réduire nos données :
%% Cell type:code id:726b112d-69ff-497a-9b48-95dadcc43b77 tags:
``` python
from sklearn.preprocessing import StandardScaler
# scaler: Z score normalization
scaler = StandardScaler()
# scaling
X_scaled = scaler.fit_transform(X)
Y_scaled = scaler.fit_transform(Y)
```
%% Cell type:markdown id:d3decd71-3f97-4ee2-8aec-f1427f70bd08 tags:
## Insertion d’une dimension neutre
%% Cell type:markdown id:74a8b53a-f77e-4bc1-80cd-20b134cef1d3 tags:
Comme lors du calcul de la droite de régression avec l’équation normale, et parce que la descente de gradient implique au final un produit matriciel avec une matrice de dimensions $(2, 1)$ il est nécessaire de rajouter une dimension aux coordonnées sur l’axe des abscisses ($X$) :
%% Cell type:code id:dd00c6eb-5fb4-434b-a8f1-b8297a954c3c tags:
``` python
# x0 = 1
X_scaled = np.c_[
np.ones(X.shape),
X_scaled
]
```
%% Cell type:markdown id:d604f6e2-8f5e-49e6-8cb5-95603290e20c tags:
## Paramètres obligatoires
%% Cell type:markdown id:28c5d6a1-4b62-4a67-b24e-6e104f8ee5ce tags:
Fournissons à présent des valeurs aux paramètres obligatoires de la descente de gradient :
%% Cell type:code id:a888aba4-fd8e-4bed-b651-e67f7588885f tags:
``` python
# learning rate
eta = 0.1
# number of iterations
n_steps = 1000
# rows in X
m = len(X_scaled)
# random values to initialize theta
theta = np.random.randn(2, 1)
```
%% Cell type:markdown id:2060fa28-d0eb-4c4d-b43e-6180e8dc9fd0 tags:
## Résolution de la formule
%% Cell type:markdown id:1abc5ee1-e520-47e7-a4f6-bba96a7ef344 tags:
Appliquons maintenant la formule :
$$\theta = \theta - \eta \nabla_\theta \text{MSE}(\theta)$$
%% Cell type:code id:7898e6a0-7948-410f-9a04-50d3e29174f8 tags:
``` python
# for each step
for step in range(n_steps):
# the gradients
gradients = 2/m * X_scaled.T.dot(X_scaled.dot(theta) - Y_scaled)
# theta
theta = theta - eta * gradients
```
%% Cell type:markdown id:fe954153-9c56-424f-80b5-8f76faf66a94 tags:
Afin de s’assurer que les valeurs de $\theta$ sont correctes, comparons-les avec la solution obtenue grâce à l’équation normale :
%% Cell type:code id:aadedff9-0f3e-4f40-bf53-ba5e1ed15dcc tags:
``` python
theta_norm = np.linalg.inv(X_scaled.T.dot(X_scaled)).dot(X_scaled.T).dot(Y_scaled)
print(
theta[0].round(10) == theta_norm[0].round(10),
theta[1].round(10) == theta_norm[1].round(10),
sep="\n"
)
```
%% Cell type:markdown id:f43554c1-e312-49ea-9756-0fb9dc77890a tags:
Et pour une vérification graphique :
%% Cell type:code id:3856b9db-64d8-428b-b18e-a51da24b3d80 tags:
``` python
import matplotlib.pyplot as plt
import seaborn as sns
# slope and intercept
m = theta[1]
b = theta[0]
# points
X = list(map(float, X_scaled[:, 1]))
Y = list(map(float, Y_scaled))
# predictions
Y_pred = [ float(m * x + b) for x in X ]
# vizualisation
ax = plt.subplots()
ax = sns.lineplot(x=X, y=Y_pred, color="fuchsia")
ax = sns.scatterplot(x=X, y=Y, color="seagreen")
ax.set(xlabel="Body mass (g)", ylabel="Flipper length (mm)")
sns.despine()
plt.show()
```
%% Cell type:code id:b3c65cc8-48c2-4b0a-8c4e-5f3ba2d6b834 tags:
``` python
```
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment