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.
- 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)
- 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]
- 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_()
- 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()