diff --git a/negation_conversion.py b/negation_conversion.py index 0ec7e36..51346a0 100644 --- a/negation_conversion.py +++ b/negation_conversion.py @@ -16,7 +16,7 @@ sentences = ["This is not a test.", ] from nltk.parse.corenlp import CoreNLPServer -from nltk.parse.corenlp import CoreNLPParser, CoreNLPDependencyParser +from nltk.parse.corenlp import CoreNLPParser set_negatives = set(['no','not','never']) # https://nlp.stanford.edu/software/stanford-parser-full-2018-10-17.zip @@ -37,23 +37,23 @@ def deepest_negative_subtree(tree): if len(negative_subtrees) == 0: return tree else: - for s in negative_subtrees: - if s.height() == min(negative_subtrees_height): - return s + for t in negative_subtrees: + if t.height() == min(negative_subtrees_height): + return t def negative_scope_pos(pos_list): scope_pos_list = [] - not_dummy = False + scope = False for pos_word in pos_list: # Fin du scope - if pos_word[1] in ['WDT'] and not_dummy==True and len(scope_pos_list)>0: - not_dummy = False + if pos_word[1] in ['WDT'] and scope==True and len(scope_pos_list)>0: + scope = False # Intérieur du scope - if not_dummy==True: + if scope==True: scope_pos_list.append(pos_word) # Début du scope if pos_word[0].lower() in set_negatives: - not_dummy = True + scope = True return scope_pos_list @@ -79,18 +79,19 @@ def convert_negated_words(sentence): # Analyser la phrase parse = next(parser.raw_parse(sentence)) - # Extraire les noeuds qui représentent une négation - - print(parse) - + # Identifier les parties négatives dans les sous arbres dns_pos_list = [] for st in parse.subtrees(): if is_negative_tree(st): + # Plus petit sous-arbre négatif current_dns = deepest_negative_subtree(st) + # Section affectée par la négation dns_pos_list.append(negative_scope_pos(current_dns.pos())) + # Sélectionner les sections négatives uniques (plusieurs sous-arbres génèrent la même section) unique_dns_pos_list = [x for i, x in enumerate(dns_pos_list) if i == dns_pos_list.index(x)] + # Produire les remplacements à effecture converted_sentence = sentence for dns_pos in unique_dns_pos_list: to_replace = " ".join(x[0] for x in dns_pos) @@ -109,8 +110,9 @@ if __name__ == '__main__': for sent in sentences: print("\nS:", sent) output_file.write("S: "+sent) - converted = convert_negated_words(sent) + # Modifications pour ressembler au fichier du prof qui a des espaces devant les ponctuations + converted = convert_negated_words(sent).replace("."," .").replace(","," ,") print("N:", converted) - output_file.write("\nN: "+converted.replace("."," .").replace(","," ,")+"\n\n") + output_file.write("\nN: "+converted+"\n\n") output_file.close() server.stop() \ No newline at end of file diff --git a/rapport.md b/rapport.md index db4851b..b4169ff 100644 --- a/rapport.md +++ b/rapport.md @@ -139,10 +139,39 @@ Le principal constat que l'on peut tirer de cet exercice est qu'il y a plusieurs ## Introduction +Dans cet exercice, il faut identifier la portée de la négation dans une phrase à l'aide d'un analyseur syntaxique. + +## Approche + +L'analyseur syntaxique utilisé est Stanford CoreNLP Parser. C'est un analyseur syntaxique en profondeur par constituants. J'ai préféré cette approche, car généralement la négation s'applique à un groupe de mots et pas nécessairement autour d'un verbe et ses dépendances. + +### 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) + - 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) + - Identifier la section où la négation a une portée effective (section du groupe de mots après le mot négatif, mais avant un autre groupe de mots, voir détail de l'algorithme 3) +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 2: deepest_negative_subtree](algorithme_2.pdf) + +![Algorithme 3: negative_scope_from_pos](algorithme_3.pdf) + +\pagebreak + +## Analyse des résultats et explications des erreurs + ## Conclusion +\pagebreak + # Références ## Classification de textes @@ -160,3 +189,4 @@ Le principal constat que l'on peut tirer de cet exercice est qu'il y a plusieurs ## Analyse syntaxique - [Syntax Parsing with CoreNLP and NLTK](https://www.districtdatalabs.com/syntax-parsing-with-corenlp-and-nltk) +- [Stanford CoreNLP Parser](https://www.nltk.org/api/nltk.parse.html#nltk.parse.corenlp.CoreNLPParser)