Bureau d'étude PEIP - IS

Projet

Le but de ce TP est de créer un classifieur qui sera capable de différencier des commentaires favorables (positifs) et défavorables (négatifs) laissés par des utilisateurs sur le compte tweeter d'une compagnie aérienne américaine. Cette page explique le fonctionnement d'un tel classifieur, les étapes de sa réalisation, et la dernière partie fixe les modalités du TP.

Ce TP est à réaliser en groupe et sera noté. Vous disposez de 4 séances pour le réaliser. Votre fichier devra se nommer "nom1_nom2_projetBEPEIP.py" et être rendu dans un mail dont l'objet sera "[PEIP][BEIS] Rendu TP Final". Aucun retard ne sera accepté. Aucune triche ne sera tolérée.

Une fonction non commentée ne sera pas prise en compte. Votre code devra impérativement s'exécuter sans erreur et sans warning. Mettez en commentaire les fonctions que vous n'avez pas pu terminer. Vous optimiserez autant que possible vos algorithmes.

Vous fournirez votre code dans une archive de format "nom1_nom2.zip", contenant également un fichier "readme.txt" dans lequel figurerons vos noms et les commandes de lancement pour votre code, et où vous expliquerez notamment les difficultés rencontrées dans le projet et la part de travail de chacun dans la réalisation du code.

Le projet est à rendre pour le 4/04/2025 23h59 dernier délai. Tout rendu en retard sera considéré comme non rendu.

Principe de base du classifieur naïf Bayésien

La tâche de classification correspond à la tâche d'assigner un label à un élément donné (en l'occurrence un texte). Dans l'exemple qui nous intéresse, nous travaillons avec seulement deux labels : "positive" (POS) et "negative" (NEG). Chaque tweet proposé dans le jeu de données est d'un et d'un seul label (un tweet ne peut pas être à la fois "POS" et "NEG", il est l'un ou l'autre).

Pour ce TP, nous utiliserons un classifieur naïf Bayésien, qui propose une méthode de classification à partir de quelques statistiques élémentaires.

Supposons que nous ayons un grand ensemble de tweets pour lesquels on connaît les labels et qu'on considère que chaque tweet est un ensemble de mot (on ne s'occupe pas de l'ordre des mots). Il est alors facile de calculer la probabilité d'apparition d'un mot pour un label. On suit la démarche suivante :

1) Considérant tous les textes d'un label, on calcule le nombre de mots total dans ces textes
2) Pour chaque mot présent dans ces textes, on calcule le nombre total de fois où il apparaît
3) Pour un mot donné, on divise son nombre d'occurrences (calculé en 2) par le nombre total de mots dans les textes du labels (calculé en 1)

La probabilité obtenue est la probabilité d'occurrence du mot dans un tweet connaissant le label de ce tweet. Si on note le mot m et le label L, on note cette probabilité P(m|L) (qu'on lit "probabilité de m sachant L"). Pour le dire autrement, il s'agit de la probabilité qu'on a de trouver le mot m sachant qu'on est en train de lire un tweet du label L.

Quelle est alors la probabilité de lire un ensemble de mots complet m1, m2, m3, ... (autrement dit, un tweet) si on lit des tweets d'un label donné ? C'est une probabilité qu'on noterait P(m1,m2,m3,... | L), mais puisque l'ordre n'a pas d'importance, on peut l'écrire comme étant simplement le produit des probabilités de trouver chaque mot sachant le label qu'on considère. On appelle cette simplification l'hypothèse du "sac de mots" ; mathématiquement, cela se note ainsi :

P(m1,m2,m3,... | L) = P(m1|L) x P(m2|L) x P(m3|L) x ...

Tout cela est très bien, mais en classification, c'est exactement le problème inverse qu'on cherche à résoudre... en effet, en général on connaît le texte du tweet, mais pas son label (qu'on cherche justement à prédire). On cherche donc à calculer pour chaque label la probabilité d'être d'un label L sachant que le tweet est composé des mots m1,m2,m3... ; on note cette probabilité P(L|m1, m2, m3...). On calcule cette valeur pour chaque label, et on supposera que la probabilité la plus élevée correspond au bon label.

C'est ici qu'intervient la formule de Bayés, qui donne son nom à ce type de classifieur. Pour deux événements A et B, celle-ci donne une relation entre la probabilité de A sachant B (notée P(A|B)), la probabilité de B sachant A (notée P(B|A)), la probabilité de A et la probabilité de B (notée P(A) et P(B)):

P(A|B) = (P(A) x P(B|A)) / P(B)

En conséquence, si on remplace l'événement A par l'événement L et si on remplace également l'événement B par l'événement m1,m2,m3...., il est très facile de calculer la probabilité P(L|m1, m2, m3...) en utilisant cette formule :

P(L|m1, m2, m3...) = ( P(L) x P(m1, m2, m3...|L) ) / P(m1, m2, m3...)

La probabilité P(L) s'obtient simplement en divisant le nombre de textes connus pour le label L par le nombre total de textes du corpus. Et en utilisant la simplification du sac du mot vue précédemment, on obtient la formule finale suivante :

P(L|m1, m2, m3...) = ( P(L) x [ P(m1|L) x P(m2|L) x P(m3|L) x ...] ) / [P(m1) x P(m2) x P(m3)...] )

Les probabilités P(m) pour chaque mot m s'obtiennent tout simplement en divisant le nombre d'occurence du mot dans l'ensemble du corpus sur le nombre total de mot du corpus.

Fonctionnement général

On l'a vu, pour pouvoir utiliser un classifieur naïf Bayésien, on doit connaître plusieurs probabilités sur le corpus : P(L) pour chaque label L, P(m|L) pour chaque mot, etc... Comment calculer ces probabilités ?

Notre jeu de données est divisé en trois jeux distincts : train, dev et test.

  • - La partie "train" est la partie d'entraînement, c'est sur ces données qu'on calculera les statistiques qui nous serviront pour notre classifieur.
  • - La partie "dev" est la partie de développement, c'est sur celle-ci qu'on testera notre programme au fur et à mesure de sa conception.
  • - La partie "test" est la partie sur laquelle nous calculerons les performances finales de notre algorithme. Une fois qu'on est sûr que le programme marche sur le dev, on peut le lancer sur le test pour obtenir le résultat final.

Notre classifieur naïf Bayésien va donc fonctionner en deux phases : une première phase d'apprentissage où il calculera les statistiques nécessaires sur le corpus d'entraînement, et une deuxième phase où il prendra les tweets du corpus de dev (pendant la rédaction du programme) ou de test (une fois que le programme marche) et leur attribuera le label le plus probable.

Application au TP

Pour résumé, voici la façon dont on va construire un classifieur naïf Bayésien dans le cadre se ce TP :

PARTIE 1 - PRÉPARATIONS
  • - Lecture du fichier train ; séparation des tweets dans deux listes "train_pos" et "train_neg" en fonction du label.
  • - Lecture des fichiers dev et test : stockages des tweets et labels dans des listes pythons "tweets_dev", "tweets_test", "label_dev" et "label_test".
  • - Pré-traitements des listes de textes : enlever les stop-words (les mots les plus fréquents qui ne portent pas d'information), la ponctuation non nécessaire (attention à conserver les smileys), et toute opération qui vous paraîtra nécessaire pour améliorer les performances de l'algorithme
PARTIE 2 - APPRENTISSAGE
  • - Compte du nombre de textes dans les listes "train_pos" et "train_neg", on stocke dans des variables "p_pos" et "p_neg" ces nombres divisés par le nombre total de textes du corpus de train.
    *On a maintenant les probabilités P(POS) et P(NEG)*
  • - Compte du nombre total de mots dans le corpus de train, stocké dans une variables "n_corp"
  • - Création d'un dictionnaire "occur_corp" dont les clés seront tous les mots du corpus, et telle que les valeurs sont le nombre de fois où le mot en clé apparaît dans les tweets du corpus.
    *On a maintenant les probabilités P(m) car elles correspondent aux éléments de "occur_corp" divisés par "n_corp".*
  • - Création de deux dictionnaires "occur_pos" et "occur_neg" dont les clés seront tous les mots différents présents respectivement dans les listes "train_pos" et "train_neg". Les valeurs associées à ces clés sont le nombre de fois où le mot clé apparaît dans les tweets de la liste train_pos (et de même pour train_neg).
    *On a maintenant les probabilités P(m|POS) car elles correspondent aux éléments de "occur_pos" divisés par "n_pos" (et de même pour NEG).*
PARTIE 3 - CLASSIFICATION
Pour chaque tweet T du corpus de test :
  • - Extraction de la liste des mots du tweets qui sont présents dans les clés de "occur_pos" (on ignore les mots pour lesquels on n'a pas de stats)
  • - Calcul de la probabilité P(POS|T) en utilisant la formule finale vu plus haut.
  • - Répéter les étapes précédentes pour NEG.
  • - Comparer les deux probabilité P(POS|T) et P(NEG|T) et prédire un label pour le tweet.
On construira au fur et à mesure une liste "prediction" contenant à la suite tous les labels qui ont étés prédits, de sorte qu'on puisse la comparer à la fin à la liste "label_dev" ou "label_test" pour calculer le pourcentage de prédiction correctes.
PARTIE 4 - ÉVALUATION
Une fois la liste des prédictions obtenues, évaluer le modèle en utilisant les éléments suivants :
  • - Affichez le pourcentage de tweets correctement classé (sur le dev ou le test)
  • - Donnez la matrice de confusion.

Les fichiers de données pour le TP sont à retrouver ici : tweets.zip. Cette archive contient également un script python qui sera votre support de départ pour ce TP et qui commence par charger les fichiers de données dans des matrices.