François HU - Data scientist au DataLab de la Société Générale Assurances - 13/10/20 - https://nbviewer.jupyter.org/github/curiousML/DSA/tree/master/text_mining/
from jyquickhelper import add_notebook_menu
add_notebook_menu()
Introduction
Les topic models (modèles thématiques en français) sont une famille de modèles qui sont capables de découvrir les topics (thèmes en français) d'une collection de documents textuels. Dans ce contexte, le terme "topic" désigne des groupes de mots qui se retrouvent souvent ensemble dans un même document. Par exemple, dans un recueil d'articles de journaux, un topic model peut identifier un topic composé des mots :
Les topics modeling ne peuvent pas affecter un titre à ces topics : c'est notre tâche d'interpréter ces topics et de leur donner des étiquettes telles que "politique" et "football".
L'un des modèles les plus populaires est le LDA. Le LDA (cf. sections précédentes) est un modèle génératif qui considère chaque document comme un mélange de topics. Ce sont ces topics qui seront en charge de générer les mots. Par exemple, le topic "football" générera le mot "pénalité" avec une probabilité élevée, tandis que le topic "politique" aura une probabilité beaucoup plus élevée pour générer le mot "politicien" que pour générer le mot "pénalité".
L'un des contextes où la modélisation des topics est très utile est celui des questions ouvertes. Il nous permet d'explorer la variation des topics abordés dans les réponses des gens.
Dans ce notebook nous allons explorer un ensemble complet de réponses du Grand Débat National, le débat public organisé par le président Macron. Le but du débat était de mieux comprendre les besoins et les opinions des Français suite aux manifestations des gilets jaunes. Les résultats de ce débat sont maintenant disponibles sous forme de données ouvertes.
import pandas as pd
# indiquer le chemin du fichier
chemin = "https://raw.githubusercontent.com/curiousML/DSA/master/text_mining/data/LA_TRANSITION_ECOLOGIQUE.csv"
#"/chemin/menant/a/LA_TRANSITION_ECOLOGIQUE.csv"
# notre dataframe
df = pd.read_csv(chemin, error_bad_lines=False, warn_bad_lines=False)
Chacune des lignes de ce dataframe df contient des réponses répondant à une liste de questions sur la transition écologique. Certaines de ces questions sont à choix multiples, tandis que d'autres sont des questions ouvertes.
df.columns
Nous nous focaliserons sur la dernière question : Y a-t-il d'autres points sur la transition écologique sur lesquels vous souhaiteriez vous exprimer ? car elle donne le plus de liberté aux personnes.
Nous espérons que notre modèle LDA nous aidera à analyser les topics sur lesquels portent leurs réponses.
question = "Y a-t-il d'autres points sur la transition écologique sur lesquels vous souhaiteriez vous exprimer ?"
df[question].head(10)
Comme nous le remarquons, il y a beaucoup de données manquantes (comme toute question ouverte, les personnes décident oui ou non d'écrire un commentaire). Une étape de néttoyage est nécessaire.
Avant d'entraîner notre modèle LDA, nous avons besoin de tokenizer notre texte. Nous allons tokenizer grâce à la librarie spaCy car nous allons effectuer seulement quelques prétraitements de base (pour les personnes intéressées, des p). Nous allons juste initialiser un modèle vierge pour la langue française.
import spacy
nlp = spacy.blank("fr")
Supprimons toutes les lignes du dataframe qui n'ont pas de réponse pour notre question (les NaNs ci-dessus). Ce nouveau dataframe s'appellera textes
textes = df[df[question].notnull()][question] # A REMPLIR
Ensuite, nous utilisons spaCy pour effectuer notre premier prétraitement :
%time spacy_docs = list(nlp.pipe(textes))
Nous avons maintenant une liste de documents spaCy. Nous allons transformer chaque document spaCy en une liste de tokens. Au lieu des tokens originaux, nous allons travailler avec les lemmes à la place. Cela permettra à notre modèle de mieux généraliser, car il pourra "voir" que "géothermiques" et "géothermique" représentent la même signification. Voici la liste complète des prétraitements :
docs = [[t.lemma_.lower() for t in doc if len(t.orth_) > 3 and not t.is_stop] for doc in spacy_docs]
print(docs[:3])
Afin de conserver un peu les ordres des mots dans notre modélisation, nous allons tenir en compte les bigrammes fréquents. Pour cela, nous allons utiliser la bibliothèque Gensim. Nous tenons à remarquer que la bibliothèse Gensim est une excellente bibliothèque NLP pour les topics modeling.
Voici le processus retenu :
import re
from gensim.models import Phrases
bigram = Phrases(docs, min_count=10)
for idx in range(len(docs)):
for token in bigram[docs[idx]]:
if '_' in token: # les bigrammes peuvent être reconnus par "_" qui concatène les mots
# A REMPLIR (DEBUT)
docs[idx].append(token)
# (FIN)
docs[2]
Passons aux dernières étapes du prétraitement spécifique à Gensim. Nous allons tout d'abord créer une représentation dictionnaire des documents. Ce dictionnaire mappera chaque mot à un identifiant unique et nous aidera à créer des représentations en sac-de-mot de chaque document. Ces représentations en sac-de-mots contiennent les identificateurs des mots du document ainsi que leur fréquence. De plus, nous pouvons supprimer les mots les moins fréquents et les plus fréquents du vocabulaire. Cela améliorera la qualité de notre modèle et accélèrera son entraînement. La fréquence minimale d'un mot est exprimée en nombre absolu, la fréquence maximale est la proportion de documents dans lesquels un mot peut figurer.
from gensim.corpora import Dictionary
dictionary = Dictionary(docs)
print('Number of unique words in original documents :', len(dictionary))
dictionary.filter_extremes(no_below=3, no_above=0.25)
print('Number of unique words after removing rare and common words :', len(dictionary))
print("Example representation of document 3 :", dictionary.doc2bow(docs[2]))
Ensuite, nous créons des représentations en sac de mots pour chaque document du corpus voir la méthode doc2bow :
corpus = [ dictionary.doc2bow(doc) for doc in docs] # A REMPLIR
Maintenant, il est temps d'entraîner notre LDA ! Pour ce faire, nous utilisons les paramètres suivants :
Sur un corpus de cette taille, l'entraînement dure généralement une ou deux minutes.
from gensim.models import LdaModel
%time model = LdaModel(corpus=corpus, id2word=dictionary, num_topics=10, chunksize=1000, passes=5, random_state=1) # A REMPLIR
Voyons ce que le modèle a appris. Pour ce faire, affichons les dix mots les plus caractéristiques pour chacun des topics. Nous obeservons déjà des tendances intéressantes : si certains topics sont plus généraux (comme le topic 3), d'autres font référence à des topics très pertinents :
for (topic, words) in model.print_topics():
print("***********")
print("* topic", topic+1, "*")
print("***********")
print(topic+1, ":", words)
print()
Une autre façon d'observer les topics est de les visualiser. Ceci peut être fait avec la bibliothèque pyLDAvis. PyLDAvis nous montrera à quel point les sujets sont populaires dans notre corpus, à quel point les sujets sont similaires et quels sont les mots les plus importants pour ce sujet. Notez qu'il est important de définir sort_topics = False sur l'appel à pyLDAvis. Si vous ne le faites pas, les sujets seront classés différemment de ceux de Gensim. Cela peut prendre quelques minutes pour charger.
import pyLDAvis.gensim
import warnings
pyLDAvis.enable_notebook()
warnings.filterwarnings("ignore", category=DeprecationWarning)
pyLDAvis.gensim.prepare(model, corpus, dictionary, sort_topics=False)
Enfin, examinons les topics que le modèle reconnaît dans certains des documents individuels. Nous voyons ici comment LDA tend à attribuer une probabilité élevée à un faible nombre de sujets pour chaque document, ce qui rend ses résultats très interprétables.
# Nous en affichons que 8
i = 0
for (text, doc) in zip(texts[:8], docs[:8]):
i += 1
print("***********")
print("* doc", i, " *")
print("***********")
print(text)
print([(topic+1, prob) for (topic, prob) in model[dictionary.doc2bow(doc)] if prob > 0.1])
print()
Conclusion
De nombreuses collections de textes non structurés ne sont pas accompagnées d'étiquettes. Les topic models (modèles thématiques en français) tels que le LDA sont une technique utile pour découvrir les topics les plus importants dans ces documents. Gensim facilite l'apprentissage sur ces sujets et pyLDAvis présente les résultats d'une manière visuellement attrayante. Ensemble, ils forment une puissante boîte à outils pour mieux comprendre ce qu'il y a à l'intérieur de grands ensembles de documents et pour explorer des sous-ensembles de textes connexes. Si ces résultats sont souvent déjà très révélateurs, il est également possible de les utiliser comme point de départ, par exemple pour un exercice d'étiquetage pour la classification supervisée de textes. En somme, les modèles thématiques devraient figurer dans la boîte à outils de chaque data scientist comme un moyen très rapide d'obtenir un aperçu des grandes collections de documents.