classification automatique

C’est quoi ?

Je vous propose aujourd’hui un petit billet sur la classification automatique. Il s’agit d’une présentation assez informelle de comment ça fonctionne. Je vais faire ça avec un petit exemple ;)

Déjà, à quoi ça sert ? L’idée de la classification automatique est d’entrainer un programme à classer des objets. Il y a plusieurs applications directes à cela, par exemple ça peut servir à répondre aux questions suivantes :

  1. est-ce que le message que je viens de recevoir est un spam ou pas ?
  2. est-ce que ce crabe est un mâle ou une femelle ?
  3. est-ce que le prénom « Jean » est masculin ou féminin ?
  4. est-ce que le patient a un rhume, une gastro ou un simple mal de tête ?
  5. etc…

Présentation des données

Il y a plein d’autres types questions auxquelles la classification automatique peut répondre. Je vous propose de regarder ensemble comment répondre à la question 2. (On va même aller un peu plus loin. ;)) Nous allons travailler avec les données crabs. Ce tableau de données regroupe deux cents crabes différents. Il y a deux espèces : les bleus et les oranges. Dans chacune des deux espèces, il y a des mâles et des femelles. On a donc quatre classes différentes, de cinquante individus chacune :

  1. bleu et mâles
  2. bleu et femelles
  3. orange et mâles
  4. orange et femelles

On va donc faire un petit programme qui va être chargé de classer un crabe dans la classe à laquelle il appartient.

Pour chaque crabe, nous avons $p$ variables qui permettent de le décrire. Par exemple :

  • la taille du lobe frontale
  • la largeur de la carapace
  • la longueur de la carapace
  • la profondeur du corps
  • etc…

C’est à partir de ces $p$ variables que se fera la classification.

Idée générale

Rentrons un peu dans l’aspect mathématique de la chose. L’idée est en fait relativement simple. On suppose que derrière chaque classe $k$ il existe une loi normale. Appelons cette loi $f_k(x)$. Grosso modo, cette loi donne la probabilité que $x$ appartienne à la classe $k$. Ainsi, il suffit simplement de passer en revue les fonctions $f_k(x)$ où $x$ est l’individu à classer et on dira qu’il appartient à la classe qui maximise la probabilité d’appartenance. Mathématiquement, on a :

$$ k^* = \arg\max_k f_k(x) $$

Le problème est donc simple une fois que l’on connait les fonctions $f_k$. Encore faut-il les connaître ; c’est justement sur ça que nous allons travailler. On va donc devoir estimer ces fonctions à partir des données que nous avons.

Estimation des distributions

Nous avons fait l’hypothèse que chaque classe suit une loi normale. Or, une loi normale peut être définie par deux paramètres :

  • $\mu$, la valeur moyenne de la distribution
  • $\Sigma$, la variance de la distribution. C’est-à-dire comment “s’étalent” les données autour de la valeur moyenne.

Il s’agit donc pour chaque classe $k$ d’estimer le couple $(\mu_k, \Sigma_k)$. L’estimation se fait à partir de l’estimation du maximum de vraisemblance.

Ainsi, on a les expressions suivantes :

\begin{equation} \mu_k = \frac{1}{n_k}\sum_{i = 0}^{n}t_{ik} x_i \end{equation}

\begin{equation} \Sigma_k = \frac{1}{n_k}\sum_{i = 0}^{n}t_{ik} (x_i - \mu_k)(x_i - \mu_k)^\prime \end{equation}

Ces formules méritent quelques explications :

  • tout d’abord, c’est quoi ce $t_{ik}$ ? C’est simplement une variable qui vaut 1 si l’individu $i$ appartient à la classe $k$ et 0 sinon. Cette variable nous permet de compter que les individus de la classe étudiée.
  • ensuite, $n_k$. Cette valeur correspond au nombre d’individus présents dans la classe $k$ (avec l’exemple des crabes, ce sera donc 25). Et $n$ correspond au nombre total d’individu (donc 200 dans notre exemple).

Connaissant ainsi les paramètres de chacune des classes, on peut construire les fonctions $f_k(x)$ :

\begin{equation} f_k(x) = \frac{1}{\sqrt{(2\pi)^k\left\vert\Sigma\right\vert}} \exp\left(-\frac{1}{2}\left(x - \mu_k\right)^\prime \Sigma_k\left(x - \mu_k\right)\right) \end{equation}

Voilà. Nous avons ainsi pu construire les distributions nous servant à calculer la probabilité pour un individu $x$ d’appartenir à la classe $k$.

Implémentation

Passons maintenant à la pratique. On va réaliser un programme en python qui va classer nos données. Pour ça, je suppose que vous connaissez un peu le python et que vous avez numpy et scikit-learn d’installés sur votre machine. Ces deux conditions remplies, on peut alors continuer.

Pour réaliser notre programme, nous devons faire plusieurs choses :

  1. charger les données ;
  2. estimer les paramètres des différentes classes ;
  3. évaluer la pertinence de la classification effectuée.

Il est très important d’évaluer la pertinence de la classification, ce qui vous permet alors de dire « mon programme classe les crabes correctement dans 90% des cas ». Et donc d’avoir une idée de la proportion des individus qui seraient mal classés. Pour faire cette évaluation il faut prendre des individus que le programme n’a jamais vus et dont vous connaissez à l’avance la classe. Ensuite, on demande au programme de classer ces individus et on compare avec les valeurs théoriques. En faisant cela sur plusieurs individus on peut alors estimer le nombre de fois où le classifieur a tort.

Il faut donc séparer les données en deux ensembles disjoints. Un ensemble d’apprentissage et un ensemble de test. Le premier sert à entrainer le classifieur (estimation des moyennes et variances). Le second sert à évaluer la pertinence du classifieur. Il est très important que ces deux ensembles soient disjoints, sinon votre évaluation sera biaisée. On va donc dans un premier temps, réaliser une fonction qui s’occupe de charger les données et créer ces deux ensembles.

Chargement des données

Commencez par télécharger le fichier crabs. Ce fichier regroupe les données des deux cents crabes. Voici le début du fichier :

sp sex FL RW CL CW BD
B M 8.1 6.7 16.1 19 7
B M 8.8 7.7 18.1 20.8 7.4
B M 9.2 7.8 19 22.4 7.7
O F 23.1 20.2 46.2 52.5 21.1

Les deux premières colonnes vont nous permettre de définir la classe. Les cinq autres colonnes correspondent aux variables explicatives :

FL
la taille du lobe frontal (Frontal lobe size)
RW
la largeur de la pate (Rear Width)
CL
la longueur de la carapace (Carapace Length)
CW
la largeur de la carapace (Carapace Width)
BD
l’épaisseur du corps (Body Depth)

On va donc tout d’abord charger tout ce fichier dans un tableau numpy.

import csv
import numpy as np

def loadcrabs(filename='crabs.csv'):
   def _getclass(species, sex):
       """ Return the class number of the crab

                species
               +---+---+
               | B | O |
            +--+---+---+
          s |M | 3 | 1 |
          e +--+---+---+
          x |F | 2 | 0 |
            +--+---+---+
       """
       return 2*(species == 'B') + (sex == 'M')

   data = []
   with open(filename) as fobj:
       csvfile = csv.DictReader(fobj)
       for crab in csvfile:
           subject = [_getclass(crab['sp'], crab['sex']),
                      crab['FL'], crab['RW'],
                      crab['CL'], crab['CW'],
                      crab['BD']]
           data.append(map(float, subject))
   return np.array(data)

Cette fonction, nous permet donc de charger le fichier de données en mémoire et associe à chaque classe de crabes un numéro : 0 pour les femelles oranges, 1 pour les mâles oranges, 2 pour les femelles bleues et 3 pour les mâles bleus.

On va maintenant mélanger les lignes de ce tableau et le couper en deux :

data = loadcrabs()
np.random.shuffle(data)
learn = data[:len(data)/2, ]
test = data[len(data)/2:, ]

Estimation des paramètres des classes

Passons à la partie apprentissage. Pour cela, on va utiliser la classe QDA de scikit-learn, qui va nous permettre de faire la classification. Cette classe dispose d’une méthode fit() qui permet de faire l’apprentissage. Elle prend deux paramètres, une matrice X qui correspond aux individus et un vecteur Y qui correspond aux classes des individus. Dans notre cas, Y est la première colonne du tableau learn et X est le reste. Ainsi, très simplement, nous faisons :

from sklearn.qda import QDA
qda = QDA()
qda.fit(learn[:, 1:], learn[:, 0])

Et c’est tout. La fonction fit() de QDA se charge toute seule d’estimer les paramètres $\mu_k$ et $\Sigma_k$ pour chacune des classes. Il nous faut maintenant passer à la classification à proprement parler.

Évaluation du classifieur

Bon, avant d’évaluer notre classifieur, on va quand même essayer de classer un seul élémement. Prenons par exemple le premier crabe de test. Dans mon cas, j’ai :

>>> test[0]
array([  2. ,  13.2,  12.2,  27.9,  32.1,  11.5])

C’est donc un crabe de classe 2 (une femelle bleue). Voyons comment le classe qda.

>>> qda.predict([test[0, 1:]]) #il ne faut PAS lui donner la première colonne
array([ 2. ])

Tada ! On voit que le crabe a bien été classé dans le groupe 2. C’est donc un succès pour ce premier classement.

Bon, allez, maintenant, on classe tout d’un coup !

>>> sorting = qda.predict(test[:, 1:])
>>> sorting
array([ 2.,  0.,  2.,  0.,  0.,  0.,  1.,  2.,  3.,  2.,  3.,  3.,  1.,
        0.,  2.,  2.,  0.,  3.,  3.,  0.,  3.,  0.,  2.,  1.,  3.,  0.,
        0.,  1.,  1.,  0.,  0.,  2.,  0.,  1.,  3.,  0.,  0.,  1.,  2.,
        0.,  0.,  1.,  0.,  0.,  3.,  0.,  2.,  3.,  3.,  0.,  2.,  2.,
        1.,  2.,  2.,  3.,  1.,  1.,  3.,  0.,  1.,  2.,  2.,  0.,  0.,
        3.,  2.,  2.,  1.,  0.,  0.,  2.,  2.,  0.,  2.,  2.,  0.,  1.,
        3.,  1.,  2.,  3.,  3.,  2.,  3.,  1.,  1.,  3.,  1.,  3.,  3.,
        0.,  1.,  0.,  0.,  1.,  3.,  3.,  2.,  1.])
>>> sorting == test[:, 0]
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True, False,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True, False,
       False,  True,  True, False,  True,  True,  True, False,  True,
        True,  True,  True, False,  True,  True, False,  True,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True,  True,  True,  True, False,  True,
        True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True, False,  True,  True, False,  True,  True,
        True,  True,  True,  True,  True,  True,  True, False,  True,  True], dtype=bool)

Ainsi, dans la liste sorting, nous avons le numéro de classe à laquelle chaque crabe a été affecté. On peut donc le comparé avec la classe réelle. Partout où il est écrit False, le classifieur s’est trompé. On peut faire une estimation en pourcentage du taux d’erreur :

>>> (100.0 * len(np.where(sorting != test[:, 0])[0]))/len(test)
11.0

Ainsi, dans l’exemple que je viens de vous montrer, le classifieur se trompe dans 11% des cas.

Si vous lancez plusieurs fois le programme, vous vous apercevrez que ce taux d’erreur change. C’est normal, puisque les ensembles d’apprentissage et de test changent !

Conclusion

Ainsi se termine ce billet introductif à la classification automatique. Je vous ai donc présenté un peu la théorie de la classification (c’est un grand mot). Et puis comment utiliser scikit-learn pour faire la dite classification. Il est possible de faire de la classification sur plein de choses ! Des fleurs, du vin, de la musique, des images, des emails. Bref, un peu tout ce que vous voulez quoi. Il faut “juste” avoir les bonnes variables ;)

Si ça vous intéresse, vous pouvez télécharger le code complet ici. Pour toute remarque, n’hésitez pas à me contacter.

PS: Les explications ont volontairement été simplifiées. J’ai, par exemple, supposé que chaque classe avait autant d’individus. Ce qui en pratique n’est pas toujours vrai. Il faut aussi calculer une probabilité dite a priori et on cherche alors à maximiser la probabilité dite a posteriori. Le lecteur curieux trouvera plus d’informations dans ce document (document dont je me suis inspiré. Merci aux auteurs !).

PPS: Les données crabes sont issues d’une étude publiée dans Australian Journal of Zoology et disponibles dans la bibliothèqe MASS du logiciel libre de statistiques R.

Published In sciences
Tags: pythonapprentissage

blogroll

social