Chapitre 10 : Analyse du sentiment

Hedonometer
L’humeur de Twitter au US  by hedonometer 

Une nouvelle industrie

L’Hedonometer est un des cas les plus remarquables de l’industrialisation du langage. c’est en partie le propos de  Dominique Boullier et Audrey Lohard Opinion mining et ‎Sentiment analysis – Méthodes et outils (Sciences Po – médialab ).

Un exemple d’application industrielle est Stride, ( page de test)

Les applications sont évidentes :

  • suivi de l’image de marque
  • analyse de l’impact d’événement sociaux
  • Contrôle de l’ « atmosphère » d’un réseau social

Deux grandes approches :

  • compositionnelle : par agrégation des sentiments unitaires associés aux unités élémentaire ( mots)
  • par apprentissage en codant manuellement les contenus et en entraînant sur ce jeu de données ( texte+ codage sentiment) différents modèles de ML ( decision tree, SVM)

Sur r différentes solutions sont possible : sentimentanalysis, et dans notre cas tidytext.

2 – Mise en oeuvre avec tidyverse

Avec tidyverse les opérations sont simples. Plusieurs dictionnaires de sentiments sont disponibles grâce à tidytext. Nous allons comparer les résultats des analyses utilisant ces différents dictionnaires. Attention : certains mots du corpus ne sont pas indexés avec les sentiments, à l’instar de home. L’analyse se fait donc sur un vocabulaire réduit par rapport à celui du corpus. Nous allons tout d’abord nous intéresser au corpus entier, puis nous observerons plus en détail certains mots et les topics dégagés précédemment.

4.1 Sentiment NRC

Le dictionnaire des sentiments NRC contient 7 types d’émotions : anticipation, peur, joie, surprise, tristesse, colère, dégoût et confiance et 2 types de sentiments : négatif, positif. Nous allons croiser ces sentiments avec notre corpus :

tbl_stem_sent<-tbl_stem%>%
  inner_join(get_sentiments("nrc"))%>%
  group_by(sentiment)%>%
  count(word, sort=TRUE)
## Joining, by = "word"
tbl_stem_sent
## # A tibble: 1,672 x 3
## # Groups:   sentiment [10]
##    sentiment    word          n
##                 
##  1 anticipation share       627
##  2 joy          share       627
##  3 positive     share       627
##  4 trust        share       627
##  5 positive     community   412
##  6 negative     tax         150
##  7 sadness      tax         150
##  8 fear         rule        125
##  9 trust        rule        125
## 10 positive     benefit      89
## # ... with 1,662 more rows

Nous allons tout d’abord regarder comment se répartissent ces émotions et sentiments dans le corpus :

tbl_stem_sent%>%
  ggplot(aes(sentiment))+
  geom_bar(aes(fill=sentiment))+
  coord_flip()

sent01.png

Nous pouvons constater une forte prédominance des émotions et sentiments positifs.

Regardons maintenant à quels émotions et sentiments appartiennent les mots les plus présents du corpus : (Attention, certains mots sont associés à plusieurs émotions et sentiments, il faut donc retraiter les données pour obtenir des représentations graphiques correctes.)

tbl_stem_sent<- tbl_stem_sent%>%
  rename(occ=n)
test<-tbl_stem_sent%>%
  ungroup()%>%
  select(word)%>%
  count(word, sort=TRUE)%>%
  rename(nword=n)
tbl_stem_sent<-inner_join(tbl_stem_sent,test)
## Joining, by = "word"
tbl_stem_sent<-tbl_stem_sent%>%
  mutate(n=occ/nword)%>%
  arrange(desc(occ))%>%
  select(sentiment,word,n,occ)
tbl_stem_sent
## # A tibble: 1,672 x 4
## # Groups:   sentiment [10]
##    sentiment    word          n   occ
##                  
##  1 anticipation share     157.    627
##  2 joy          share     157.    627
##  3 positive     share     157.    627
##  4 trust        share     157.    627
##  5 positive     community 412     412
##  6 negative     tax        75     150
##  7 sadness      tax        75     150
##  8 fear         rule       62.5   125
##  9 trust        rule       62.5   125
## 10 positive     benefit    89      89
## # ... with 1,662 more rows
g1<-tbl_stem_sent%>%
  filter(occ>50)%>%
  select(occ,word,n,sentiment)%>%
  ggplot(aes(word, n)) +
  geom_col(aes(fill=sentiment)) +
  xlab(NULL) +
  coord_flip()
g1

sent02

g2<-tbl_stem_sent%>%
  filter(occ>50)%>%
  select(occ,word,n,sentiment)%>%
  ggplot(aes(word, n)) +
  geom_col(aes(fill=sentiment)) +
  facet_wrap(~sentiment,ncol = 3, scales = "free_x")+
  coord_flip()+
  theme_light()+
  theme(legend.position=c(0.7,0.1), legend.direction = "horizontal", axis.title.y = element_blank(),axis.title.x = element_blank(),axis.text.y=element_text(size=7))
g2

sent03

g1<-g1+
   theme(legend.position="none", axis.title.y = element_blank(),axis.title.x = element_blank(),axis.text.y=element_text(size=7))
g2<-g2+
  theme(axis.text.y=element_text(size=5))

grid.arrange(g1,g2, nrow=2, heights=c(1.5, 3.5))

sent04

Il y a très clairement une forte prédominance des émotions et sentiments positifs. Les émotions surprise, dégoût et colère ne sont pas présents parmi les termes les plus fréquents. Pour plus de détails, nous pouvons regarder les mots représentatifs de chacun de ces sentiments et émotions :

#créer les graphiques
vide<-ggplot()
a<-7

tbl_nrc_anger<-tbl_stem_sent%>%
  filter(sentiment=="anger")%>%
  arrange(desc(occ))%>%
  slice(1:10)%>%
  ggplot(aes(word,occ))+
  geom_col()+
  theme_light()+
  coord_flip()+
  labs(title = "anger")+
  theme(axis.title.x = element_blank(), axis.title.y = element_blank(),axis.text.y = element_text(size=a), legend.position="none")
#On fait ensuite la même chose pour les autres sentiments
#les afficher

plot_grid(tbl_nrc_positive,tbl_nrc_joy,tbl_nrc_trust,
          tbl_nrc_anticipation,tbl_nrc_surprise,
          vide,
          tbl_nrc_negative,tbl_nrc_sadness, tbl_nrc_fear,
          tbl_nrc_anger,tbl_nrc_disgust,
          ncol=3,label_x = 0.5, label_y = 1)

sent05

Le terme“share” est largement dominant dans les émotions positives.

4.2 Sentiment Bing

Le dictionnaire Bing décrit les termes selon qu’ils sont positifis ou négatifs. A laide de ce dictionnaire, nous pouvons voir quels sont les 10 mots les plus contributifs au côté positif ou négatif :

bing_word_counts <- tbl_stem %>%
  inner_join(get_sentiments("bing")) %>%
  count(word, sentiment, sort = TRUE) %>%
  ungroup()
## Joining, by = "word"
bing_word_counts %>%
  group_by(sentiment) %>%
  top_n(10) %>%
  ungroup() %>%
  mutate(word = reorder(word, n)) %>%
  ggplot(aes(word, n, fill = sentiment)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~sentiment, scales = "free_y") +
  labs(y = "Contribution au sentiment",
       x = NULL) +
  coord_flip()
## Selecting by n

sent06.png

On peut également voir la répartition la contribution des documents au sentiment dans le temps :

tbl_bing<- tbl_stem %>%
  inner_join(get_sentiments("bing")) %>%
  count(year,  sentiment,doc, sort=TRUE) %>%
  spread(sentiment, n, fill = 0) %>%
  mutate(sentiment = positive - negative)
## Joining, by = "word"
ggplot(tbl_bing, aes(doc, sentiment, fill = year)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~year, ncol = 2, scales = "free_x")+
  theme(axis.text.x = element_blank())

sent07

4.3 Sentiment Afinn

Le dictionnaire de sentiment Afinn attribue un score allant de -5 à 5 aux mots. Nous pouvons regarder la répartition des mots selon leur contribution au sentiment global du corpus :

tbl_affin<-tbl_stem%>%
  inner_join(get_sentiments("afinn"))%>%
  count(word,score,sort=TRUE)%>%
  mutate(contribution = n * score) %>%
  arrange(desc(abs(contribution))) %>%
  head(30) %>%
  mutate(word = reorder(word, contribution)) %>%
  ggplot(aes(word, contribution, fill = contribution > 0)) +
  geom_col(show.legend = FALSE) +
  xlab("Les 30 mots les plus contributifs au sentiment") +
  ylab("Sentiment score * nombre d'occurrences") +
  coord_flip()
## Joining, by = "word"
tbl_affin 

sent08

 

A nouveau, le corpus est largement positif.

4.4 Comparer les trois sentiments

Nous pouvons comparer la répartition des documents selon les différentes méthodes d’analyse du sentiment vues précédemment :

afinn <- tbl_stem %>% 
    inner_join(get_sentiments("afinn")) %>% 
    group_by(doc)%>%
    summarise(sentiment = sum(score)) %>% 
    mutate(method = "AFINN")  
## Joining, by = "word"
bing_and_nrc <- bind_rows(tbl_stem %>% 
                              inner_join(get_sentiments("bing")) %>%
                              mutate(method = "Bing et al."),
                            tbl_stem %>% 
                              inner_join(get_sentiments("nrc") %>% 
                                           filter(sentiment %in% c("positive", 
                                                                   "negative"))) %>%
                              mutate(method = "NRC")) %>%
    count(method,doc, sentiment) %>%
    spread(sentiment, n, fill = 0) %>%
    mutate(sentiment = positive - negative)
## Joining, by = "word"
## Joining, by = "word"
bind_rows(afinn, 
          bing_and_nrc) %>%
  ggplot(aes(doc, sentiment, fill = method)) +
  geom_col(show.legend = FALSE) +
  facet_wrap(~method, ncol = 1, scales = "free_y")+
  theme(axis.text.x = element_blank())

sent09

Pour aller plus loin la maîtrise des dictionnaires est nécessaire

3 – la nécessité des dictionnaires

L’avantage des approche par dictionnaire est de disposer d’une référence, le corpus sur lesquels ils se constitue pouvant être précisément caractérisé.

Il semble qu’il y ait peu de lexique publics en français et ne pas hésitez à les signaler). Dans le secteur privé il en existe certainement de très performants, ils sont commercialisé dans des solutions plus générales, où de plus en plus as a service à l’exemple de stride,  dendelion  de manière plus générale MonkeyLearn, ou encore les solutions des grands services de cloud à l’exemple de Azure de Microsoft.

Préparer son propre dictionnaire. méthode de propagation.

4 – Des approches par apprentissage.

Par apprentissage en codant manuellement les contenus et en entraînant sur ce jeu de données ( texte+ codage sentiment) différents modèles de ML ( decision tree, SVM). Ce type de méthode est employée quotidiennement par les anti-spam de nos boites mails, et s’appuie généralement sur un modèle bayesien naif, qui a l’avantage de se réactualiser en fonction des actions de l’utilisateur ( signaler les mails indésirables).  Il suffira d’ajouter un bouton bonheur sur les mail que l’on reçoit pour se faire son propre baromètre d’humeur des messages entrants ( et pourquoi pas aussi sortant).

Une approche plus sophistiquée peut passer par une phase de filtrage et la vectorisation du corpus, avec word2vec. par exemple la procédure suivante  qui s’appuie simplement sur le calcul d’un vecteur moyen  comme input dans l’analyse et pose la question de la pondération des mots du verbatim (il propose d’utiliser le score TFIDF). Le modèle d’apprentissage est un réseaux de 32 neurones en deux couches.

Les spécialistes de l’apprentissage automatique sophistiqueront autant qu’il le peuvent pour gagner en précision ( elle est dans l’exemple annoncée à 80%) pourvu que le corpus soit assez grand pour prendre en compte beaucoup plus de paramètres. Un exemple est donné notamment par Andrew Ng dans un papier de 2013.

Publicités