Objectif principal Ce TP vise autant à comprendre la régression logistique qu’à maîtriser la syntaxe Python utilisée en Machine Learning.
Comment travailler ce TP
Ce TP est guidé et commenté ligne par ligne.
# ============================================================
# 1) IMPORTS — comprendre la syntaxe Python
# ============================================================
# import <bibliothèque> as <alias>
# → permet d'utiliser un nom court pour appeler la bibliothèque
import numpy as np # NumPy : calcul numérique, tableaux, matrices
import matplotlib.pyplot as plt # matplotlib : affichage de graphiques
Nous allons créer artificiellement :
# np.random.seed(0)
# → fixe le générateur aléatoire
# → garantit les mêmes résultats à chaque exécution
np.random.seed(0)
# np.random.randn(200, 2)
# → crée une matrice NumPy de taille (200 lignes, 2 colonnes)
# → chaque valeur suit une loi normale centrée réduite
X = np.random.randn(200, 2)
# X[:, 0] : toutes les lignes, colonne 0
# X[:, 1] : toutes les lignes, colonne 1
#
# (X[:,0] + X[:,1] > 0) :
# → comparaison élément par élément
# → retourne un tableau de booléens (True / False)
#
# .astype(int) :
# → convertit True → 1 et False → 0
y = (X[:, 0] + X[:, 1] > 0).astype(int)
# Vérification des dimensions
print("Dimensions de X :", X.shape) # (200, 2)
print("Dimensions de y :", y.shape) # (200,)
print("10 premières valeurs de y :", y[:10])
Dimensions de X : (200, 2) Dimensions de y : (200,) 10 premières valeurs de y : [1 1 1 1 1 1 1 1 1 0]
# Visualisation du dataset
# plt.scatter(x, y) : nuage de points
# c=y : couleur dépend de la classe
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="bwr")
plt.xlabel("x1")
plt.ylabel("x2")
plt.title("Données : classification binaire")
plt.show()
La sigmoïde transforme une valeur réelle en une probabilité entre 0 et 1.
def sigmoid(z):
"""
Fonction sigmoïde :
σ(z) = 1 / (1 + exp(-z))
z peut être :
- un nombre (float)
- un tableau NumPy (vectorisation automatique)
NumPy applique exp() élément par élément sans boucle.
"""
return 1 / (1 + np.exp(-z))
# Test visuel de la sigmoïde
# np.linspace(a, b, n) → n valeurs régulièrement espacées entre a et b
z = np.linspace(-10, 10, 200)
plt.plot(z, sigmoid(z))
plt.xlabel("z")
plt.ylabel("σ(z)")
plt.title("Fonction sigmoïde")
plt.grid(True)
plt.show()
def model(X, w, b):
"""
X : matrice (n_samples, n_features)
w : vecteur des poids (n_features,)
b : biais (scalaire)
np.dot(X, w) :
- effectue le produit matriciel
- résultat : vecteur de taille (n_samples,)
+ b :
- broadcasting NumPy
- b est ajouté à chaque élément du vecteur
"""
z = np.dot(X, w) + b
return sigmoid(z)
def log_loss(y, y_pred):
"""
y : vraies classes (0 ou 1)
y_pred : probabilités prédites
np.clip :
- empêche les valeurs 0 ou 1
- évite log(0)
"""
epsilon = 1e-15
y_pred = np.clip(y_pred, epsilon, 1 - epsilon)
# Toutes les opérations sont vectorisées :
# aucune boucle for n'est utilisée
loss = -np.mean(
y * np.log(y_pred) +
(1 - y) * np.log(1 - y_pred)
)
return loss
def gradients(X, y, y_pred):
"""
Calcul des dérivées partielles :
X.T : transposée de la matrice X
len(y) : nombre d'exemples
La fonction retourne deux valeurs :
- dw (vecteur)
- db (scalaire)
"""
n = len(y)
dw = np.dot(X.T, (y_pred - y)) / n
db = np.mean(y_pred - y)
return dw, db
# Initialisation des paramètres
# np.zeros(X.shape[1]) :
# → crée un vecteur de zéros de taille = nombre de colonnes de X
w = np.zeros(X.shape[1])
b = 0.0
learning_rate = 0.1
epochs = 1000
losses = []
for i in range(epochs):
# 1) Calcul des probabilités
y_pred = model(X, w, b)
# 2) Calcul du coût
loss = log_loss(y, y_pred)
losses.append(loss)
# 3) Calcul des gradients
dw, db = gradients(X, y, y_pred)
# 4) Mise à jour (syntaxe raccourcie -=)
w -= learning_rate * dw
b -= learning_rate * db
# Affichage final
print("Poids w :", w)
print("Biais b :", b)
print("Dernière valeur de la loss :", losses[-1])
Poids w : [3.77977079 3.87593655] Biais b : -0.004352079320227879 Dernière valeur de la loss : 0.1111669520011031
# Courbe de convergence
plt.plot(losses)
plt.xlabel("Itérations")
plt.ylabel("Log-Loss")
plt.title("Convergence de l'entraînement")
plt.grid(True)
plt.show()
def predict_class(X, w, b, threshold=0.5):
"""
threshold : seuil de décision
(proba >= threshold) :
- retourne True / False
.astype(int) :
- convertit en 1 / 0
"""
proba = model(X, w, b)
return (proba >= threshold).astype(int)
y_pred_class = predict_class(X, w, b)
accuracy = np.mean(y_pred_class == y)
print("Accuracy =", accuracy)
Accuracy = 0.995
# Génération des valeurs x1
x1 = np.linspace(X[:, 0].min(), X[:, 0].max(), 200)
# Calcul de x2 à partir de l'équation de la droite
x2 = - (w[0] * x1 + b) / w[1]
plt.scatter(X[:, 0], X[:, 1], c=y, cmap="bwr")
plt.plot(x1, x2, "k--", label="Frontière de décision")
plt.xlabel("x1")
plt.ylabel("x2")
plt.legend()
plt.title("Régression Logistique — Frontière de décision")
plt.show()