fin rapport

This commit is contained in:
François Pelletier 2019-11-03 18:40:40 -05:00
parent 854f94f00c
commit 4dbc2cc9e4
4 changed files with 48 additions and 33 deletions

BIN
erreur_1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
erreur_2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -31,7 +31,7 @@ def deepest_negative_subtree(tree):
negative_subtrees = []
negative_subtrees_height = []
for s in tree.subtrees():
if is_negative_tree(s):
if is_negative_tree(s) and len(s.leaves())>1:
negative_subtrees.append(s)
negative_subtrees_height.append(s.height())
if len(negative_subtrees) == 0:
@ -46,7 +46,7 @@ def negative_scope_pos(pos_list):
scope = False
for pos_word in pos_list:
# Fin du scope
if pos_word[1] in ['WDT'] and scope==True and len(scope_pos_list)>0:
if pos_word[1] in ['WDT','RB',','] and scope==True and len(scope_pos_list)>0:
scope = False
# Intérieur du scope
if scope==True:
@ -85,6 +85,7 @@ def convert_negated_words(sentence):
if is_negative_tree(st):
# Plus petit sous-arbre négatif
current_dns = deepest_negative_subtree(st)
print("sous-arbre courant: "+str(current_dns))
# Section affectée par la négation
dns_pos_list.append(negative_scope_pos(current_dns.pos()))
@ -102,10 +103,10 @@ def convert_negated_words(sentence):
if __name__ == '__main__':
server = CoreNLPServer("/home/francois/stanford-corenlp-full-2018-10-05/stanford-corenlp-3.9.2.jar",
"/home/francois/stanford-corenlp-full-2018-10-05/stanford-english-corenlp-2018-10-05-models.jar")
server.start()
parser = CoreNLPParser()
#server = CoreNLPServer("/home/francois/stanford-corenlp-full-2018-10-05/stanford-corenlp-3.9.2.jar",
# "/home/francois/stanford-corenlp-full-2018-10-05/stanford-english-corenlp-2018-10-05-models.jar")
#server.start()
#parser = CoreNLPParser()
output_file = open("/home/francois/nlp_a2019_tp2/nlp_a2019_tp2/output_negative.txt","w")
for sent in sentences:
print("\nS:", sent)
@ -115,4 +116,4 @@ if __name__ == '__main__':
print("N:", converted)
output_file.write("\nN: "+converted+"\n\n")
output_file.close()
server.stop()
#server.stop()

View file

@ -18,38 +18,38 @@ Toutes les tâches de traitement du langage naturel ont été effectuées à l'a
## Prétraitement
Nous effectuons quatre étapes de prétraitement:
Nous effectuons quatre étapes de prétraitement :
- Tokenisation
- Normalisation
- Sélection des types
- Vectorisation
Pour chacune de ces étapes, certaines techniques seront expérimentées. Celles-ci produisent, aprèsla dernière étape, une matrice creuse de caractéristiques qui servira à entraîner les différents modèles. Nous ajoutons trois caractéristiques additionnelles à cette matrice, soit les nombres de mots positifs et négatifs, et le nombre total de mot du document.
Pour chacune de ces étapes, certaines techniques seront expérimentées. Celles-ci produisent, après la dernière étape, une matrice creuse de caractéristiques qui servira à entraîner les différents modèles. Nous ajoutons trois caractéristiques additionnelles à cette matrice, soit les nombres de mots positifs et négatifs, et le nombre total de mots du document.
### Tokenisation
La tokenisation a été effectuée à l'aide de **Tok-tok**. Ce tokenizer récent fonctionne à l'aide d'expressions régulières. Comme il fonctionne à l'aide de phrases et non de documents, les commentaires ont d'abord été segmentés en phrases à l'aide de **Punkt**. La segmentation sera aussi conservée, car elle sera utilisée à l'étape de la sélection des types, en utilisant un étiquetage de classes grammaticale (POS).
La tokenisation a été effectuée à l'aide de **Tok-tok**. Ce tokenizer récent fonctionne à l'aide d'expressions régulières. Comme il fonctionne à l'aide de phrases et non de documents, les commentaires ont d'abord été segmentés en phrases à l'aide de **Punkt**. La segmentation sera aussi conservée, car elle sera utilisée à l'étape de la sélection des types, en utilisant un étiquetage de classes grammaticales (POS).
Une correction a été apportée aux documents, car de nombreuses fin de phrases étaient représentées par deux espaces au lieu d'un point et d'un espace. Cette substitution a été effectuée avec une expression régulière. Enfin, comme Tok-tok conserve le dernier caractère de ponctuation de la phrase comme un token, celui-ci est supprimé avant de retourner le résultat.
Une correction a été apportée aux documents, car de nombreuses fins de phrases étaient représentées par deux espaces au lieu d'un point et d'un espace. Cette substitution a été effectuée avec une expression régulière. Enfin, comme Tok-tok conserve le dernier caractère de ponctuation de la phrase comme un token, celui-ci est supprimé avant de retourner le résultat.
### Normalisation
Deux types de normalisation sont testées dans cet exercice: la désuffixation et la lemmatisation.
Deux types de normalisation sont testés dans cet exercice : la désuffixation et la lemmatisation.
La désuffixation (*stemming*) est effectuée à l'aide de l'algorithme **Porter stemmer**. Cet algorithme ne conserve que la racine du mot, ce qui a pour effet de produire des racines qui peuvent n'avoir aucun lien avec la nature du mot. Par exemple, *was* devient *wa*.
La lemmatisation est effectuée à l'aide du **WordNet Lemmatizer**, qui utiliser la fonction *morphy* incluse dans WordNet. Si le mot n'est pas trouvé dans WordNet, il est retourné inchangé. Cet algorithme retourne par défaut un lemme qui est un nom, mais il est aussi possible de spécifier de retourner un verbe. C'est ce choix qui a été effectué dans le cas de cet exercice, car dans plusieurs cas nous avons des phrases incomplètes et ceci nous assure de retrouver au moins un verbe dans la phrase.
La lemmatisation est effectuée à l'aide du **WordNet Lemmatizer**, qui utilise la fonction *morphy* incluse dans WordNet. Si le mot n'est pas trouvé dans WordNet, il est retourné inchangé. Cet algorithme retourne par défaut un lemme qui est un nom, mais il est aussi possible de spécifier de retourner un verbe. C'est ce choix qui a été effectué dans le cas de cet exercice, car dans plusieurs cas nous avons des phrases incomplètes et ceci nous assure de retrouver au moins un verbe dans la phrase.
### Sélection des types
Trois types de sélection des types sont testés dans cet exercice:
Trois types de sélection des types sont testés dans cet exercice :
- La fréquence du mot est supérieure à 3 dans le corpus.
Cette approche est implémentée à l'aide d'un dictionnaire qui recense le dénombrement de chaque type.
- Le mot ne figure pas dans une liste de mots-outils (*stop-words*).
- Le mot ne figure pas dans une liste de mots-outils (*stop words*).
Cette approche utilise la liste de mots-outils incluse dans *nltk* pour l'anglais.
@ -57,19 +57,19 @@ Cette approche utilise la liste de mots-outils incluse dans *nltk* pour l'anglai
L'algorithme par défaut utilise le Penn Treebank, mais on effectue l'étiquetage à l'aide des **Universal POS tags** qui sont plus simples, comme on veut identifier des classes ouvertes à haut niveau, soit les noms, adjectifs, adverbes et verbes, représentés par NOUN, ADJ, ADV et VERB. Le mot *I* n'est pas classé correctement dans plusieurs cas, il est considéré comme un nom. Comme c'est un pronom, qui est une classe fermée, il est aussi éliminé de la liste des tokens sélectionnés.
L'usage de ces techniques de sélection des types limitent le nombre d'attributs et aident à réduire le surapprentissage.
L'usage de ces techniques de sélection des types limite le nombre d'attributs et aide à réduire le surapprentissage.
### Vectorisation
Trois types de vectorisations ont été testées dans cet exercice:
Trois types de vectorisations ont été testées dans cet exercice :
- Cardinalité des mots dans le commentaire
On utilise la fonction **CountVectorizer** de Scikit-Learn. Comme les données sont déjà tokenisées et transformées, on remplace les fonctions par défaut par la fonction identité.
- Occurence des mots dans le commentaire
- Occurrence des mots dans le commentaire
On utilise la même fonction que pour la vectorisation en compteurs, mais en ajoutant une contrainte binaire à l'aide d'un parametre additionnel.
On utilise la même fonction que pour la vectorisation en compteurs, mais en ajoutant une contrainte binaire à l'aide d'un paramètre additionnel.
- Mesure TF-IDF des mots présente dans le commentaire, comparé à l'ensemble des documents.
@ -84,22 +84,22 @@ Il est important de noter que ces trois méthodes produisent des matrices creuse
Afin de compter les mots ayant une polarité positive ou négative dans chacun des commentaires, on utilise l'extension SentiWordnet de Wordnet. On identifie d'abord le *synset* de WordNet, soit le groupe sémantique auquel appartient le mot, puis on extrait les valeurs de polarités associées à ce synset depuis SentiWordnet.
Lorsque le score est positif, on ajoute 1 au compteur de mots positifs, et lorsqu'il est négatif, on ajoute 1 au compteur de mots négatifs. On ne compte pas les mots neutres. Notons que nous n'utilisons pas les phrases ici, mais seulement les mots des commentaires, sous le modèle du sac de mots.
Lorsque le score est positif, on ajoute 1 au compteur de mots positifs, et lorsqu'il est négatif, on ajoute 1 au compteur de mots négatifs. On ne compte pas les mots neutres. Notons que nous n'utilisons pas les phrases, mais seulement les mots des commentaires, sous le modèle du sac de mots.
On crée aussi un attribut qui comprend le nombre de mots dans le commentaire.
Ces trois attributs sont ajoutés aux matrices créées par l'étape de vectorisation. Les matrices résultantes sont toujours creuses, en utilisant lesa fonctions `csr_matrix` et `hstack` de Scikit-Learn.
Ces trois attributs sont ajoutés aux matrices créées par l'étape de vectorisation. Les matrices résultantes sont toujours creuses, en utilisant les fonctions `csr_matrix` et `hstack` de Scikit-Learn.
## Entraînement des modèles
Deux types de modèles sont entrainés sur chacunes des 18 combinaisons de normalisation, sélection et vectorisation:
Deux types de modèles sont entrainés sur chacune des 18 combinaisons de normalisation, sélection et vectorisation :
- Naive Bayes, en utilisant la classe MultinomialNB du module `naive_bayes` de Scikit-Learn et en conservant les paramètres par défaut.
- Régression logistique, en utilisant la classe LogisticRegression du module `linear_model` de Scikit-Learn et en conservant les paramètres par défaut.
## Analyse de la performance
Pour chacun des modèles entraînés, les métriques de performance suivantes sont calculées à l'aide du jeu de données de test:
Pour chacun des modèles entraînés, les métriques de performance suivantes sont calculées à l'aide du jeu de données de test :
- Précision
$$
@ -118,20 +118,20 @@ Pour les modèles entrainés avec l'algorithme Naive Bayes, les résultats appar
#### Naive Bayes
![Métriques de performance: Naive Bayes](table_metriques_nb.pdf)
![Métriques de performance : Naive Bayes](table_metriques_nb.pdf)
On observe qu'avec l'approche de Naive Bayes, peu importe l'algorithme de normalisation, la sélection des classes ouvertes et la vectorisation TF-IDF donnent une plus grande précision, mais offrent aussi le rappel le plus bas. La vectorisation par occurences offre la meilleure exactitude et une précision comparable, pour un rappel généralement plus élevé.
La désuffixation, la sélection par classe ouverte et la vectorisation par occurence semblent former la meilleure combinaison à utiliser avec le classificateur Naive Bayes.
On observe qu'avec l'approche de Naive Bayes, peu importe l'algorithme de normalisation, la sélection des classes ouvertes et la vectorisation TF-IDF donnent une plus grande précision, mais offrent aussi le rappel le plus bas. La vectorisation par occurrences offre la meilleure exactitude et une précision comparable, pour un rappel généralement plus élevé.
La désuffixation, la sélection par classe ouverte et la vectorisation par occurrences semblent former la meilleure combinaison à utiliser avec le classificateur Naive Bayes.
#### Régression logistique
![Métriques de performance: Régression logistique](table_metriques_rl.pdf)
![Métriques de performance : Régression logistique](table_metriques_rl.pdf)
Contrairement à la situation avec le classificateur Naive Bayes, avec la régression logistique, le rappel est plus grand avec la vectorisation TF-IDF qu'avec les deux autres approches. La normalisation par désuffixation offre des performances légèrement supérieures sur les trois mesures dans la majorité des situations. Cependant, si on veut augmenter le rappel, il est mieux d'opter pour les approches utilisant la lemmatisation. Il n'y a pas de combinaison qui se démarque véritablement dans ce cas-ci, car les valeurs des métriques sont plutôt similaires. La combinaison désuffixation, sélection par classe ouverte et vectorisation TF-IDF serait une bonne approche.
## Conclusion
Le principal constat que l'on peut tirer de cet exercice est qu'il y a plusieurs approches disponibles pour effectuer le prétraitement d'un corpus en vue d'effectuer l'entraînement d'un modèle d'apprentissage statistique. Ces différentes méthodes peuvent influencer une ou plusieurs mesures de performance de l'algorithme d'entraînement. Si on veut utiliser l'algorithme Naive Bayes, on peut utiliser des approches moins sophistiquées de normalisation et de vectorisation, tel que le compteur d'occurences, sans perdre beaucoup de valeur statistique, alors que pour la régression logistique, nous sommes avantagés par les techniques qui conservent davantage de précision dans les données, tel que la vectorisation TF-IDF.
Le principal constat que l'on peut tirer de cet exercice est qu'il y a plusieurs approches disponibles pour effectuer le prétraitement d'un corpus en vue d'effectuer l'entraînement d'un modèle d'apprentissage statistique. Ces différentes méthodes peuvent influencer une ou plusieurs mesures de performance de l'algorithme d'entraînement. Si on veut utiliser l'algorithme Naive Bayes, on peut utiliser des approches moins sophistiquées de normalisation et de vectorisation, tel que le compteur d'occurrence, sans perdre beaucoup de valeur statistique, alors que pour la régression logistique, nous sommes avantagés par les techniques qui conservent davantage de précision dans les données, telle que la vectorisation TF-IDF.
\pagebreak
@ -147,8 +147,8 @@ L'analyseur syntaxique utilisé est Stanford CoreNLP Parser. C'est un analyseur
### Algorithme général
1. Pour chacun des sous-arbres identifiés avec l'analyseur syntaxique, pour une phrase donnée:
- Identifier si le sous-arbre contient une négation (à partir d'une liste de mots négatifs: `'no','not','never'`, voir algorithme 1)
1. Pour chacun des sous-arbres identifiés avec l'analyseur syntaxique, pour une phrase donnée :
- Identifier si le sous-arbre contient une négation (à partir d'une liste de mots négatifs : `'no','not','never'`, voir algorithme 1)
- Si oui, on continue le traitement
- Sinon, on ignore ce sous-arbre
- Identifier le sous-arbre le plus profond qui contient la négation, mais qui n'est pas composé seulement du mot négatif. L'arbre le plus profond est identifié comme celui ayant la hauteur la plus petite, parmi les sous-arbres considérés, de façon récursive (voir détail de l'algorithme 2)
@ -156,20 +156,34 @@ L'analyseur syntaxique utilisé est Stanford CoreNLP Parser. C'est un analyseur
2. Conserver seulement les sections uniques identifiées à l'étape 1
3. Transformer les sections identifiées précédemment dans la phrase en ajoutant `NOT_` devant chacun des mots
![Algorithme 1:is_negative_tree](algorithme_1.pdf)
![Algorithme 1: is_negative_tree](algorithme_1.pdf)
![Algorithme 2: deepest_negative_subtree](algorithme_2.pdf)
![Algorithme 2 : deepest_negative_subtree](algorithme_2.pdf)
![Algorithme 3: negative_scope_from_pos](algorithme_3.pdf)
![Algorithme 3 : negative_scope_from_pos](algorithme_3.pdf)
\pagebreak
## Analyse des résultats et explications des erreurs
Cet algorithme simple parvient à identifier correctement la portée de la négation dans 13 des 15 phrases. Voici les phrases où l'algorithme a échoué et une analyse des causes potentielles :
> It is not so much a work of entertainment as it is unique study
![Erreur 1](erreur_1.png)
On remarque que la négation *not* est directement associée au groupe verbal, et que tous les autres groupes en sont des sous-groupes. Ce qui fait de sorte que l'entièreté du groupe est associée à la négation, en tant qu'arbre le plus profond comportant la négation. Pour obtenir le même résultat que le fichier de validation, il faudrait être en mesure d'exclure le groupe subordonné (SBAR) de l'analyse par une règle.
> I would never do it even if I can.
![Erreur 2](erreur_2.png)
Dans le fichier de résultats, le verbe *do* n'est pas associé à la négation, mais dans plusieurs autres exemples, la négation est associée au verbe. On ne peut donc pas conclure que c'est vraiment une erreur de l'algorithme. Autrement, il faudrait écrire un algorithme différent lorsque le mot négatif est *never*.
## Conclusion
En utilisant l'analyse syntaxique par constituants, on peut identifier la portée de la négation dans plusieurs types de phrases différentes.
\pagebreak
# Références