Sallsamba's blog

Trouver des thèmes abstraits dans des documents grâce au topic modelling

December 11, 2020

topic modelling

Le Topic Modelling (ou Modeling, si vous préférez l’orthographe américaine) consiste à mettre en place un modèle statistique pour découvrir des textes abstraits dans un corpus de documents.

Pour faire simple, supposons que vous possédiez un ensemble de livres que vous n’avez pas du tout envie de lire, mais que vous souhaitiez quand même savoir, abstraitement, quels sont les thèmes abordés dans ces livres. Eh bien le Topic Modelling vous permettra de manière non supervisée (c’est-à-dire, entre autres, sans que vous n’ayez besoin de lire chaque livre), de déterminer un ensemble de thèmes communs entre vos bouquins.

Bien entendu, il s’agit d’une technique qui peut s’appliquer à une grande variété de données, pour peu qu’elles soient textuelles. Il peut s’agir d’un ensemble de tweets, d’articles de journaux, d’échanges de mails, ou encore d’articles de blogs (comme dans l’exemple que l’on développera dans cet article).

Sans plus tarder, nous allons mettre en pratique la technique sur un jeu de données importé depuis Kaggle.

Vous trouverez tout le code utilisé, ainsi que les données, sur ce Notebook Colab.

Le modèle que j’ai choisi d’utiliser est l’Allocation de Dirichlet Latente (ou LDA,pour Latent Dirichlet Allocation), un modèle bayésien, et l’un des plus utilisés pour la modélisation thématique.

Nous allons procéder par une approche classique en traitement du langage naturel :

  • Importation et nettoyage des données
  • (Exploration des données)
  • Vectorisation des données
  • Génération et entraînement du modèle
  • Visualisation des résultats

L’exploration des données sera volontairement omise, car elle n’est pas strictement nécessaire pour avoir un topic modelling fonctionnel.

I - Importation et nettoyage des données

Les données utilisées dans cet article proviennent de Kaggle. Très prisée des amateurs de Data Science, il s’agit d’une plateforme où vous pourrez trouver de nombreux datasets d’excellente qualité (ainsi que des compétitions, et une communauté très active dans le domaine de la data science et du machine learning).

Le dataset consiste en un corpus d’environ 350 articles publiés sur Medium. Vous pouvez les télécharger en cliquant ici (remarque : il est nécessaire d’avoir un compte Kaggle).

import pandas as pd

documents_df = pd.read_csv('articles.csv')
documents_df.head()

data.head()

Comme vous le voyez, pour chaque article, les données comportent l’auteur, le nombre de claps (sortes de mentions like), le temps de lecture conseillé, le lien vers l’article, le titre de ce dernier, et son contenu. Dans un premier temps, seul le contenu texte de l’article (et dans une moindre mesure son titre) nous intéressent, pour le Topic Modelling.

Lisons le contenu d’un article au hasard… Par exemple le 6ème car il s’agit de mon chiffre préféré.

documents_df['text'][6]

sample text

Oh no ! Il y a des problèmes de formatage, des \n qui apparaissent de partout…

Pas de panique. Avant toute tâche de NLP (traitement du langage naturel), il est important de nettoyer son texte pour se débarrasser de certaines reliques d’encodage, ou de formatage par exemple (les \n correspondent en Python au retour à la ligne mais il y aurait pu y en avoir beaucoup d’autres).

Il s’agit d’une étape indispensable, mais par chance, les données dont nous disposons sont de plutôt bonne qualité (pas de HTML, ni de problèmes d’encodage), et il y aura donc très peu de pré-traitement à faire.

Nous allons donc :

  • Enlever les éventuels caractères d’échappement comme \n , \b etc…
  • Normaliser le texte en Unicode en remplaçant les caractères spéciaux comme ‘é, â, ä etc… par e, a, a, etc…
  • Nous débarrasser des majuscules, car pour un humain, “Designers” et “designers” signifient (presque) la même chose, mais pour un ordinateur il s’agit de deux mots totalement différents.
import unicodedata

def normalize_text(text) :
        return unicodedata.normalize('NFKD', text)\
                            .encode('ascii', 'ignore')\
                            .decode('ascii')
def remove_tags(text):
    text = text.replace ('\n', ' ')
    text = text.replace ('\b', ' ')
    text = text.replace ('\t', ' ')
    
    return text

def lowercase(text):
    text = text.lower()
    return text

def clean_text (text):
    normalized_text = normalize_text(text)
    untagged_text = remove_tags(normalized_text)
    clean_text = lowercase(untagged_text)
    
    return clean_text

documents_df['clean_text'] = documents_df['text'].apply(clean_text)

Et voilà ! Nous avons maintenant une colonne clean_text qui va contenir les textes dont nous allons extraire des thèmes.

II- Vectorisation des documents

Avant de continuer, ouvrons une petite parenthèse sur une des nombreuses différences entre un humain et un ordinateur.

Lorsqu’un humain comme vous et moi lit une phrase telle que “Aujourd’hui j’ai mangé une pomme”, il est capable d’interpréter le contenu de la phrase dans son ensemble (pour peu qu’il s’agisse d’une langue qu’il maîtrise… Evidemment un francophone qui n’a jamais été confronté à un texte en japonais aura bien du mal à comprendre le contenu d’un livre nippon). Un ordinateur ne peut pas comprendre un texte brut. Il a besoin que le texte soit écrit dans un langage qu’il connaît, et c’est là qu’intervient la vectorisation (qui consiste à transformer un texte dans toute sa richesse, en une suite de nombres).

La vectorisation consiste en un nombre d’étapes plus ou moins élevé, mais pour simplifier, un texte comme : “Aujourd’hui, j’ai mangé une pomme” sera par exemple transformé en “aujourd mange pomme”, puis cette même phrase pourra être transformé en un vecteur [0, 0, 0, 1, 0, 1, 0, 0, 1, …].

Notez qu’il y a de très nombreuses techniques de vectorisation, et que pour le modèle LDA de Topic Modelling, je vais utiliser un Count Vectorizer, l’une des plus intuitives. Il consiste simplement à mettre en place un dictionnaire de mots qui apparaissent dans l’ensembles des articles (en supprimant les mots trop courants comme “and”, “if”, etc.. qui ne donnent pas beaucoup d’informations sur le contenu d’un texte).

La vectorisation est un aspect riche du traitement du langage naturel, et je vous invite, si vous souhaitez en apprendre plus, à lire cet article par exemple.

Bonne nouvelle ! Le Count Vectorizer possède une implémentation Python dans le module sklearn, et nous allons l’utiliser pour transformer nos textes en vecteurs.

from sklearn.feature_extraction.text import CountVectorizer

def vectorization (text):
    vectorizer = CountVectorizer(strip_accents = 'unicode',
                               stop_words = ['lol'],
                               lowercase = True,
                               token_pattern = r'\b[a-zA-Z]{3,}\b',
                               max_df = 0.1
                              )
  
    dtm = vectorizer.fit_transform(text)
    samples = vectorizer.inverse_transform(dtm)
    vocab = vectorizer.get_feature_names()
  
    return (vectorizer, dtm, samples, vocab)

vectorizer, dtm, samples, vocab = vectorization (documents_df['clean_text'])

Avant de continuer, jetons un œil au bloc de code ci-dessous.

Pour vectoriser un texte, nous définissons un vectorizer, qui met en place toutes les étapes nécessaires pour transformer le texte en vecteur. Remarquez les paramètres strip_accents et lowercase qui permettent de se débarrasser des accents et de convertir le texte en minuscule. Nous avions déjà effectué ces étapes dans le nettoyage du texte, mais le vectorizer peut les réeffectuer (je les laisse de manière indicative mais la plupart des paramètres du vectorizer sont optionnels).

Le paramètre stop_words permet de définir une liste de mots que nous souhaitons éliminer de notre vocabulaire. Il peut être utile si certains mots sont si spécifiques à nos documents qu’ils pourraient influencer le choix des thèmes. Pour l’instant, nous n’ajouterons pas de stop words. token_pattern permet de spécifier au vectorizer comment nous souhaitons que les textes soient tokenisés. Le tokenisation consiste à découper un texte en un ensemble de tokens (le plus souvent de simples mots). Ici, nous ne souhaitons garder que les mots de plus de 3 lettres et éliminer les nombres. Enfin, max_df est un paramètre très important qui permet de supprimer les mots qui apparaissent dans un trop grand nombres de documents (plus de 10%) et qui ne convoient donc aucune information dans les thèmes des articles.

Pour plus d’informations, vous pouvez consulter la documentation officielle de sklearn au sujet de countvectorizer

vocab va contenir le vocabulaire de l’ensembles des documents.

dtm qui signifie document-term matrix contient les informations de chaque document, par rapport à chaque mot.

vocabulary

III- Le Topic Modelling proprement dit avec LDA

A) Présentation sommaire de la théorie derrière l’Allocation de Dirichlet Latente

(Il n’est pas nécessaire de comprendre cette partie pour réaliser la suite du topic modelling. Elle est là à titre indicatif)

lda-model graph

Le modèle LDA peut être représentée par la figure ci-dessus.

  • La boîte M représente l’ensemble des articles.
  • Beta représente la distribution notre vocabulaire (l’ensemble de tous les mots sélectionnés depuis tous les articles) pour chaque thème (suivant une loi de Dirichlet).
  • Alpha est un paramètre qui caractérise la distribution de Dirichlet (Theta) des thèmes des documents (initialement, on se fixe un nombre de thèmes, par exemple 10).
  • z représente les thèmes traités dans chaque document et w représente les mots retrouvés dans chaque document.

Le modèle est initialisé aléatoirement tel que suit :

Pour chacun des M documents, on initialise Theta (vecteurs des thèmes) suivant une loi de Dirichlet de paramètre Alpha.

Ensuite, le modèle est entraîné de manière itérative, en suivant un principe d’inférence bayésienne variationnelle. L’intuition derrière l’entraînement du modèle est très bien décrit dans cet article.

Après entraînement, le modèle renvoie deux matrices Theta (qui lie chaque document aux thèmes fixés) et Beta (qui lie chaque thème aux mots du vocabulaire), optimisés par rapport à notre corpus.

B) En pratique

from sklearn.decomposition import LatentDirichletAllocation

lda = LatentDirichletAllocation(n_components=11, max_iter=5,
                                learning_method = 'online',
                                learning_offset = 50.,
                                random_state = 0)
lda.fit(dtm)

n_top_words = 15

def print_top_words(model, feature_names, n_top_words):
    
    for index, topic in enumerate(model.components_):
        message = "\nTopic {}:".format(index)
        message += " ".join([feature_names[i] for i in topic.argsort()[:-n_top_words - 1 :-1]])
        print(message)
        print('-'*70)

Le modèle LDA est simplement construit avec les paramètres spécifiés (voir documentation pour comprendre les choix effectués). Il est ensuite entraîné sur le document-term matrix obtenu grâce au countvectorizer.

On peut ensuite définir une fonction pour afficher les thèmes extraits à travers l’ensemble des documents (en affichant les mots les plus représentatifs de ces thèmes).

print_top_words(lda, vectorizer.get_feature_names() , n_top_words)

extracted topics

Et voilà le résultat ! Comme demandé dans les paramètres, le modèle a détecté 11 thèmes dans le corpus de 350 documents. Comme on pouvait s’y attendre, un très grand nombre de thèmes sont des sous-thèmes du machine learning (beaucoup d’articles publiés sur medium portent en effet sur ce thème).

Le topic 1 par exemple est lié aux modèles RNN pour le traitement du langage naturel.

Le topic 7 fait intervenir des architectures de calcul (GPU, CPU, motherboard, etc…)

Et ainsi de suite… Comme spécifié en introduction, le topic modelling fait ressortir des thèmes abstraits dans l’ensemble des documents. Il est ensuite possible, un document étant donné, de savoir à quel point il s’attache à un topic en particulier, en utilisant les vecteurs Theta du modèle LDA.

C) Visualisation avec PyLDAvis

Pour finir, il peut être intéressant (et amusant) de visualiser les thèmes extraits des articles sur un graphique. C’est possible grâce à PyLDAvis, un module Python qui permet comme son nom l’indique de visualiser les résultats du modèle LDA.

import pyLDAvis
import pyLDAvis.sklearn

data_viz = pyLDAvis.sklearn.prepare(lda, dtm, vectorizer)

Ce morceau de code permet de charger les résultats à visualiser.

pyLDAvis.display(data_viz)

visualisation

On peut remarquer que les thèmes extraits sont très proches entre eux (il s’agit des sous-thèmes du machine learning), sauf les thèmes 1 et 2 qui sont un peu plus isolés (remarque : il ne s’agit pas de la même numérotation que précédemment).

Conclusion

Il y a encore beaucoup de matière à étudier dans le topic modelling. J’ai présenté un aspect très simple et introductif du sujet, mais il s’agit d’un domaine très utile et très utilisé dans l’industrie pour analyser des documents.

Pour aller plus loin, il est possible d’étudier des modèles autres que LDA, il y a par exemple ARTM.

Il est aussi possible de réaliser une exploration préalable des données (très conseillé lorsque les données sont de moins bonnes qualités, ce qui arrive très souvent dans l’industrie).

L’utilisation du Topic Modelling peut permettre à une entreprise de mieux s’adapter à sa clientèle en analysant par exemple l’ensemble des commentaires émis sur les réseaux sociaux par les utilisateurs. Et contrairement à des méthodes de classification, il s’agit d’une approche non supervisée, qui ne demande pas de travail de labellisation, et qui peut constituer un point de départ pour mener des analyses encore plus poussées.

Et voilà, c’est tout pour aujourd’hui. N’hésitez pas à donner vos avis et suggestions en commentaire de cet article, ainsi qu’à partager à tous ceux qui pourraient être intéressés par des sujets de Machine Learning, autour de vous.


Ecrit par Samba Sall, élève-ingénieur à CentraleSupélec, et Data Scientist à Dassault Systèmes. Contact LinkedIn

This is a nice footer, yes !