Ce tutoriel présuppose que vous avez fait vos premiers pas avec le module R Quanteda et que vous maîtrisez les notions stemming, stopwords, matrice document-terme, etc. On part du principe que vous avez installé et activé les modules quanteda, quanteda.textstats, quanteda.textplots, readtext, seededlda et magrittr.

Créez un corpus de textes à partir de données existantes

Idéalement, vous avez déjà un ensemble de fichiers de textes groupés dans un dossier (par exemple vos transcriptions d’interviews). Demandez à R d’ingérer tout le contenu de ce dossier à l’aide de la fonction readtext.

mestextes <- readtext("chemin/vers/votre/dossier/de/textes") Code language: R (r)

Si vous n’avez pas encore un tel dossier de textes, vous pouvez faire ingérer un exemple. Par exemple, une Collection de romans français du dix-huitième siècle:

mestextes <- readtext("chemin/vers/le/fichier/exemple_textes.zip") Code language: R (r)

Astuce: notez que la fonction readtext est capable de lire non seulement des dossiers standard mais aussi des dossiers compressés (zip, tar, etc.).

À ce stade, vos textes sont stockés en mémoire dans un tableau de données plutôt brut. Vous aurez besoin de ce format plus tard, si vous lemmatisez. Pour l’heure, transformez-le en un objet de type Quanteda corpus de textes pour la suite des analyses:

moncorpus <- corpus(mestextes)Code language: R (r)

Analysez le corpus

Transformons le corpus de textes en un corpus de tokens. Notez que nous utilisons du simple stemming pour éviter les redondances dans les flexions des mots (montage – montagnes, etc.). La lemmatisation serait plus sophistiquée, mais demande nettement plus de temps de calcul. Je la présente plus loin dans ce tutoriel.

Comme toujours, nous allons enlever les mots dépourvus de teneur sémantique pertinente pour notre analyse.

mystopwords <- c(stopwords("fr"),"si","plus","où")Code language: JavaScript (javascript)

N’hésitez pas à compléter cette liste en fonction de la nature de vos données et de vos besoins. Veillez à choisir le bon langage des stopwords (ici, “fr”, mais, par exemple “en” si vous travaillez avec des textes en anglais).

Dans la chaîne de création des tokens, j’emploie aussi la commande token_split("'", valuetype = "fixed"), afin d’éviter que des mots avec déterminant à apostrophe soient traités comme distincts (“humanité”, “l’humanité”):

mestokens <- tokens(moncorpus, remove_punct=T, remove_symbols=T, remove_numbers=T) %>% 
tokens_split("'", valuetype = "fixed") %>%
tokens_remove(mystopwords) %>% 
tokens_wordstem(language="fr")Code language: HTML, XML (xml)

Construisons à présent une matrice document-feature (DFM).

madfm <- dfm(mestokens) %>% 
dfm_trim(min_termfreq = 3, max_termfreq = 300, min_docfreq=10)Code language: R (r)

Regardons de plus près le code ci-dessus. Notez d’abord que nous ne conservons que des mots qui apparaissent au moins 3 fois pour réduire le bruit de notre modèle. Par contre, baissez évidemment ce paramètre min_termfreq à 1 si les mots qui n’apparaissent qu’une seule fois dans tout votre corpus sont importants pour votre analyse.

Notez aussi le paramètre max_termfreq. Ce dernier enlève au contraire les termes trop fréquents, qui peuvent dominer la matrice document-terme et obscurcir les thèmes sous-jacents. Réduire la taille de la DFM accélère en outre dramatiquement l’algorithme de détection de thématiques.

Le paramètre min_docfreq=10, enfin, signifie qu’un terme doit apparaître dans au moins 10 documents différents pour être pris en compte. Une telle contrainte n’est pas conseillée dans la majorité des analyses, mais, dans le cas de romans, elle permet d’éviter que des mots trop spécifiques à un texte unique – comme notamment les noms de ses personnages – ne dominent la définition de thèmes.

À ce stade, tout est prêt pour du topic modeling, notamment à l’aide de la méthode dite de “L’allocation de Dirichelet latente“. Procédons en nous rappelant qu’il demeure indispensable de modifier les paramètres ci-dessus par la suite, notamment en réaction réflexive au résultat de votre modélisation thématique, et à la lumière de votre connaissance de votre propre terrain. Le topic modeling est un processus itératif et ne saurait pas être entièrement automatisé. Autrement dit, l’approche quantitative ne vous dédouane pas de l’effort de penser votre terrain d’analyse.

Avec le module seededlda

Développé par l’équipe à l’origine du module Quanteda, le module seededlda englobe le plus ancien module topicmodels. Il permet de traiter directement un corpus de textes quanteda et donne notamment accès à la fonction textmodel_ld, qui identifie les thèmes principaux et leur présence dans les textes de ce corpus. Le paramètre k précise le nombre de thématiques que vous souhaitez. Adaptez-le selon vos besoins et vos observations.

Pour m’assurer que l’analyse avance bien, je spécifie verbose=TRUE; cela me permet de suivre l’avancement du modèle. Avertissement: cette fonction peut prendre du temps; profitez-en pour vous lever, vous hydrater et faire quelques exercices.

lda_model <- textmodel_lda(my_dfm, k = 6, auto_iter= T, verbose=TRUE) Code language: R (r)

Inspectez le modèle en analysant les termes dominants chacun des 6 topics.

terms(lda_model)Code language: R (r)

Comme vous le constatez, on trouve encore trop de noms propres, effectivement fréquents, et caractéristiques pour chaque roman. Comme ils ne nous disent rien sur le propos des romans, il faudra les supprimer en amont – au même titre que les stopwords.

Comme évoqué, il est impératif de répéter ce processus d’analyse avec divers paramètres de nettoyage pour obtenir un résultat parlant. Voici le code proposé jusqu’ici, adapté pour enlever les termes sans valeur pour l’analyse:

mystopwords <- c(stopwords("fr"),"si","plus","où","a","fait","alcibiad","zéphir","paul","capucin","ste","charlott","emile","zéphire","celle-là","théodor","eléonor","martin","dois-j","elles-mêm","pierrot","jasmin","à-la-fois","julien","septembr","jenny","bernard","andré","août","suzann","à-peu-pres","mai","léon","pouvez-vous","augustin","william","roland","ajouta-t-el","même-temp","bertrand","là-b","çà","continua-t-il","quel-qu","a-t-on","par-là","de-là","aurait-il","octobr","dimanch","guillaum","nicol","ci-dev","là-haut")
mestokens <- tokens(moncorpus, remove_punct = T, remove_symbols = T, remove_numbers = T) %>% 
tokens_split("'", valuetype = "fixed") %>% 
tokens_wordstem(language = "fr") %>% 
tokens_remove(mystopwords) 

madfm <- dfm(mestokens) %>% dfm_trim(min_termfreq = 10, max_termfreq = 300, min_docfreq=9)

lda_model <- textmodel_lda(madfm, k = 6, auto_iter= T, verbose=T)  # Specify the number of topics (k)

terms(lda_model,12)Code language: PHP (php)

Et son résultat

Des hypothèses quant à la logique thématique plus profonde qui regroupe les mots dans chacun de ces 6 thèmes commencent à s’esquisser. À ce stade, il commence à devenir intéressant d’examiner quels livres sont associés à quelles thématiques, à l’aide de la fonction topics(lda_model):

Pour avoir cette information dans un autre ordre, vous pouvez aussi exploiter le visualiseur de tableaux de RStudio:

texttopics <- data.frame(docnames(moncorpus),moncorpus$topic)
View(texttopics)Code language: PHP (php)

Aller plus loin

Lemmatiser plutôt que stemmer

Évidemment, le résultat sera bien meilleur si, au lieu du simple stemming, vous parvenez à lemmatiser votre corpus avant la tokenisation. Une nouvelle fois, veillez à bien spécifier la langue de votre corpus (dans mon cas, “french”) pour que le script utilise le bon modèle de lemmatisation. Je ne retiens que les noms, les verbes et les adjectifs, porteurs du cœur de la signification d’un texte.

getlemma <- function(x) {
	print(substr(x,1,100))
	ttg <- udpipe(x,object = udmodel) 
	ttg <- ttg[ttg$upos %in% c("NOUN","VERB","ADJ"),] 
	return(paste(ttg$lemma, collapse=" "))
}
udmodel <- udpipe_download_model(language = "french")
mestexteslematises <- lapply(mestextes$text,getlemma)
mestokens <- quanteda::tokens(unlist(mestexteslemmatises))
docnames(mestokens) <- mestextes$doc_idCode language: R (r)

L’exécution de ce script sera plus longue mais son résultat vaudra la peine. Profitez du temps d’attente pour faire vos courses ou une promenade à l’extérieur.

Astuce: pour ne pas avoir à refaire la lemmatisation, je recommande de sauvegarder son résultat

saveRDS(mestexteslematises, file = "chemin/du/dossier/de/votre/choix/mestokens.rds"))Code language: JavaScript (javascript)

Une fois le corpus de tokens lemmatisés constitué, la suite est identique à ce que nous avons déjà vu:

madfm <- dfm(mestokens) %>% 
dfm_trim(min_termfreq = 10, max_termfreq = 300, min_docfreq=9)
lda_model <- textmodel_lda(madfm, k = 6, auto_iter= T, verbose=T) 
terms(lda_model,12)Code language: R (r)

Ou alors, poursuivez ce tutoriel, pour employer un autre algorithme, qui vous permettra une visualisation plus intéressante.

Plus de détails numériques et graphiques

Le module topicmodels, sur lequel est construit seededlda, mais légèrement différent, donne accès à des informations supplémentaires qui permettent aussi des graphismes plus précis. Commençons par recalculer les thèmes:

install.packages("topicmodels")
library(ggplot2)
library(stringr)
lda_model2 <- topicmodels::LDA(convert(madfm, to = "topicmodels"), k = 6)Code language: R (r)

À ce stade, nous pouvons de nouveau identifier les mots principaux de chaque thème à l’aide de la fonction topicmodels::terms(lda_model2, 15):

Mais l’apport de l’usage direct du module topicmodels est surtout l’accès à la fonction topicmodels::posterior(lda_model2)$topics. Son résultat permet de ne pas associer un seul thème à chaque document, mais d’étudier la présence relative de chacun des thèmes identifiés dans chaque document. Un graphique plus précis peut alors être produit.

doc_topics <- topicmodels::posterior(lda_model2)$topics

df <- data.frame(doc_id = row.names(doc_topics) %>% str_replace(fixed(".txt"),""), doc_topics)
df_long <- tidyr::pivot_longer(df, cols = starts_with("X"), names_to = "topic", values_to = "importance")

p <- ggplot(df_long, aes(x = importance, y = doc_id, fill = factor(topic))) +
	geom_bar(stat = "identity") +
	labs(x = "Topic Importance", y = "Document ID", fill = "Topic") +
	theme_minimal() +
	theme(axis.text.y = element_text(angle = 0, hjust = 1))
p
ggsave("mytextsplot.png", plot= p, width=5,height=25)Code language: R (r)

Analyse du résultat avec l’intelligence artificielle

Pour peu de ne pas se reposer uniquement sur l’IA, vous pouvez tenter de soumettre vos résultats à ChatGPT, qui permet actuellement l’importation de fichiers de données.

ATTENTION ! Ne faites en aucun cas cela si vos données d’origine contiennent des données confidentielles!

Exportez les données de vos résultats:

data.table::fwrite(doc_topics %>% as.data.frame,file.path(basepath,"TopicModelling_doc_topics.csv"))
data.table::fwrite(topicmodels::terms(lda_model2, 200) %>% as.data.frame,file.path(basepath,"TopicModelling_topics.csv"))Code language: CSS (css)

Vous pouvez désormais glisser les fichiers TopicModelling_doc_topics.csv et TopicModelling_topics.csv dans l’interface de dialogue de ChatGPT et demander “J’aimerais une interprétation de chacun des six topics”. Le résultat est à prendre avec précaution, et mis en exergue avec votre propre expérience du terrain d’étude. Néanmoins, étant donné que les large language models, à la base des IA textuelles génératives, sont très proches de procédés statistiques comme l’allocation de Diriechelet latente (LDA), le recours à eux dans ce contexte fait particulièrement sens.

  1. Topic 1 :
    • Ce topic semble associer des termes liés à des titres ou rôles sociaux (prieur, sultan, régiment).
    • Potentiellement lié à des contextes historiques ou militaires.
  2. Topic 2 :
    • Les mots indiquent des concepts fondamentaux ou élémentaires (germe, substance, centre, élément, industrie).
    • Peut-être lié à la science ou à des discussions sur la nature des choses.
  3. Topic 3 :
    • Mots variés mais avec une connotation potentiellement négative ou corporelle (décharger, cuisse, cave, pendre, téton).
    • Pourrait être lié à des descriptions physiques ou à des récits plus sombres.
  4. Topic 4 :
    • Termes associés à des lieux et statuts (indigent, chapelle, vallée, maitresse, qre).
    • Semblerait traiter de la pauvreté, de la religion, ou de relations sociales.
  5. Topic 5 :
    • Mots évoquant des personnages historiques ou actions (écuyer, lance, trépas, calife, retentir).
    • Connotation chevaleresque ou historique.
  6. Topic 6 :
    • Termes variés avec une touche de prestige ou de formalité (supérieure, lady, courrier, fausseté, septembre).
    • Peut-être lié à des contextes sociaux élevés ou des intrigues.
ChatGPT, version GPT-4o, 1.6.2024

Annexe: visualisation de tous les documents

Leave a comment

Your email address will not be published. Required fields are marked *