mes-scripts-de-ml/01-fondamentaux/06-regression_logistique.py

158 lines
7.4 KiB
Python

import time
import numpy as np
import sklearn
from sklearn.linear_model import LogisticRegression
from sklearn import datasets
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
###############################################################################
# 06-regression_logistique.py
# @title: Fondamentaux - Apprentissage par régression logistique
# @project: Mes scripts de ML
# @lang: fr
# @authors: Philippe Roy <philippe.roy@ac-grenoble.fr>
# @copyright: Copyright (C) 2023 Philippe Roy
# @license: GNU GPL
###############################################################################
###
# Commandes NumPy :
# - np.array : créer un tableau à partir d'une liste de listes
# - np.c_ : concatène les colonnes des tableaux
# - np.linspace : créer un tableau 1D de la valeur de début à la valeur de fin avec n valeurs
# - np.meshgrid : créer un tableau 2D avec l'ensemble des combinaisons allant des deux valeurs de début aux deux valeurs de fin
# - .reshape : reformater la tableau avec le nombre de lignes et le nombre de colonnes
###
###
# Commandes Scikit-Learn :
# - sklearn.linear_model.LogisticRegression : créer un modèle de régression logistique
# - sklearn.linear_model.LogisticRegression(multi_class="multinomial", solver="lbfgs", C=10) : créer un modèle de régression logistique multi-classes du type Softmax
# - .fit : entrainement du modèle
# - .predict : prédiction du modèle
###
###############################################################################
# Initialisation
###############################################################################
# Init du temps
t_debut = time.time()
# Init des plots
fig = plt.figure(figsize=(15, 5))
fig.suptitle("Classificateur par régression logistique")
donnees1_ax = fig.add_subplot(131) # Observations : x1 et cibles : y1
donnees2_ax = fig.add_subplot(132) # Observations : x1, x2 et cibles : y11 (type de marque)
donnees3_ax = fig.add_subplot(133) # Observations : x1, x2 et cibles : y (type de marque)
###############################################################################
# Observations
###############################################################################
# Observations d'apprentisage
iris = sklearn.datasets.load_iris() # Jeu de données Iris
x1 = iris['data'][:, 2].reshape(-1, 1) # Longueur de pétale
x2 = iris['data'][:, 3].reshape(-1, 1) # Largeur de pétale
X = iris['data'][:, (2, 3)] # Matrice de données
y1 = (iris["target"] == 2).astype(np.int32) # Si Iris virginica -> 1 sinon -> 0
y = (iris["target"]) # Type d'Iris
# Plot x1,y1 (largeur de pétale, probabilité d'être Iris virginica)
donnees1_ax.plot(x2[y1==1], y1[y1==1], "g^" , label="Iris virginica")
donnees1_ax.plot(x2[y1==0], y1[y1==0], "rs", label="Iris non-virginica")
# Plot x1,x2 et y1 (longeur de pétale, largeur de pétale et probabilité d'être Iris virginica (format de la marque))
donnees2_ax.plot(x1[y1==1], x2[y1==1], "g^" , label="Iris virginica")
donnees2_ax.plot(x1[y1==0], x2[y1==0], "rs", label="Iris non-virginica")
# Plot x1,x2 et y (longeur de pétale, largeur de pétale et type de Iris (format de la marque))
donnees3_ax.plot(x1[y==0], x2[y==0], "yo", label="Iris setosa (0)")
donnees3_ax.plot(x1[y==1], x2[y==1], "bs" , label="Iris versicolor (1)")
donnees3_ax.plot(x1[y==2], x2[y==2], "g^" , label="Iris virginica (2)")
# Nouvelles observations
x1_new=np.linspace(0, 8, 1000).reshape(-1, 1)
x2_new=np.linspace(0, 3.5, 1000).reshape(-1, 1)
x1_new_mg, x2_new_mg = np.meshgrid(x1_new, x2_new)
X_new = np.c_[x1_new_mg.ravel(), x2_new_mg.ravel()]
###############################################################################
# Phase d'apprentissage
###############################################################################
model_1d = sklearn.linear_model.LogisticRegression() # Modèle régression logistique
model_1d.fit(x2, y1) # Entrainement
model_2d = sklearn.linear_model.LogisticRegression() # Modèle régression logistique
model_2d.fit(X, y1) # Entrainement
model = LogisticRegression(multi_class="multinomial", solver="lbfgs", C=10) # Modèle régression logistique du type Softmax (multi-classes, multinomial)
model.fit(X, y) # Entrainement
###############################################################################
# Phase d'inférence
###############################################################################
# Analyse 1D
y1_predict_1d=model_1d.predict(x2_new) # Prédiction
y1_proba_1d = model_1d.predict_proba(x2_new) # Probabilité
frontiere_decision_1d = x2_new[y1_proba_1d[:, 1] >= 0.5][0][0]
# Analyse 2D
y1_predict_2d=model_2d.predict(X_new) # Prédiction
y1_proba_2d = model_2d.predict_proba(X_new) # Probabilité
model_2d_a = -model_2d.coef_[0][0] / model_2d.coef_[0][1] # coef directeur de la frontière de décision
model_2d_b = -model_2d.intercept_ / model_2d.coef_[0][1] # ordonée à l'origine de la frontière de décision
frontiere_decision_2d = model_2d_a * x1_new + model_2d_b
y1_proba_2d_contour = y1_proba_2d[:, 1].reshape(x1_new_mg.shape)
# Analyse multi-classes (Softmax)
y_predict=model.predict(X_new) # Prédiction
y_proba = model.predict_proba(X_new) # Probabilité
y_proba_contour0 = y_proba[:, 0].reshape(x1_new_mg.shape)
y_proba_contour1 = y_proba[:, 1].reshape(x1_new_mg.shape)
y_proba_contour2 = y_proba[:, 2].reshape(x1_new_mg.shape)
y_predict_map = y_predict.reshape(x1_new_mg.shape)
###############################################################################
# Résultats
###############################################################################
# Plot x1,y1 (largeur de pétale, probabilité d'être Iris virginica)
donnees1_ax.set_title("Binomiale - Frontière de décision à 1 entrée")
donnees1_ax.plot(x2_new, y1_proba_1d[:,1], 'b:', label="Probabilité")
donnees1_ax.plot(x2_new, y1_predict_1d, 'y-', label="Prédictions")
donnees1_ax.set_xlabel(r'$x_2$'+" - Largeur de pétale")
donnees1_ax.set_ylabel(r'$y$'+" - Probabilité d'être Iris virginica")
donnees1_ax.legend(loc="center left")
# Plot x1,x2 et y1 (longeur de pétale, largeur de pétale et probabilité d'être Iris virginica)
donnees2_ax.set_title("Binomiale - Frontière de décision à 2 entrées")
donnees2_ax.set(xlim=(0, 7.5), ylim=(0, 3.5))
donnees2_ax.plot(x1_new, frontiere_decision_2d, "k--")
donnees2_ax.set_xlabel(r'$x_1$'+" - Longueur de pétale")
donnees2_ax.set_ylabel(r'$x_2$'+" - Largeur de pétale")
donnees2_ax.legend(loc="upper left")
donnees2_contour = donnees2_ax.contour(x1_new_mg, x2_new_mg, y1_proba_2d_contour, cmap=plt.cm.brg) # Contour pour la classe Iris versicolor (type 1)
donnees2_ax.clabel(donnees2_contour, inline=1, fontsize=10)
# Plot x1,x2 et y (longeur de pétale, largeur de pétale et type de Iris (format de la marque))
donnees3_ax.set_title("Multinomiale (régression Softmax)")
donnees3_ax.set(xlim=(0, 7.5), ylim=(0, 3.5))
donnees3_ax.set_xlabel(r'$x_1$'+" - Longueur de pétale")
donnees3_ax.set_ylabel(r'$x_2$'+" - Largeur de pétale")
donnees3_ax.legend(loc="upper right")
custom_cmap = ListedColormap(['#fafab0','#9898ff','#a0faa0'])
donnees3_ax.contourf(x1_new_mg, x2_new_mg, y_predict_map, cmap=custom_cmap)
donnees3_contour = donnees3_ax.contour(x1_new_mg, x2_new_mg, y_proba_contour1, cmap=plt.cm.brg) # Contour pour la classe Iris versicolor (type 1)
donnees3_ax.clabel(donnees3_contour, inline=1, fontsize=10)
plt.show()
# Performances
print ("Frontière de décision 1D sur la largeur de pétale : "+str(round(frontiere_decision_1d, 6)))
print ("Temps : "+str(time.time()-t_debut))