aerial photography of road

Ajuster un modèle de régression linéaire – la descente de gradient

Nous savons tous que Scikit-Learn peut ajuster des modèles pour nous. Mais savons-nous ce qu’il fait réellement lorsque nous appelons .fit() ?

Aujourd’hui, nous allons écrire un ensemble de fonctions qui mettent en œuvre la descente de gradient pour ajuster un modèle de régression linéaire. Ensuite, nous comparerons les poids de notre modèle à ceux d’un modèle ajusté par Sklearn.

Contexte nécessaire :
•Ajustement = trouver le biais d’un modèle et le(s) coefficient(s) qui minimisent l’erreur.
•Erreur = erreur quadratique moyenne (EQM). La moyenne des différences au carré entre les valeurs réelles et prédites, dans un ensemble de données.
Régression linéaire simple = Un modèle basé sur l’équation d’une ligne, « y=mx+b ». Il prend une seule caractéristique en entrée, applique un biais et un coefficient, et prédit y.
•En outre, le coefficient et le biais sont parfois appelés « poids ».
•Préparation de l’ensemble de données
•Téléchargez l’ensemble de données sur le logement en Californie à partir de kaggle, et chargez-le dans un cadre de données.

import pandas as pd
df = pd.read_csv('california-housing-dataset.csv')
df.head()

Pour cet ensemble de données, nous essayons généralement de prédire  median_house_value en utilisant toutes les caractéristiques.

Mais dans notre cas, nous sommes concernés par l’ajustement d’une régression linéaire simple (qui ne prend qu’une seule caractéristique d’entrée), nous choisirons donc median_income comme seule caractéristique en ignorant le reste. Cela ne créera pas le meilleur modèle possible, mais cela simplifiera la mise en œuvre de la descente de gradient.

Ici, on commence par compter les entrées dans l’ensemble de données :

len(df)
#=> 20640

C’est beaucoup de données. Réduisons la taille de 75% :

df = df.sample(frac=0.25)
len(df)
#=> 5160

C’est plus facile à gérer.
Rassemblez notre caractéristique d’entrée et nos variables :

X = df['median_income'].tolist()
y = df['median_house_value'].tolist()

L’algorithme du gradient désigne un algorithme d’optimisation différentiable. Il est par conséquent destiné à minimiser une fonction réelle différentiable définie sur un espace euclidien. L’algorithme est itératif et procède par améliorations successives.
Ainsi, la descente de gradient utilise le gradient de la fonction d’erreur pour prédire dans quelle direction le coefficient, m, et le biais, b, doivent être mis à jour pour réduire l’erreur sur un ensemble de données donné.
Le gradient de chaque poids, m et b, est calculé séparément. Un gradient est calculé en faisant la moyenne de la dérivée partielle d’un poids sur tous les exemples.
La direction et la pente du gradient déterminent dans quelle direction les poids sont mis à jour, et de combien. Cette dernière est également influencée par un hyperparamètre, le taux d’apprentissage.
Cet algorithme itère de manière répétée sur l’ensemble d’apprentissage et met à jour les poids jusqu’à ce que la fonction de coût soit à son minimum.

Implémentation :
Vous pouvez trouver les dérivées partielles de la fonction MSE (comme ci-dessous), partout sur internet, nous ne les dériverons donc pas ici.

Nous allons implémenter ce qui précède avec ce qui suit, tout en itérant sur l’ensemble des données.

m_gradient += -(2/N) * x * (y - y_hat)
b_gradient += -(2/N) * (y - y_hat)

Écrivons une fonction qui prend les poids actuels, les caractéristiques, les étiquettes et le taux d’apprentissage, et produit les poids mis à jour.

def bias_coef_update(m, b, X, Y, learning_rate) :
m_gradient = 0
b_gradient = 0
N = len(Y)

# itérer sur les exemples
for idx in range(len(Y)) :
x = X[idx]
y = Y[idx]

# prédire y avec le biais et le coefficient actuels
y_hat = (m * x) + b
m_gradient += -(2/N) * x * (y - y_hat)
b_gradient += -(2/N) * (y - y_hat)

# utiliser le gradient avec le taux d'apprentissage pour modifier le biais et le coefficient
nouveau_coef = m - (m_gradient * taux_d'apprentissage)
nouveau_biais = b - (b_gradient * taux d'apprentissage)

return new_coef, new_bias

La dernière partie de cette fonction multiplie le gradient par le taux d’apprentissage et utilise le résultat pour mettre à jour les poids actuels. Plus le taux d’apprentissage est élevé, plus le modèle s’ajuste rapidement, au prix de la recherche du minimum local exact (remarque : il n’atteindra jamais le vrai minimum).
Écrivez une autre fonction qui applique itérativement la fonction ci-dessus pendant un nombre déterminé d’époques. Il s’agit d’une approche moins sophistiquée (pour plus de simplicité) que le retour des poids ajustés à une pente de gradient prédéterminée.

def run(epoch_count=1000):
    # store output to plot later
    epochs = []
    costs = []
        
    m = 0 
    b = 0 
    learning_rate = 0.01    for i in range(epoch_count):
        m, b = bias_coef_update(m, b, X, y, learning_rate)
        print(m,b)
        
        C = cost(b, m, x_y_pairs)
        
        epochs.append(i)
        costs.append(C)
    
    return epochs, costs, m, bepochs, costs, m, b = run()

Sortons le coût final et les poids du modèle ajusté :

print(m)
print(b)
print(costs[-1])# Je les ai arrondis moi-même pour qu'ils soient plus agréables à regarder.
#=> 46,804
#=> 19,963
#=> 7,261,908,362

Et voyez comment il s’est amélioré au fil des époques :

import matplotlib.pyplot as pltplt.xlabel('Epoch')
plt.ylabel('Error')
plt.suptitle('Cost by epoch')plt.plot(epochs,costs, linewidth=1)

Cool. Nous pouvons voir qu’il a fait la plupart de ses progrès dans les 100 premières époques.
Ajuster une régression linéaire avec Scikit-Learn
Maintenant nous allons vérifier les mêmes valeurs dans un modèle ajusté avec Scikit-Learn.

Remodeler les caractéristiques :

import numpy as np
X_array = np.array(X).reshape(5160,1)

Ajustez un modèle :

from sklearn.linear_model import LinearRegression
model = LinearRegression()
model.fit(X_array,y)

Vérifiez les poids et les erreurs :

from sklearn.metrics import mean_squared_errorm = model.coef_[0]
b = model.intercept_mse = mean_squared_error(y_test, y_pred, sample_weight=None, multioutput='uniform_average')print(m)
print(b)
print(mse)# rounded
#=> 42,324
#=> 41,356
#=> 7,134,555,443

Comment avons-nous fait ?
Scikit-Learn a dépassé notre modèle d’une petite marge, 7 134 555 443 VS 7 261 908 362, mais nous sommes assez proches.
Notre biais est également très différent de celui trouvé par Scikit-Learn, 41 356 VS 19 963.
Sans chercher à savoir comment les valeurs ont changé après chaque époque (ce que vous pouvez facilement faire), je pense que nous pourrions améliorer notre modèle en diminuant le taux d’apprentissage et en augmentant le nombre d’époques.

Merci pour votre lecture ! Si vous souhaitez lire nos prochains articles autour de la Data Science, vous pouvez nous suivre sur FacebookLinkedIn et Twitter pour être notifié lorsqu’un nouvel article est publié !

Cet article a été inspiré par : Fit a Linear Regression Model with Gradient Descent from Scratchde GreekDataGuy