TP sur le topic modeling (corrigé)

François HU - ENSAE - 14/04/20 - https://curiousml.github.io/

source : nlptown

In [1]:
from jyquickhelper import add_notebook_menu
add_notebook_menu()
Out[1]:
run previous cell, wait for 2 seconds

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 :

  • "homme politique", "droit" et "parlement", et un autre caractérisé par
  • "joueur", "match" et "carton rouge"

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é".

Données : Grand Débat National

  • 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.

  • Nous allons tout d'abord télécharger un des fichiers csv sur la transition écologique et charger le contenu dans un dataframe pandas.

3.1. Charger les données

In [1]:
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)
/miniconda3/envs/xeus-cling2/lib/python3.7/site-packages/IPython/core/interactiveshell.py:3049: DtypeWarning: Columns (6) have mixed types. Specify dtype option on import or set low_memory=False.
  interactivity=interactivity, compiler=compiler, result=result)

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.

In [2]:
df.columns
Out[2]:
Index(['reference', 'title', 'createdAt', 'publishedAt', 'updatedAt',
       'trashed', 'trashedStatus', 'authorId', 'authorType', 'authorZipCode',
       'Quel est aujourd'hui pour vous le problème concret le plus important dans le domaine de l'environnement ?',
       'Que faudrait-il faire selon vous pour apporter des réponses à ce problème ?',
       'Diriez-vous que votre vie quotidienne est aujourd'hui touchée par le changement climatique ?',
       'Si oui, de quelle manière votre vie quotidienne est-elle touchée par le changement climatique ?',
       'À titre personnel, pensez-vous pouvoir contribuer à protéger l'environnement ?',
       'Si oui, que faites-vous aujourd'hui pour protéger l'environnement et/ou que pourriez-vous faire ?',
       'Qu'est-ce qui pourrait vous inciter à changer vos comportements comme par exemple mieux entretenir et régler votre chauffage, modifier votre manière de conduire ou renoncer à prendre votre véhicule pour de très petites distances ?',
       'Quelles seraient pour vous les solutions les plus simples et les plus supportables sur un plan financier pour vous inciter à changer vos comportements ?',
       'Par rapport à votre mode de chauffage actuel, pensez-vous qu'il existe des solutions alternatives plus écologiques ?',
       'Si oui, que faudrait-il faire pour vous convaincre ou vous aider à changer de mode de chauffage ?',
       'Avez-vous pour vos déplacements quotidiens la possibilité de recourir à des solutions de mobilité alternatives à la voiture individuelle comme les transports en commun, le covoiturage, l'auto-partage, le transport à la demande, le vélo, etc. ?',
       'Si oui, que faudrait-il faire pour vous convaincre ou vous aider à utiliser ces solutions alternatives ?',
       'Si non, quelles sont les solutions de mobilité alternatives que vous souhaiteriez pouvoir utiliser ?',
       'Et qui doit selon vous se charger de vous proposer ce type de solutions alternatives ?',
       'Que pourrait faire la France pour faire partager ses choix en matière d'environnement au niveau européen et international ?',
       'Y a-t-il d'autres points sur la transition écologique sur lesquels vous souhaiteriez vous exprimer ?'],
      dtype='object')

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.

In [3]:
question = "Y a-t-il d'autres points sur la transition écologique sur lesquels vous souhaiteriez vous exprimer ?"
df[question].head(10)
Out[3]:
0               Multiplier les centrales géothermiques
1    Les problèmes auxquels se trouve confronté l’e...
2                                                  NaN
3                                                  NaN
4      Une vrai politique écologique et non économique
5    Les bonnes idées ne grandissent que par le par...
6    Pédagogie dans ce sens là dés la petite école ...
7                                                  NaN
8    faire de l'écologie incitative et non punitive...
9    Développer le ferroutage pour les poids lourds...
Name: Y a-t-il d'autres points sur la transition écologique sur lesquels vous souhaiteriez vous exprimer ?, dtype: object

3.2. Nettoyer et vectoriser les documents

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. Nous allons juste initialiser un modèle vierge pour la langue française.

In [4]:
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

In [5]:
textes = df[df[question].notnull()][question] # A REMPLIR

Ensuite, nous utilisons spaCy pour effectuer notre premier prétraitement :

In [6]:
%time spacy_docs = list(nlp.pipe(textes))
CPU times: user 14.7 s, sys: 214 ms, total: 14.9 s
Wall time: 14.9 s

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 :

  • supprimer tous les mots de moins de 3 caractères,
  • supprimer tous les stop-words, et
  • lemmatiser les mots restants et,
  • mettre ces mots en minuscules.
In [7]:
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])
[['multiplier', 'centrales', 'géothermiques'], ['problèmes', 'trouve', 'confronté', 'ensemble', 'planète', 'dénoncent', 'parfait', 'désordre', 'gilets', 'jaunes', 'france', 'surpopulation', 'mondiale', 'population', 'passée', 'd’1,5', 'milliards', 'habitants', '1900', 'milliards', '2020', 'montera', 'bientôt', 'milliards', '2040', 'progrès', 'communication', 'village', 'mondial', 'individu', 'fond', 'asie', 'fond', 'afrique', 'passant', 'quartiers', 'campagnes', 'pays', 'aspire', 'vivre', 'blâmer', 'lotis', 'concitoyens', 'logement', 'nourriture', 'biens', 'consommation', 'déplacement', 'mère', 'problèmes', 'solution', 'problèmes', 'stabilisation', 'croissance', 'démographique', 'partage', 'richesses', 'partage', 'terres', 'partage', 'protection', 'biodiversité', 'règlement', 'conflits', 'lutte', 'déforestation', 'lutte', 'dérèglement', 'climatique', 'règlement', 'conflits', 'stabilisation', 'migrations', 'concurrence', 'commerciale', 'mondiale', 'etc.', 'française', 'européenne', 'mondiale', 'france', 'jouer', 'rôle', 'moteur', 'autour', 'déroulera', 'grand', 'débat', 'paraît', 'anecdotique'], ['vrai', 'politique', 'écologique', 'économique']]

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 :

  • Nous identifions d'abord les bigrammes fréquents dans le corpus,
  • puis nous les ajoutons à la liste des tokens pour les documents dans lesquels ils apparaissent. Cela signifie que les bigrammes ne seront pas dans leur position correcte dans le texte, mais cela ne pose pas de soucis : les topic models sont des modèles de bag-of-words (sac-de-mots en français) qui ignorent la position des mots de toute façon.
In [8]:
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)
In [9]:
docs[2]
Out[9]:
['vrai', 'politique', 'écologique', 'économique']

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.

In [10]:
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]))
Number of unique words in original documents : 43652
Number of unique words after removing rare and common words : 17759
Example representation of document 3 : [(85, 1), (86, 1), (87, 1), (88, 1)]

Ensuite, nous créons des représentations en sac de mots pour chaque document du corpus voir la méthode doc2bow :

In [11]:
corpus = [ dictionary.doc2bow(doc) for doc in docs] # A REMPLIR

3.3. Topic Modeling avec LDA

Maintenant, il est temps d'entraîner notre LDA ! Pour ce faire, nous utilisons les paramètres suivants :

  • corpus : les représentations en sac-de-mots de nos documents
  • id2token : le mappage des index aux mots
  • num_topics : le nombre de topics que le modèle doit identifier (fixons à 10)
  • chunksize : le nombre de documents que le modèle voit à chaque mise à jour (fixons à 1 000)
  • passes : le nombre de fois où nous montrons le corpus total au modèle pendant l'entraînement (fixons à 5)
  • random_state : nous utilisons une graine pour assurer la reproductibilité (fixons à 1)

Sur un corpus de cette taille, l'entraînement dure généralement une ou deux minutes.

In [12]:
from gensim.models import LdaModel

%time model = LdaModel(corpus=corpus, id2word=dictionary, num_topics=10, chunksize=1000, passes=5, random_state=1) # A REMPLIR
CPU times: user 37.4 s, sys: 185 ms, total: 37.5 s
Wall time: 36.6 s

3.3. Résultats et visualisation

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 :

  • véhicules électriques (topic 1),
  • énergie (alternative) (topic 2),
  • agriculture (topic 6),
  • déchets et recyclage (topic 7) et
  • fiscalité (topic 9).
In [13]:
for (topic, words) in model.print_topics():
    print("***********")
    print("* topic", topic+1, "*")
    print("***********")
    print(topic+1, ":", words)
    print()
***********
* topic 1 *
***********
1 : 0.052*"produits" + 0.021*"agriculture" + 0.017*"interdire" + 0.014*"animaux" + 0.012*"pesticides" + 0.011*"agriculteurs" + 0.010*"production" + 0.009*"consommation" + 0.009*"santé" + 0.008*"favoriser"

***********
* topic 2 *
***********
2 : 0.010*"climatique" + 0.007*"problème" + 0.007*"planète" + 0.006*"population" + 0.006*"réchauffement" + 0.005*"années" + 0.005*"grand" + 0.005*"changement" + 0.004*"problèmes" + 0.004*"croissance"

***********
* topic 3 *
***********
3 : 0.024*"protection" + 0.018*"environnement" + 0.017*"pollueur" + 0.015*"grande_distribution" + 0.013*"distribution" + 0.013*"payeur" + 0.011*"arrêtons" + 0.011*"principe" + 0.010*"obsolescence" + 0.009*"grande"

***********
* topic 4 *
***********
4 : 0.012*"villes" + 0.012*"déchets" + 0.012*"entreprises" + 0.011*"grandes" + 0.009*"zones" + 0.006*"ville" + 0.006*"obliger" + 0.006*"faut" + 0.006*"faire" + 0.006*"centre"

***********
* topic 5 *
***********
5 : 0.035*"vitesse" + 0.019*"limitation" + 0.017*"routes" + 0.015*"sécurité" + 0.015*"lieu" + 0.012*"route" + 0.012*"gouvernement" + 0.009*"temps" + 0.009*"disparaissent" + 0.008*"politique"

***********
* topic 6 *
***********
6 : 0.023*"faut" + 0.017*"faire" + 0.008*"terme" + 0.008*"écologie" + 0.008*"monde" + 0.008*"pays" + 0.007*"france" + 0.007*"changer" + 0.007*"planète" + 0.007*"sommes"

***********
* topic 7 *
***********
7 : 0.031*"faire" + 0.015*"pollueurs" + 0.013*"gros" + 0.010*"payer" + 0.010*"exemple" + 0.007*"grands" + 0.007*"montrer" + 0.006*"grosses" + 0.006*"pollution" + 0.006*"nuit"

***********
* topic 8 *
***********
8 : 0.038*"énergie" + 0.032*"nucléaire" + 0.028*"énergies" + 0.016*"production" + 0.015*"centrales" + 0.015*"électricité" + 0.014*"renouvelables" + 0.014*"développer" + 0.013*"éoliennes" + 0.012*"panneaux"

***********
* topic 9 *
***********
9 : 0.023*"voiture" + 0.019*"véhicules" + 0.016*"voitures" + 0.015*"taxer" + 0.014*"électrique" + 0.013*"transport" + 0.011*"véhicule" + 0.011*"diesel" + 0.010*"taxe" + 0.010*"transports"

***********
* topic 10 *
***********
10 : 0.047*"transition" + 0.044*"écologique" + 0.029*"transition_écologique" + 0.012*"place" + 0.012*"entreprises" + 0.010*"investissement" + 0.008*"faut" + 0.007*"taxes" + 0.007*"mettre" + 0.007*"mise"

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.

In [14]:
import pyLDAvis.gensim
import warnings

pyLDAvis.enable_notebook()
warnings.filterwarnings("ignore", category=DeprecationWarning) 

pyLDAvis.gensim.prepare(model, corpus, dictionary, sort_topics=False)
/miniconda3/envs/xeus-cling2/lib/python3.7/site-packages/pyLDAvis/_prepare.py:257: FutureWarning: Sorting because non-concatenation axis is not aligned. A future version
of pandas will change to not sort by default.

To accept the future behavior, pass 'sort=False'.

To retain the current behavior and silence the warning, pass 'sort=True'.

  return pd.concat([default_term_info] + list(topic_dfs))
Out[14]:

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.

In [41]:
# 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()
***********
* doc 1   *
***********
Multiplier les centrales géothermiques
[(5, 0.27430695), (8, 0.5254362)]

***********
* doc 2   *
***********
Les problèmes auxquels se trouve confronté l’ensemble de la planète et que dénoncent, dans le plus parfait désordre, les gilets jaunes de France ne sont-ils pas dus, avant tout, à la surpopulation mondiale ? Cette population est passée d’1,5 milliards d’habitants en 1900 à 7 milliards en 2020 et montera bientôt à 10 milliards vers 2040.  Avec les progrès de la communication dans ce village mondial, chaque individu, du fin fond de l’Asie au fin fond de l’Afrique, en passant par les « quartiers » et les « campagnes » de notre pays, aspire à vivre – et on ne peu l’en blâmer – comme les moins mal lotis de nos concitoyens (logement, nourriture, biens de consommation, déplacement,etc.).  Voilà la mère de tous les problèmes. Si tel est bien le cas, la solution à tous les problèmes (stabilisation de la croissance démographique, partage des richesses, partage des terres, partage de l’eau, protection de la biodiversité, règlement des conflits, lutte contre la déforestation, lutte contre dérèglement climatique, règlement des conflits, stabilisation des migrations, concurrence commerciale mondiale, etc.) ne sera ni française, ni européenne, mais mondiale. La France se doit d’y jouer un rôle moteur.  Le reste, autour duquel se déroulera « le Grand débat », paraît assez anecdotique.
[(2, 0.8235213)]

***********
* doc 3   *
***********
Une vrai politique écologique et non économique
[(6, 0.8199753)]

***********
* doc 4   *
***********
Les bonnes idées ne grandissent que par le partage.

En ces jours pénibles où s'affrontent le peuple et ceux entre lesquels ils ont remis leur sort. (tous concernés puis qu’élus).

Une idée qui flotte en ce moment sur la mobilité propre dans les zones non ou mal desservies et qui est encore expérimentale dans quelques communes.

L'avenir  est entre vos mains et les nôtres. Pourquoi ne pas planifier l'installation de flotte de véhicules électriques partagés en location, dans ces zones isolées.

En construisant un partenariat avec : état, région, département, commune ou communauté, professionnel de la location, entreprises locales, constructeurs de véhicules, les bailleurs sociaux, pour la mise en place. Avec l'apport financier de la TICPE qui baissera au fil de la décroissance de la consommation, il faudra penser à la déconnecter du budget de fonctionnement de l'état !

Avec un forfait de location pour les utilisateurs, adapté à leur ressources et peut être tout simplement basé sur leurs dépenses de mobilité actuelles. Ainsi pas d'investissements inaccessibles pour eux.

Tout le monde y gagnerait, cela  pourrait prendre l'image du relais de poste comme par le passé et réglerait le problème d'autonomie actuel et engendrerait quelques emplois de service, nouveaux.
[(2, 0.13684697), (4, 0.12634256), (6, 0.16841982), (9, 0.2015315), (10, 0.36116502)]

***********
* doc 5   *
***********
Pédagogie dans ce sens là dés la petite école pour sensibilisation via les parcs naturels . Les enfants doivent devenir des prescripteurs  pour les générations futures . Il y a urgence
[(2, 0.13640082), (4, 0.2681016), (6, 0.5488043)]

***********
* doc 6   *
***********
faire de l'écologie incitative et non punitive on n'est pas des gosses à qui on distribue des bonnes ou mauvaises notes
[(2, 0.46371236), (10, 0.44722226)]

***********
* doc 7   *
***********
Développer le ferroutage pour les poids lourds, Instaurer une vignette pour les poids lourds étrangers qui traversent la France
[(9, 0.8150414), (10, 0.13784769)]

***********
* doc 8   *
***********
- Favoriser le tri des déchets en mettant en place une redevance incitatrice et non punitive,
- Arrêt d'une écologie punitive à base de taxes qui est néfaste à la compréhension d'une écologie intelligente, adaptée et durable.
- Revoir la politique de pose des panneaux solaire en Ferme qui en fait gâche des terrains et sert plus à récolter des taxes pour les caisses des collectivités,
- Revoir la politique de mise en place des éoliennes qui ne sont pas adaptées, peu rentable (< 30%) et qui produisent des effets néfastes par du bruit et des infra-ondes,
- Arrêt du rachat, à des prix exorbitants, de l'énergie produite par des panneaux solaire, par des éoliennes ou par tout autre moyen dit propre (Bio-masse, hydraulique),
- Favoriser le développement de la bio-masse,
- Développer le stockage intelligent de l'énergie,
- Réaliser une grande opération pour assurer l'isolation des bâtiments énergivores,
- Éteindre les éclairages la nuit,
- Développer les transports en site propre.
[(1, 0.18159804), (8, 0.45110586), (10, 0.21095155)]

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.