Régression logistique

Nous allons utiliser une bibliothèque d'apprentissage automatique disponible dans python dénommée scikit-learn. Cette bibliothèque contient de très nombreux algorithmes de classification supervisée et non supervisée.

Réalisation d'une régression logistique

Chargement du jeu de données

À nouveau, nous allons utiliser les iris. Pour cela, nous écrirons :

from sklearn import linear_model, datasets
iris = datasets.load_iris()
X = iris.data[:, :]
Y = iris.target

Remarque : ce sont exactement les mêmes données que dans le TP sur les k plus proches voisins. Seulement, elles sont fournies ici sous forme de matrice car scikit_learn attend des données stockées sous cette forme.

Calculer le modèle de régression logistique

Effectuer la régression logistique est très simple. Il suffit d'écrire :

modele_regression_logistique = linear_model. LogisticRegression ()
modele_regression_logistique. fit (X, Y)

Prédire la classe de données à partir du modèle de régression logistique

On peut maintenant utiliser le modèle pour prédire la classe d'une donnée ou obtenir la probabilité pour cette donnée d'appartenir à chacune des 3 classes.
Pour cela, en supposant que donneesApredire est un tableau contenant les attributs des données à prédire (tableau de la même forme que X), on écrit :

classesPredites = modele_regression_logistique. predict (donneesApredire)

qui met dans classesPredites la classe prédite pour chacune des données contenues dans donneesApredire.

Par exemple, si on applique le modèle sur l'ensemble d'entraînement, on obtient :

modele_regression_logistique. predict (X)
[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1
 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2]

Quand vous le faites, le résultat que vous obtenez peut-être légérement différent. (Pourquoi d'après vous ?)

Pour obtenir les probablités d'appartenance à chacune des classes, on écrit :

probaClasses = modele_regression_logistique. predict_proba (donneesApredire)

probaClasses est alors un tableau dont chaque ligne correspond à une donnée de donneesApredire, chaque élément (colonne) correspondant à la probabilité d'appartenir à une classe.

Par exemple, si on applique le modèle sur l'ensemble d'entraînement, on obtient :

modele_regression_logistique. predict_proba (X)
[[  8.79681649e-01   1.20307538e-01   1.08131372e-05]
 [  7.99706325e-01   2.00263292e-01   3.03825365e-05]
 [  8.53796795e-01   1.46177302e-01   2.59031285e-05]
...
 [  1.45811816e-03   2.98379693e-01   7.00162189e-01]
 [  1.09779827e-03   1.31785617e-01   8.67116585e-01]
 [  1.68397530e-03   2.81057800e-01   7.17258224e-01]]

Quand vous le faites, le résultat que vous obtenez peut-être légérement différent. (Pourquoi d'après vous ?)

Naturellement, pour chaque donnée, la classe prédite correspond à la colonne ayant la plus grande valeur (probabilité).

À faire

  1. Pour commencer, vous effectuez une régression logistique sur tous les iris en mettant bout à bout ce qui est expliqué ci-dessus.
  2. Comme vous le savez, on n'utilise pas toutes les données pour construire un modèle d'apprentissage supervisé. Aussi, on va découper le jeu de données en une partie pour l'entraînement et une partie pour le test (mettez 80% des exemples dans le jeu d'entraînement, prenez garde à ce que le jeu d'entraînement soit stratifié).
    Calculez le taux d'erreur.
    Construisez la matrice de confusion.
  1. On fait du copier/coller des différents éléments indiqués ci-dessus.
    from sklearn import linear_model, datasets
    iris = datasets.load_iris()
    X = iris.data[:, :]
    Y = iris.target
    modele_regression_logistique = linear_model. LogisticRegression ()
    modele_regression_logistique. fit (X, Y)
    classe_predite = modele_regression_logistique. predict (X)
    
  2. # à la suite de ce qui précède
    from random import shuffle
    nb_erreur = 0
    
    # découpe du jeu d'exemples en 80 % pour l'entraînement du modèle, le reste pour son test (voir le TP sur les k plus proches voisins)
    p = 0.8 # 80%
    listeDesIndices = [i for i in range (len (X))]
    shuffle (listeDesIndices)
    # la liste des indices des exemples utilisés pour l'entraînement
    indices_entrainement = listeDesIndices [0:int(p*len(X))]
    # la liste des indices des exemples utilisés pour le test
    indices_test = listeDesIndices [int(p*len(X)):len(X)]
    
    # on entraîne le modèle
    modele_regression_logistique. fit (X [indices_entrainement], Y [indices_entrainement])
    # on prédit la classe des exemples du jeu de test
    classe_predite = modele_regression_logistique. predict (X [indices_test])
    # on calcule le nombre d'erreurs de prédiction
    for i in range (len (indices_test)):
        if Y [indices_test [i]] != classe_predite [i]:
            nb_erreur += 1
    # et on calcule le taux d'erreur demandé
    taux_erreur = nb_erreur / len (indices_test)
    

    Pour la matrice de confusion :

    matrice_confusion = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
    for i in range (len (indices_test)):
        matrice_confusion [Y [indices_test [i]]] [classe_predite [i]] += 1
    

Quelques graphiques

Comme on dit, « un dessin vaut mieux qu'un long discours ». Quand on cherche à extraire des informations de données, il est effectivement essentiel de faire des graphiques qui permettent de voir facilement des choses que des algorithmes ont du mal à trouver, pour comprendre des relations, ... Aussi, allons-nous apprendre à réaliser quelques graphiques simples pour poursuivre ce TP sur la régression logistique. Les notions vues ci-dessous sont très générales et s'appliquent bien entendu en dehors de la régression logistique.

Faire un scatter-plot

Un scatter-plot représente un ensemble de points dans le plan. Python fournit tous les éléments pour que cela se fasse sans difficulté. On fait comme cela :

from random import randint
x = [randint (0, 10) for i in range(10)]
y = [randint (5, 20) for i in range(10)]
import matplotlib.pyplot as plt
plt.figure ()
plt.scatter (x, y)
plt.show ()

On peut donner une taille à chacun des points comme cela :

taille = [i*10 for i in range (10)]
plt.scatter (x, y, s = taille)
plt.show ()

et une couleur :

from matplotlib.colors import ListedColormap
palette = ListedColormap(['#FF0000', '#00FF00', '#0000FF'])
couleur = [i % 3 for i in range (10)]
plt.scatter (x, y, c = couleur, s = taille, cmap = palette)
plt.show ()

Si on trouve que le cercle noir autour des points n'est pas joli, on ajoute edgecolors='none' en paramètre de la fonction plt.scatter() pour le retirer.
On peut aussi dessiner autre chose que des cercles/disques avec le paramètre marker. Celui-ci prend la valeur 'o' par défaut. Des tas d'autres valeurs sont possibles : voir cette page de documentation pour en avoir la liste.
Il faut toujours donner un titre à un graphique et indiquer ce que les axes représentent. Cela se fait à l'aide des fonctions :

Faire un premier graphique des iris

  1. Créer un graphique représentant les iris en fonction de leurs attributs 3 et 4 (longueur et largeur des pétales). Toutes les données doivent être de la même taille, représentées par un même symbole.
  2. Les colorer en fonction de leur classe.

Faire un graphique des iris indiquant ceux dont la classe prédite est fausse

On veut maintenant identifier les données mal prédites.
Refaites le même graphique en colorant les exemples en fonction de leur classe (comme précédemment). Cette fois-ci, vous indiquez les exemples mal classés en utilisant une forme particulière (à vous de choisir quelque chose de joli, qui se voit et qui se comprend au premier coup d'œil).

Classe prédite incertaine

La regression logistique estime la probabilité d'appartenance d'une donnée à chacune des classes. Cette probabilité donne une information quant à l'incertitude de cette prédiction. Pour une donnée, si toutes les probabilités sont faibles sauf une qui est presqu'égale à 1, alors la classe est (presque) certaine. Au contraire, pour certaines données, des probabilités significativement différentes de 0 et de 1 sont estimées ; dans ce cas, la prédiction de la classe est incertaine.
Ici, on dit que si une probabilité est < 0,9 ou > 0,1, alors la classe prédite pour cette donnée est incertaine.
Refaites le même graphique que précédemment. Cette fois-ci vous indiquez les exemples dont la classe est incertaine par une couleur, une taille ou une forme particulière (à votre choix, du moment que l'on voit et comprend au premier coup d'œil).

Quand toutes les couleurs spécifiées sont la même, les points sont rouges. Si on veut spécifier une autre couleur, on peut utiliser son nom : voir ici la liste des couleurs disponibles.

Courbe ROC et AUC

Courbe ROC

La courbe ROC est un moyen très pratique pour estimer la qualité d'une procédure de classification. Nous allons la dessiner. Avant cela, il faut en calculer les points en suivant les indications données en cours, rappelées ci-dessous.

La courbe ROC est un graphique en deux dimensions représentant le taux de vrais positifs par rapport au taux de faux positifs.

Cas de la classification binaire

Pour un problème de classification binaire, on procède comme suit :

  1. On considère un ensemble de n exemples.
  2. On calcule la probabilité d'appartenance à chacune des classes (avec predict_proba(), comme indiqué plus haut).
  3. On trie les exemples selon ces probabilités par ordre décroissant. Pour cela, la fonction argsort dans numpy est bien utile :
    from numpy import argsort
    toto = [6, 8, 2, 1, 4]
    titi = argsort (toto)
    
    titi contient un tableau dont les éléments sont [3, 2, 4, 0, 1]. Ce sont les indices des éléments du tableau qui ont été triés par argsort dans l'ordre croissant : le plus petit est celui d'indice 3, puis celui d'indice 2, ...
    Il ne reste plus qu'à inverser cette liste d'indice, ou à la parcourir du dernier élément au premier.
  4. On compte le nombre d'exemples positifs, noté n+.
  5. Dans une boucle, on parcourt les exemples selon les probabilités décroissantes comme expliqué ci-dessus. On calcule :
    • tvp [i] qui est le nombre de vrais positifs parmi les exemples numérotés de 0 à i (dans l'ordre décroissant)
    • tfp [i] qui est le nombre de faux positifs parmi les exemples numérotés de 0 à i (dans l'ordre décroissant)
    i prend les valeurs comprises entre 0 et n.
  6. On divise chacun des éléments de tvp et tfp par n+.

Reste à faire le graphique avec tfp en abscisses et tvp en ordonnées.

Cas de la classification non binaire

S'il y a k classes, on réalise k courbes ROC. Pour réaliser chacune de ces courbes, l'une des classes est considérée comme positive, toutes les autres comme négatives.

  1. Écrire une fonction python qui renvoie l'ensemble des points (x,y) qui permettent de dessiner la courbe ROC.
    Cette fonction prend trois paramètres :

    1. les (vraies) classes des données utilisées.
    2. les classes prédites des données utilisées.
    3. les probabilités d'appartenance à chacune des deux classes.
    Attention : la courbe ROC se calcule pour un problème de classification supervisée binaire (à 2 classes). Les iris comportent trois classes. On considérera donc 3 problèmes binaires : setosa ou autre, virginica ou autre, versicolor ou autre. On calculera la courbe ROC pour chacun de ces trois problèmes binaires.
  2. Quand cette fonction est écrite, utilisez-là pour dessiner les 3 courbes ROC sur un même graphique.
  3. Écrire une fonction qui calcule l'AUC. Calculez cette AUC pour chacune des 3 courbes ROC.

AUC (Area under curve)

L'AUC est la surface en dessous de la courbe ROC. Elle se calcule très facilement comme une intégrale de Riemann, en faisant la somme des surfaces des rectangles situés sous la courbe ROC. Avec les valeurs de tvp calculées précédemment, ce calcul est immédiat.