Implémentation manuelle d'une régression linéaire avec PyTorch

Cet article montre comment construire pas à pas une régression linéaire simple avec PyTorch, sans utiliser de couche ou d'optimiseur intégré. On génère un jeu de données synthétique, on implémente un chargeur de mini-lots, on définit un modèle de prédiction, une fonction de coût et une méthode d'optimisation, puis on trace l'évolution de la perte au cours de l'entraînement.

  1. Génération du jeu de données

On utilise make_regression de scikit-learn pour créer des données unidimensionnelles avec un bruit, puis on les convertit en tenseurs PyTorch.

import torch
from sklearn.datasets import make_regression
import matplotlib.pyplot as plt

torch.manual_seed(42)

def genere_donnees(n=100, bruit=10):
    X, y, coef = make_regression(
        n_samples=n,
        n_features=1,
        noise=bruit,
        coef=True,
        bias=14.5,
        random_state=0
    )
    X = torch.tensor(X, dtype=torch.float32)
    y = torch.tensor(y, dtype=torch.float32).view(-1, 1)
    return X, y, float(coef)

def affiche_nuage(X, y):
    plt.scatter(X.numpy(), y.numpy())
    plt.show()

if __name__ == '__main__':
    X, y, _ = genere_donnees()
    affiche_nuage(X, y)

  1. Chargeur de mini-lots personnalisé

Pour parcourir les données par groupes, on écrit un générateur qui mélange les indices et renvoie des lots de taille choisie.

def chargeur_batches(X, y, taille_batch):
    n = X.size(0)
    indices = torch.randperm(n)
    for debut in range(0, n, taille_batch):
        fin = min(debut + taille_batch, n)
        idx = indices[debut:fin]
        yield X[idx], y[idx]

  1. Modèle, fonction de coût et optimiseur

Le modèle est réduit à un poids et un biais, initialisés comme des paramètres autograd. La fonction de coût est l'erreur quadratique moyenne et la mise à jour s'effectue par descente de gradient manuelle.

class RegressionLineaire:
    def __init__(self):
        self.poids = torch.tensor(0.1, requires_grad=True, dtype=torch.float32)
        self.biais = torch.tensor(0.0, requires_grad=True, dtype=torch.float32)

    def prediction(self, X):
        return self.poids * X + self.biais

def erreur_quadratique(y_pred, y_reel):
    return torch.mean((y_pred - y_reel) ** 2)

def mise_a_jour(modele, taux):
    with torch.no_grad():
        modele.poids -= taux * modele.poids.grad
        modele.biais -= taux * modele.biais.grad
        modele.poids.grad.zero_()
        modele.biais.grad.zero_()

  1. Entraînement et vsiualisation

La boucle d'entraînement applique l'inférence, le calcul de la perte, la rétropropagation et la mise à jour des paramètres sur chaque lot. On trace ensuite la droite ajustée et l'évolution de la perte.

def entrainer():
    X, y, pente_reelle = genere_donnees()
    modele = RegressionLineaire()
    epochs = 100
    taux = 1e-2
    taille_batch = 16
    historique = []

    for epoch in range(epochs):
        perte_epoch = 0.0
        for batch_x, batch_y in chargeur_batches(X, y, taille_batch):
            sortie = modele.prediction(batch_x)
            perte = erreur_quadratique(sortie, batch_y)
            perte.backward()
            mise_a_jour(modele, taux)
            perte_epoch += perte.item()
        historique.append(perte_epoch)

    plt.figure()
    plt.scatter(X.numpy(), y.numpy(), label='Données')

    x_trace = torch.linspace(X.min(), X.max(), 500).view(-1, 1)
    y_estime = modele.prediction(x_trace).detach().numpy()
    y_theorique = x_trace.numpy() * pente_reelle + 14.5

    plt.plot(x_trace.numpy(), y_estime, label='Modèle entraîné', color='red')
    plt.plot(x_trace.numpy(), y_theorique, label='Valeur réelle', color='green')
    plt.legend()
    plt.grid(True)
    plt.show()

    plt.figure()
    plt.plot(range(epochs), historique)
    plt.title("Évolution de la perte")
    plt.xlabel("Époque")
    plt.ylabel("Perte")
    plt.grid(True)
    plt.show()

entrainer()

Étiquettes: PyTorch Régression Linéaire descente de gradient Scikit-learn Matplotlib

Publié le 16 août à 20h57