From 1d08d7cc70242be0beea2ab4eb214a07117bd6b7 Mon Sep 17 00:00:00 2001 From: Maria Stefan Date: Tue, 14 Jul 2020 17:31:12 +0200 Subject: [PATCH] =?UTF-8?q?Tokenizer=20customis=C3=A9.=20Prend=20en=20comp?= =?UTF-8?q?te=20les=20mots=20avec=20un=20'-'.=20Premi=C3=A8re=20version.?= =?UTF-8?q?=20A=20am=C3=A9liorer.=20Ref=20#19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il faut encore ajouter plus de mots dans la liste de mots composés. Il faut aussi faire marcher le custom training pour le POS tagger, son problème c'est que l'apprentissage se passe avant le tokenizer customisé donc avant que les mots comme 'celui-là' par exemple deviennent un seul token. --- .gitignore | 2 + .gitlab-ci.yml | 2 +- models/__init__.py | 0 .../__main__.py | 10 +- .../coreferences/analyses_texte.py | 39 +---- .../coreferences/custom_model_creation.py | 41 +++++ .../custom_model_training/__init__.py | 0 .../custom_model_params/compound_words.json | 57 +++++++ .../compound_words_old.json | 58 +++++++ .../custom_model_training/custom_tokenizer.py | 73 +++++++++ .../custom_model_training/train_new_tagger.py | 83 ++++++++++ .../data/phrases_test | 18 ++- .../test-precision_et_historique_precision.py | 40 ++++- .../test-extraction_mot-duree-d-execution.py | 3 - .../test-extraction_mot-mot-inexistant.py | 14 +- .../test-extraction_mot-mots-composes.py | 5 +- ...est-extraction_mot-relations_entre_mots.py | 33 +++- .../tests/test-informations_pronoms.py | 32 ++-- .../tests/test-pronoms-et-mots-composes.py | 29 +++- .../tests/test-relations_mot.py | 142 +++++++++++++++++- .../tests/test-spacy.py | 23 +++ run_tests.sh | 2 + 22 files changed, 618 insertions(+), 88 deletions(-) create mode 100644 models/__init__.py create mode 100644 resolution_coreferences_pronominales/coreferences/custom_model_creation.py create mode 100644 resolution_coreferences_pronominales/custom_model_training/__init__.py create mode 100644 resolution_coreferences_pronominales/custom_model_training/custom_model_params/compound_words.json create mode 100644 resolution_coreferences_pronominales/custom_model_training/custom_model_params/compound_words_old.json create mode 100644 resolution_coreferences_pronominales/custom_model_training/custom_tokenizer.py create mode 100644 resolution_coreferences_pronominales/custom_model_training/train_new_tagger.py create mode 100644 resolution_coreferences_pronominales/tests/test-spacy.py create mode 100644 run_tests.sh diff --git a/.gitignore b/.gitignore index d8a43fa..189ddb8 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,5 @@ resolution_coreferences_pronominales/tests/tests-spacy.py uninstall.sh resolution_coreferences_pronominales/data/phrases_tmp + +resolution_coreferences_pronominales/data/brouillon diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0d22891..30ab385 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,7 @@ job:mon_test: - apt-get update -qy - sh install.sh - python3 ./resolution_coreferences_pronominales/__main__.py - - for f in resolution_coreferences_pronominales/tests/*.py; do if [ $f != resolution_coreferences_pronominales/tests/test-duree_execution.py ]; then python3 "$f"; fi; done + - for f in resolution_coreferences_pronominales/tests/*.py; do if [ $f != resolution_coreferences_pronominales/tests/test-duree_execution.py and $f != resolution_coreferences_pronominales/tests/test-spacy.py]; then python3 "$f"; fi; done - python3 resolution_coreferences_pronominales/tests-regression/test-duree_execution.py 1 1 - sh uninstall.sh diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resolution_coreferences_pronominales/__main__.py b/resolution_coreferences_pronominales/__main__.py index 43f5551..6ddce00 100755 --- a/resolution_coreferences_pronominales/__main__.py +++ b/resolution_coreferences_pronominales/__main__.py @@ -1,14 +1,14 @@ import sys +import os + sys.path.append(".") from resolution_coreferences_pronominales.coreferences import analyses_texte if __name__ == '__main__': + filename = os.path.basename(__file__) + print('Start ' + filename) phrase = "Le chat est tombé dans le puits. Il est profond. Il s'est blessé la patte." print(phrase) print("\nChargement en cours... \n") - infos = analyses_texte.informations_pronoms(phrase) print(analyses_texte.affichier_antecedents_dans_phrase(phrase, True)) - print('\nRésultat de la fonction analyses_texte.informations_pronoms(phrase) :') - print(analyses_texte.informations_pronoms(phrase)) - - + print('Completed : ' + filename) diff --git a/resolution_coreferences_pronominales/coreferences/analyses_texte.py b/resolution_coreferences_pronominales/coreferences/analyses_texte.py index 398f307..1957eab 100644 --- a/resolution_coreferences_pronominales/coreferences/analyses_texte.py +++ b/resolution_coreferences_pronominales/coreferences/analyses_texte.py @@ -1,35 +1,10 @@ from statistics import mean - import spacy -import fr_core_news_sm from resolution_coreferences_pronominales.coreferences import relations_entre_mots -from spacy.matcher import Matcher - - -def nlp_loader(): - nlp = fr_core_news_sm.load() - matcher = Matcher(nlp.vocab) - matcher.add('HYPHENS', None, [{'IS_ALPHA': True}, {'ORTH': '-', 'OP': '+'}, {'IS_ALPHA': True}]) - liste = ['intelligence artificielle'] - for e in liste: - zzz = [] - for i in e.split(" "): - zzz.append({'ORTH': i}) - matcher.add(e, None, zzz) - - def quote_merger(doc): - # this will be called on the Doc object in the pipeline - matched_spans = [] - matches = matcher(doc) - for match_id, start, end in matches: - span = doc[start:end] - matched_spans.append(span) - for span in matched_spans: # merge into one token after collecting all matches - span.merge() - return doc - - nlp.add_pipe(quote_merger, first=True) # add it right after the tokenizer - return nlp +from spacy.tokens import Doc +import sys +sys.path.append(".") +from resolution_coreferences_pronominales.custom_model_training import custom_tokenizer # Prend une phrase et retourne des informations sur ses pronoms # Tous les mots sont lemmatisés @@ -44,7 +19,7 @@ def quote_merger(doc): def informations_pronoms(phrase: str or spacy.tokens.doc.Doc): # Nous vérifions si phrase est de type spacy.tokens.doc.Doc pour gagner du temps (car spacy.load('fr') est lent) if isinstance(phrase, str): - nlp = nlp_loader() + nlp = custom_tokenizer.nlp_loader() doc = nlp(phrase) else: doc = phrase @@ -143,7 +118,7 @@ def informations_pronoms(phrase: str or spacy.tokens.doc.Doc): def coreferences_phrase(phrase: str or spacy.tokens.doc.Doc, cache: bool): # Nous vérifions si phrase est de type spacy.tokens.doc.Doc pour gagner du temps (car spacy.load('fr') est lent) if isinstance(phrase, str): - nlp = nlp_loader() + nlp = custom_tokenizer.nlp_loader() phrase = nlp(phrase) infos_pronoms = informations_pronoms(phrase) coreferences = [] @@ -221,7 +196,7 @@ def coreferences_phrase(phrase: str or spacy.tokens.doc.Doc, cache: bool): def affichier_antecedents_dans_phrase(phrase: str, cache: bool): - nlp = nlp_loader() + nlp = custom_tokenizer.nlp_loader() phrase = nlp(phrase) coreferences = coreferences_phrase(phrase, cache) phrase_antecedents = '' diff --git a/resolution_coreferences_pronominales/coreferences/custom_model_creation.py b/resolution_coreferences_pronominales/coreferences/custom_model_creation.py new file mode 100644 index 0000000..0fe6896 --- /dev/null +++ b/resolution_coreferences_pronominales/coreferences/custom_model_creation.py @@ -0,0 +1,41 @@ +import os +import fr_core_news_sm +from spacy.matcher import Matcher + +custom_exceptions_list = ['intelligence artificielle', 'pommes de terre'] +nlp = fr_core_news_sm.load() +matcher = Matcher(nlp.vocab) +model_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) + '/models/' + + +def hyphen_tokens_merged(): + matcher.add('HYPHENS', None, [{'IS_ALPHA': True}, {'TEXT': '-'}, {'IS_ALPHA': True}]) + + +def custom_tokenizer_exceptions_list(custom_list): + for compound_word in custom_list: + pattern = [] + for word in compound_word.split(" "): + pattern.append({'TEXT': word}) + matcher.add(compound_word, None, pattern) + + +def custom_tokenizer_merger(doc): + # this methods add matches to the matches + hyphen_tokens_merged() + custom_tokenizer_exceptions_list(custom_exceptions_list) + + # this will be called on the Doc object in the pipeline + matched_spans = [] + matches = matcher(doc) + for match_id, start, end in matches: + span = doc[start:end] + matched_spans.append(span) + for span in matched_spans: # merge into one token after collecting all matches + span.merge() + return doc + + +nlp.add_pipe(custom_tokenizer_merger, first=True) # add it right after the tokenizer +# nlp.factories['custom_tokenizer_merger'] = custom_tokenizer_merger +nlp.to_disk(model_path + 'costom_model_v1') diff --git a/resolution_coreferences_pronominales/custom_model_training/__init__.py b/resolution_coreferences_pronominales/custom_model_training/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resolution_coreferences_pronominales/custom_model_training/custom_model_params/compound_words.json b/resolution_coreferences_pronominales/custom_model_training/custom_model_params/compound_words.json new file mode 100644 index 0000000..58866a8 --- /dev/null +++ b/resolution_coreferences_pronominales/custom_model_training/custom_model_params/compound_words.json @@ -0,0 +1,57 @@ +{ + "intelligence artificielle": { + "lemma" : "intelligence artificielle", + "pos" : "NOUN" + }, + "intelligences artificielles": { + "lemma" : "intelligence artificielle", + "pos" : "NOUN" + }, + "bande dessinée" : { + "lemma" : "bande dessinée", + "pos" : "NOUN" + }, + "bandes dessinées" : { + "lemma" : "bande dessinée", + "pos" : "NOUN" + }, + "compte rendu" : { + "lemma" : "compte rendu", + "pos" : "NOUN" + }, + "comptes rendus" : { + "lemma" : "compte rendu", + "pos" : "NOUN" + }, + "faux sens" : { + "lemma" : "faux sens", + "pos" : "NOUN" + }, + "hôtel de ville" : { + "lemma" : "hôtel de ville", + "pos" : "NOUN" + }, + "hôtels de ville" : { + "lemma" : "hôtel de ville", + "pos" : "NOUN" + }, + "coup de poing" : { + "lemma" : "coup de poing", + "pos" : "NOUN" + }, + "coups de poing" : { + "lemma" : "coup de poing", + "pos" : "NOUN" + }, + "ce qui" : { + "lemma" : "ce qui", + "pos" : "PRON", + "type_pro" : "relatif" + }, + "ceux qui" : { + "lemma" : "ce qui", + "pos" : "PRON", + "type_pro" : "relatif" + } + +} \ No newline at end of file diff --git a/resolution_coreferences_pronominales/custom_model_training/custom_model_params/compound_words_old.json b/resolution_coreferences_pronominales/custom_model_training/custom_model_params/compound_words_old.json new file mode 100644 index 0000000..4f8dc25 --- /dev/null +++ b/resolution_coreferences_pronominales/custom_model_training/custom_model_params/compound_words_old.json @@ -0,0 +1,58 @@ +{ + "intelligence artificielle": { + "lemma" : "intelligence artificielle", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Fem|Number=Sing" + }, + "intelligences artificielles": { + "lemma" : "intelligence artificielle", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Fem|Number=Plur" + }, + "bande dessinée" : { + "lemma" : "bande dessinée", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Fem|Number=Sing" + }, + "bandes dessinées" : { + "lemma" : "bande dessinée", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Fem|Number=Plur" + }, + "compte rendu" : { + "lemma" : "compte rendu", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Masc|Number=Sing" + }, + "comptes rendus" : { + "lemma" : "compte rendu", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Masc|Number=Plur" + }, + "faux sens" : { + "lemma" : "faux sens", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Masc" + }, + "hôtel de ville" : { + "lemma" : "hôtel de ville", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Masc|Number=Sing" + }, + "hôtels de ville" : { + "lemma" : "hôtel de ville", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Masc|Number=Plur" + }, + "coup de poing" : { + "lemma" : "coup de poing", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Masc|Number=Sing" + }, + "coups de poing" : { + "lemma" : "coup de poing", + "pos" : "NOUN", + "tag" : "NOUN__Gender=Masc|Number=Plur" + } + +} \ No newline at end of file diff --git a/resolution_coreferences_pronominales/custom_model_training/custom_tokenizer.py b/resolution_coreferences_pronominales/custom_model_training/custom_tokenizer.py new file mode 100644 index 0000000..e70568c --- /dev/null +++ b/resolution_coreferences_pronominales/custom_model_training/custom_tokenizer.py @@ -0,0 +1,73 @@ +import fr_core_news_sm +import os +from spacy.matcher import Matcher +import json +from spacy.language import Language +from spacy.tokens import Doc + +json_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) + \ + '/custom_model_training/custom_model_params/compound_words.json' + + +def nlp_loader(): + """ + Temporary fonction allowing to load the nlp with the custom tokenizer. + This will later become a fonction creating a new model and scripts will no longer be loading the model with + this fonction but directly from the new customized model + :return: nlp + """ + nlp = fr_core_news_sm.load() + + class CompoundWordsMerger: + def __init__(self, words_path): + # self.model_size = model_size + self.words_path = words_path + + def __call__(self, doc: Doc): + # Adding hyphen compound words to the matcher + matcher = Matcher(nlp.vocab) + matcher.add('HYPHENS', None, [{'IS_ALPHA': True}, {'TEXT': '-'}, {'IS_ALPHA': True}]) + + # Opening the json file containing the information about our custom compound words + with open(self.words_path) as json_file: + compound_words = json.load(json_file) + + # Creating a list which will contain the keys of the dictionary in words_path json file + # These keys correspond to the custom compound words text + custom_exceptions_list = [] + for key in compound_words.keys(): + custom_exceptions_list.append(key) + + # Adding the custom compound words from the json file to the matcher + for word in custom_exceptions_list: + pattern = [] + for word in word.split(' '): + pattern.append({'TEXT': word}) + matcher.add(word, None, pattern) + + # Adding the matches containing the compound words to the doc + matched_spans = [] + matches = matcher(doc) + for match_id, start, end in matches: + span = doc[start:end] + matched_spans.append(span) + if str(span) in compound_words.keys(): + nlp.tokenizer.add_special_case(str(span), + [{'ORTH': str(span), 'POS': compound_words[str(span)]["pos"]}]) + for span in matched_spans: # merge into one token after collecting all matches + span.merge() + + # Adding the custom lemmas for the custom compound words + for token in doc: + if ' ' in token.text: + if token.text in compound_words.keys(): + token.lemma_ = compound_words[token.text]["lemma"] + + return doc + + nlp.add_pipe(CompoundWordsMerger(json_path), + first=True) # , first=True : add it right after the tokenizer; default : last + + # Adding the custom pipeline to the factories + Language.factories['CompoundWordsMerger'] = lambda _: CompoundWordsMerger(json_path) + return nlp diff --git a/resolution_coreferences_pronominales/custom_model_training/train_new_tagger.py b/resolution_coreferences_pronominales/custom_model_training/train_new_tagger.py new file mode 100644 index 0000000..11ccc25 --- /dev/null +++ b/resolution_coreferences_pronominales/custom_model_training/train_new_tagger.py @@ -0,0 +1,83 @@ +import random +from pathlib import Path +import spacy +import sys +import os +sys.path.append(".") +from resolution_coreferences_pronominales.coreferences import analyses_texte + +output_dir = os.path.abspath(os.path.dirname(__file__)) + '/customPOS/' +base_model = 'fr_core_news_sm' + +# meta.json of the new model +lang = 'fr' +name = 'custom_sm' +description = 'Custom model based on fr_core_news_sm : French multi-task CNN trained on the ' \ + 'French Sequoia (Universal Dependencies) and ' \ + 'WikiNER corpus. Assigns context-specific token vectors, POS tags, dependency ' \ + 'parse and named entities. Supports identification of PER, LOC, ORG and MISC ' \ + 'entities.' +version = '0.0.0' + + +TRAIN_DATA = [ + ('I love eating', {'tags': ['PRON', 'VERB', 'VERB']}), + ('Adrien voudrait plus de gateau. Il est culotté celui-là.', + {'tags': ['PROPN', 'VERB', 'ADV', 'ADP', 'NOUN', 'PUNCT', 'PRON', 'VERB', 'ADJ', 'PRON','PUNCT','PRON', 'PUNCT']}) +] + + +def train_tagger(model='fr_core_news_sm', output=None, n_iter=0): + + # Loading the model with custom tokenizer + nlp = analyses_texte.nlp_loader() + + # Training the custom model tagger starting with the existing 'fr_core_news_sm' tagger + nlp.vocab.vectors.name = 'spacy_pretrained_vectors' + optimizer = nlp.begin_training() + for i in range(n_iter): + random.shuffle(TRAIN_DATA) + losses = {} + for text, annotations in TRAIN_DATA: + nlp.update([text], [annotations], sgd=optimizer, losses=losses) + print(losses) + + # Temporary ! Testing the trained model with phrases from a file + # ------------------------------------------------------------------------------------------ # + test_file = open(os.path.dirname(os.path.dirname(__file__)) + "/data/phrases_test", "r") + test_text = [] + for line, phrase in enumerate(test_file): + if line % 2 == 0: + test_text.append(phrase) + docs = list(nlp.pipe(test_text)) # same as, but faster than : docs = [nlp(text) for text in textes] + for doc in docs: + print(doc.text) + print([[token.text, token.pos_] for token in doc]) + # ------------------------------------------------------------------------------------------ # + + # save model to output directory + if output is not None: + output = Path(output) + if not output.exists(): + output.mkdir() + nlp.meta['lang'] = lang + nlp.meta['name'] = name + nlp.meta['description'] = description + nlp.meta['version'] = version + nlp.to_disk(output) + print("Saved model to", output) + + # test the save model + # Temporary ! Testing the trained model with phrases from a file + # ------------------------------------------------------------------------------------------ # + print("Loading from", output) + nlp2 = spacy.load(output) + docs = list(nlp2.pipe(test_text)) + for doc in docs: + print(doc.text) + print([[token.text, token.pos_] for token in doc]) + # ------------------------------------------------------------------------------------------ # + + +if __name__ == '__main__': + train_tagger(base_model, output_dir) diff --git a/resolution_coreferences_pronominales/data/phrases_test b/resolution_coreferences_pronominales/data/phrases_test index c1740df..8ae5531 100644 --- a/resolution_coreferences_pronominales/data/phrases_test +++ b/resolution_coreferences_pronominales/data/phrases_test @@ -1,3 +1,9 @@ +Le logiciel de traduction ne tient pas compte du contexte pour traduire un mot, ce qui peut résulter en des faux sens flagrants. +[['ce qui', 'Le logiciel de traduction ne tient pas compte du contexte pour traduire un mot']] +Adrien voudrait plus de gateau. Il est culotté celui-là. +[['Il', 'adrien'], ['celui-là', 'adrien']] +Adrien voudrait plus de gateau. Il est culotté celui-ci. +[['Il', 'adrien'], ['celui-ci', 'adrien']] Le chien est tombé dans le puits. Il a aboyé toute la nuit. [['Il', 'chien']] Le chien est tombé dans le puits. Il est profond. @@ -10,7 +16,13 @@ Le chien est tombé dans le puits. Il s'est cassé le museau. Il va ainsi reteni [['Il', 'chien'], ["s'", 'chien'], ['Il', 'chien']] Jean est entré. Il s'est assis toute de suite. [['Il', 'Jean'], ["s'", 'Jean']] -Adrien voudrait plus de gateau. Il est culotté celui-là. -[['Il', 'adrien'], ['celui', 'adrien']] Hugo s'est acheté une bicyclette. Ce moyen de transport est très écologique. -[["s'", 'Hugo']] \ No newline at end of file +[["s'", 'Hugo']] +Aujourd'hui Volodia est tombé car Julien a crié. Il est méchant celui-ci. L'intelligence artificielle est passionnante. +[['Il', 'Julien'], ['celui-ci', 'Julien']] +Aujourd'hui Maria est tombé car Julien a crié. Il est méchant celui-là. Anna aime l'intelligence artificielle. Elle trouve qu'elle est passionnante. +[['Il', 'Julien'], ['Elle', 'Anna'], ['elle', 'intelligence artificielle']] +J'aime lire tous les soirs une bande dessinée, cela permet de me détendre. +[['cela', 'lire tous les soirs une bande dessinée']] +Le professeur veut que je rédige un compte rendu. Il veut que je me dépeche. +[['Il', 'professeur']] \ No newline at end of file diff --git a/resolution_coreferences_pronominales/tests-regression/test-precision_et_historique_precision.py b/resolution_coreferences_pronominales/tests-regression/test-precision_et_historique_precision.py index 71c480b..39aade0 100644 --- a/resolution_coreferences_pronominales/tests-regression/test-precision_et_historique_precision.py +++ b/resolution_coreferences_pronominales/tests-regression/test-precision_et_historique_precision.py @@ -4,8 +4,10 @@ sys.path.append(".") from resolution_coreferences_pronominales.coreferences import analyses_texte +import time if __name__ == '__main__': + start = time.time() phrases_test = open(os.path.dirname(os.path.dirname(__file__)) + "/data/phrases_test", "r") numero_ligne = 0 incorrect = 0 @@ -22,13 +24,36 @@ correct += len(corefs_justes) else: print(phrase.rstrip() + " : PAS TOTALEMENT JUSTE") - for ind in range(len(corefs_justes)): - if corefs_justes[ind][1] != corefs_a_tester[ind][1]: - print("La coréférence trouvée pour le pronom numéro " + str(ind + 1) + " est " + - str(corefs_a_tester[ind]) + " alors qu'elle devrait etre " + str(corefs_justes[ind])) - incorrect += 1 - else: - correct += 1 + if len(corefs_a_tester) == len(corefs_justes): + for ind in range(len(corefs_justes)): + if corefs_justes[ind][1].lower() != corefs_a_tester[ind][1].lower(): + print("La coréférence trouvée pour le pronom numéro " + str(ind + 1) + " est " + + str(corefs_a_tester[ind]) + " alors qu'elle devrait etre " + str(corefs_justes[ind]) + '\n') + incorrect += 1 + else: + correct += 1 + elif len(corefs_a_tester) != 0 and len(corefs_justes) != 0 and len(corefs_a_tester) != len(corefs_justes): + print("Toutes les coréférences n'ont pas été trouvées, seulement :", corefs_a_tester) + elif len(corefs_a_tester) == 0: + print('Aucune coréférence trouvée' + '\n') + incorrect == len(corefs_justes) + else: # len(corefs_a_tester) < len(corefs_justes) + for ind_j in range(len(corefs_justes)): + for ind_t in range(len(corefs_a_tester)): + if corefs_justes[ind_j][0].lower() == corefs_a_tester[ind_t][0].lower(): + if corefs_justes[ind_j][1].lower() != corefs_a_tester[ind_t][1].lower(): + print("La coréférence trouvée pour le pronom numéro " + str(ind + 1) + " est " + + str(corefs_a_tester[ind]) + " alors qu'elle devrait etre " + str( + corefs_justes[ind]) + '\n') + incorrect += 1 + break + else: + correct += 1 + break + else: + print() + incorrect += 1 + numero_ligne += 1 phrases_test.close() precision = (correct - incorrect) / correct @@ -56,3 +81,4 @@ else: historique_precision.write("\n" + str(precision)) historique_precision.close() + print(time.time() - start) \ No newline at end of file diff --git a/resolution_coreferences_pronominales/tests/test-extraction_mot-duree-d-execution.py b/resolution_coreferences_pronominales/tests/test-extraction_mot-duree-d-execution.py index 150808d..0f8d121 100644 --- a/resolution_coreferences_pronominales/tests/test-extraction_mot-duree-d-execution.py +++ b/resolution_coreferences_pronominales/tests/test-extraction_mot-duree-d-execution.py @@ -24,6 +24,3 @@ (time1 / nb_boucle)) + " secondes --- par boucle en moyenne") print("temps total --- " + str((time.time() - start_time_total)) + " secondes --- pour " + str( nb_boucle) + " boucles") - - # Si vous voulez vider le cache : - # extraction_mot.vider_cache() diff --git a/resolution_coreferences_pronominales/tests/test-extraction_mot-mot-inexistant.py b/resolution_coreferences_pronominales/tests/test-extraction_mot-mot-inexistant.py index f5b914e..0ca8705 100644 --- a/resolution_coreferences_pronominales/tests/test-extraction_mot-mot-inexistant.py +++ b/resolution_coreferences_pronominales/tests/test-extraction_mot-mot-inexistant.py @@ -1,11 +1,19 @@ import sys +import os sys.path.append(".") from resolution_coreferences_pronominales.coreferences import mot if __name__ == '__main__': + filename = os.path.basename(__file__) + print('Start : ' + filename) le_mot = 'bfjhjnfkfjiez' extraction_html = mot.extraction_html(le_mot, 'all') - if extraction_html is not None: - sys.exit('Problème de gestion de mots inexistants sur jdm ( dans extraction_mot.extraction_html() )\n' - 'Le mot \'' + le_mot + '\' n\'est pas censé exister sur jdm mais la fonction ne retourne pas None !') + try: + assert extraction_html is None + except AssertionError: + sys.exit(filename + ' : Problème de gestion de mots inexistants sur jdm ( ' + 'dans extraction_mot.extraction_html() )\n' + 'Le mot \'' + le_mot + '\' n\'est pas censé exister sur jdm mais la fonction ne ' + 'retourne pas None !') + print('Completed : ' + filename) diff --git a/resolution_coreferences_pronominales/tests/test-extraction_mot-mots-composes.py b/resolution_coreferences_pronominales/tests/test-extraction_mot-mots-composes.py index 41f3177..6820e94 100644 --- a/resolution_coreferences_pronominales/tests/test-extraction_mot-mots-composes.py +++ b/resolution_coreferences_pronominales/tests/test-extraction_mot-mots-composes.py @@ -5,6 +5,8 @@ from resolution_coreferences_pronominales.coreferences import mot if __name__ == '__main__': + filename = os.path.basename(__file__) + print('Start : ' + filename) le_mot = 'être vivant' mot.relations_mot(le_mot, 'all', True) if ' ' in le_mot: @@ -14,4 +16,5 @@ if os.path.isfile(chemin_fichier): print('Le fichier\n' + chemin_fichier + '\na bien été créé (ou existait déjà)') else: - print('Erreur création fichier ' + chemin_fichier) + print(filename + ' : Erreur création fichier ' + chemin_fichier) + print('Completed : ' + filename) diff --git a/resolution_coreferences_pronominales/tests/test-extraction_mot-relations_entre_mots.py b/resolution_coreferences_pronominales/tests/test-extraction_mot-relations_entre_mots.py index f305bb6..409935d 100644 --- a/resolution_coreferences_pronominales/tests/test-extraction_mot-relations_entre_mots.py +++ b/resolution_coreferences_pronominales/tests/test-extraction_mot-relations_entre_mots.py @@ -1,15 +1,36 @@ import sys +import os sys.path.append(".") from resolution_coreferences_pronominales.coreferences.relations_entre_mots import relations_entre_mots if __name__ == '__main__': + filename = os.path.basename(__file__) + print('Start : ' + filename) cache = True liste_mots = ["eau", "rivière", "profond"] - print("Résultat de relations_entre_mots(" + str(liste_mots) + ", " + str(cache) + ") :") - for e in relations_entre_mots(liste_mots, True): - print(e) + resultat = relations_entre_mots(liste_mots, cache) + attendu = [ + ['eau', 'rivière', + {0: 0.000423, 10: 0.000273, 15: 0.000606, 27: 2.7e-05, 35: 7.1e-05, 666: 4e-06, 163: 2.5e-05}], + ['rivière', 'eau', {0: 0.000493, 3: 3.5e-05, 9: 7.1e-05, 10: -0.040313549832026875, 28: 0.000448, 50: 6.5e-05, + 666: 4e-06, 131: 2.5e-05}], + ['profond', 'eau', {0: 3.2e-05}] + + ] + try: + assert resultat == attendu + except AssertionError: + sys.exit('test-extraction_mot-relations_entre_mots.py FAILED for : ' + str(liste_mots)) liste_mots = ["eau", "rivièreeeee", "profond"] - print("\nRésultat de relations_entre_mots(" + str(liste_mots) + ", " + str(cache) + ") :") - for e in relations_entre_mots(liste_mots, True): - print(e) + resultat = relations_entre_mots(liste_mots, cache) + attendu = [ + ['profond', 'eau', {0: 3.2e-05}] + ] + try: + assert resultat == attendu + except AssertionError: + sys.exit(filename + ' FAILED for : ' + str(liste_mots)) + print('Completed : ' + filename) + + diff --git a/resolution_coreferences_pronominales/tests/test-informations_pronoms.py b/resolution_coreferences_pronominales/tests/test-informations_pronoms.py index 73b9290..d0e6909 100644 --- a/resolution_coreferences_pronominales/tests/test-informations_pronoms.py +++ b/resolution_coreferences_pronominales/tests/test-informations_pronoms.py @@ -1,22 +1,22 @@ import sys +import os sys.path.append(".") from resolution_coreferences_pronominales.coreferences import analyses_texte -if __name__ == '__main__': - # phrase = "L'humain a été mordu par le chien. Il lui a déchiré le bras. Il va ainsi retenir la leçon." - # phrase = "Le chien est tombé dans le puits. Il s'est cassé le museau. Il va ainsi retenir la leçon." - # phrase = "Le chien est tombé dans le puits. Il est profond." - # phrase = "L'humain a mis un coup de poing au chien. Il lui a cassé le museau." - # phrase = '\"Le chien est tombé dans le puits. Il s\'est cassé le museau. Il va ainsi retenir la leçon.\"' - # phrase = 'Le chien est tombé dans le puits. Il s\'est cassé le museau. Il n\'y a aucune raison de paniquer.' - # phrase = 'Le chien est tombé dans le puits. Il s\'est cassé le museau. Il n\'y a aucune raison de paniquer.' - # phrase = 'Adrien est tombé dans le puits. Il est maladroit celui-là.' - # phrase = 'Adrien est tombé et a pleuré car Julien l\'a poussé. Il est méchant celui-là.' - phrase = "Adrien est tombé car Julien a crié. Il est méchant celui-là." - - infos = analyses_texte.informations_pronoms(phrase) - print(phrase) - for pronom in infos: - print(pronom) +filename = os.path.basename(__file__) +print('Start : ' + filename) +phrase = "Le chien est tombé dans le puits. Il s'est cassé le museau. Il va ainsi retenir la leçon." +resultat = analyses_texte.informations_pronoms(phrase) +attendu = [ + ['Il', ['chien', 'puits'], {'ROOT': 'casser', 'sens': 'sortante', 'obj': 'museau'}], + ["s'", ['chien', 'puits'], {'ROOT': 'casser', 'sens': 'sortante', 'obj': 'museau'}], + ['Il', ['chien', 'puits', 'museau'], {'ROOT': 'aller', 'sens': 'sortante', + 'xcomp': ['retenir', {'aux': 'ainsi', 'obj': 'leçon'}]}] +] +try: + assert resultat == attendu +except AssertionError: + sys.exit(filename + ' FAILED for : ' + phrase) +print('Completed : ' + filename) diff --git a/resolution_coreferences_pronominales/tests/test-pronoms-et-mots-composes.py b/resolution_coreferences_pronominales/tests/test-pronoms-et-mots-composes.py index b4b8547..cca4ad3 100644 --- a/resolution_coreferences_pronominales/tests/test-pronoms-et-mots-composes.py +++ b/resolution_coreferences_pronominales/tests/test-pronoms-et-mots-composes.py @@ -1,11 +1,24 @@ import sys -import spacy -import fr_core_news_sm +import os + sys.path.append(".") -from resolution_coreferences_pronominales.coreferences import analyses_texte +from resolution_coreferences_pronominales.custom_model_training import custom_tokenizer -phrase = u'Aujourd\'hui Volodia est tombé car Julien a crié. Il est méchant celui-là. L\'intelligence artificielle ' \ - u'est passionnante.' -nlp = analyses_texte.nlp_loader() -doc = nlp(phrase) -print([token.text for token in doc]) +filename = os.path.basename(__file__) +print('Start : ' + filename) +text = 'intelligences artificielles intelligence artificielle bandes dessinées bande dessinée comptes rendus compte ' \ + 'rendu faux sens faux sens hôtels de ville hôtel de ville' +nlp = custom_tokenizer.nlp_loader() +doc = nlp(text) +for token in doc: + try: + assert ' ' in token.text + except AssertionError: + sys.exit('Tokenizer error, a token which is meant to be compound is not : ' + token.text) + if token.i % 2 == 0: + try: + assert token.lemma_ == doc[token.i + 1].text + except AssertionError: + sys.exit(filename + ' : Tokenizer error, problem with : "' + token.text + '" or/and it\'s lemma : "' + + doc[token.i + 1].text + '"') +print('Completed : ' + filename) diff --git a/resolution_coreferences_pronominales/tests/test-relations_mot.py b/resolution_coreferences_pronominales/tests/test-relations_mot.py index 1d6da06..0aca062 100644 --- a/resolution_coreferences_pronominales/tests/test-relations_mot.py +++ b/resolution_coreferences_pronominales/tests/test-relations_mot.py @@ -1,11 +1,147 @@ import sys +import os sys.path.append(".") from resolution_coreferences_pronominales.coreferences import mot if __name__ == '__main__': - cache = True + filename = os.path.basename(__file__) + print('Start : ' + filename) + cache = False le_mot = 'chatouilles' - for e in mot.relations_mot(le_mot, 'all', False): - print(e) + resultat = mot.relations_mot(le_mot, 'all', cache) + attendu = [ + ['chatouillement', '0', 2.5e-05, 'sortante'], + ['chatouille', '0', 2.6e-05, 'sortante'], + ["plume>140176';1", '0', 3e-05, 'sortante'], + ['chatouiller', '0', 2.6e-05, 'sortante'], + ['guili guili', '0', 2.5e-05, 'sortante'], + ['_COM', '0', 1.0, 'sortante'], + ['affect', '0', 4e-05, 'sortante'], + ["chatouilles>171870';1", '2', 4.5e-05, 'sortante'], + ["chatouilles>2946892';1", '2', 4.5e-05, 'sortante'], + ['Ver:Conjug', '4', 4.5e-05, 'sortante'], + ['VerbalTime:Present', '4', 4.5e-05, 'sortante'], + ['Number:Plur', '4', 4.5e-05, 'sortante'], + ['Gender:Fem', '4', 4.5e-05, 'sortante'], + ['VerbalMode:Indicatif', '4', 4.5e-05, 'sortante'], + ['VerbalMode:Subjonctif', '4', 4.5e-05, 'sortante'], + ['VerbalPers:P2', '4', 2.5e-05, 'sortante'], + ['VerbalNumber:SG', '4', 2.5e-05, 'sortante'], + ['Ver:', '4', 5.8e-05, 'sortante'], + ['Ver:SPre+SG+P2', '4', 2.5e-05, 'sortante'], + ['Ver:IPre+SG+P2', '4', 2.5e-05, 'sortante'], + ['Ver:IPre+SG+P2:SPre+SG+P2', '4', 5.5e-05, 'sortante'], + ['Nom:Fem+PL', '4', 5.2e-05, 'sortante'], + ['Nom:', '4', 5.5e-05, 'sortante'], + ['faire des chatouilles', '11', 0.00016, 'sortante'], + ['_FL:0', '12', 1.8e-05, 'sortante'], + ['Morpho:nospace', '18', 3.2e-05, 'sortante'], + ['Morpho:min', '18', 3.1e-05, 'sortante'], + ['chatouiller', '19', 5.4e-05, 'sortante'], + ['chatouille', '19', 5.3e-05, 'sortante'], + ['donner la chair de poule', '24', 1.7e-05, 'sortante'], + ['faire rire', '24', 9.7e-05, 'sortante'], + ['satisfaction', '32', 7.5e-05, 'sortante'], + ['bonheur', '32', 8.1e-05, 'sortante'], + ['surprise', '32', 7.4e-05, 'sortante'], + ['indifférence', '32', 4.4e-05, 'sortante'], + ['_POL-NEG', '36', 5.8e-05, 'sortante'], + ['_POL-POS', '36', 0.000131, 'sortante'], + ['_POL-NEUTRE', '36', 0.000103, 'sortante'], + ['_POL-POS_PC', '36', 4.5e-05, 'sortante'], + ['_POL-NEUTRE_PC', '36', 3.5e-05, 'sortante'], + ['_POL-NEG_PC', '36', 2.9e-05, 'sortante'], + ['_INFO-COUNTABLE-YES', '36', 4e-05, 'sortante'], + ['_INFO-POLYMORPHIC', '36', 2.5e-05, 'sortante'], + ['_INFO-WIKI-YES', '36', 2.5e-05, 'sortante'], + ['éclater de rire', '41', 0.000105, 'sortante'], + ['avoir des frissons', '41', 3.6e-05, 'sortante'], + ['rire', '41', 9.6e-05, 'sortante'], + ['wiki:@', '444', 3.1e-05, 'sortante'], + ['_SW', '555', 0.000124, 'sortante'], + ['Eunuque', '777', 1e-05, 'sortante'], + ['Hatchepsout', '777', 1e-05, 'sortante'], + ['eunuques', '777', 1e-05, 'sortante'], + ['tsarines', '777', 1e-05, 'sortante'], + ['Rire', '777', 1e-05, 'sortante'], + ['Douleur', '777', 1e-05, 'sortante'], + ['oreille', '777', 1e-05, 'sortante'], + ['onomatopéique', '777', 1e-05, 'sortante'], + ['Centre national de ressources textuelles et lexicales', '777', 1e-05, 'sortante'], + ['nocicepteur', '777', 1e-05, 'sortante'], + ['chtouille', '777', 1e-05, 'sortante'], + ['primate', '777', 1e-05, 'sortante'], + ['ultrason', '777', 1e-05, 'sortante'], + ['Aristote', '777', 1e-05, 'sortante'], + ['nociceptif', '777', 1e-05, 'sortante'], + ['Graziadio Isaia Ascoli', '777', 1e-05, 'sortante'], + ['PubMed', '777', 1e-05, 'sortante'], + ['Les Chatouilles', '777', 1e-05, 'sortante'], + ['Current Biology', '777', 1e-05, 'sortante'], + ['langues romanes', '777', 1e-05, 'sortante'], + ['eunuque', '777', 1e-05, 'sortante'], + ['rétroaction', '777', 1e-05, 'sortante'], + ['onomatopée', '777', 1e-05, 'sortante'], + ['langues indo-européennes', '777', 1e-05, 'sortante'], + ['Socrate', '777', 1e-05, 'sortante'], + ['ISBN', '777', 1e-05, 'sortante'], + ['primates', '777', 1e-05, 'sortante'], + ['Épiderme', '777', 1e-05, 'sortante'], + ['Londres', '777', 1e-05, 'sortante'], + ['douleur', '777', 1e-05, 'sortante'], + ['Tsar', '777', 1e-05, 'sortante'], + ['plante des pieds', '777', 1e-05, 'sortante'], + ['Médecine', '777', 1e-05, 'sortante'], + ['chatouille', '777', 1e-05, 'sortante'], + ['épiderme', '777', 1e-05, 'sortante'], + ['Littré', '777', 1e-05, 'sortante'], + ['rats', '777', 1e-05, 'sortante'], + ['Oreille', '777', 1e-05, 'sortante'], + ['Platon', '777', 1e-05, 'sortante'], + ['Rat', '777', 1e-05, 'sortante'], + ['ancien français', '777', 1e-05, 'sortante'], + ['oreilles', '777', 1e-05, 'sortante'], + ['rire', '777', 1e-05, 'sortante'], + ['fétichisme sexuel', '777', 1e-05, 'sortante'], + ['université de Cambridge', '777', 1e-05, 'sortante'], + ['Nature', '777', 1e-05, 'sortante'], + ['ultrasons', '777', 1e-05, 'sortante'], + ['Charles Darwin', '777', 1e-05, 'sortante'], + ['DOI', '777', 1e-05, 'sortante'], + ['International Standard Book Number', '777', 1e-05, 'sortante'], + ['PMID', '777', 1e-05, 'sortante'], + ['plume', '0', 2e-05, 'entrante'], + ['chatouillement', '0', 2.5e-05, 'entrante'], + ['guili guili', '0', 2.5e-05, 'entrante'], + ["plume>140176';1", '0', 3.1e-05, 'entrante'], + ['pied', '0', 3.7e-05, 'entrante'], + ['rendre fou', '0', 5.1e-05, 'entrante'], + ['prêtre pédophile', '0', 5.1e-05, 'entrante'], + ['faire des papouilles', '0', 5.1e-05, 'entrante'], + ['chatouillis', '0', 5.8e-05, 'entrante'], + ['plante du pied', '0', 6.4e-05, 'entrante'], + ['chatouiller', '0', 9e-05, 'entrante'], + ['faire des chatouilles', '0', 0.000157, 'entrante'], + ['donner la chair de poule', '13', 3e-05, 'entrante'], + ['faire rire', '13', 3e-05, 'entrante'], + ['rendre fou', '16', 3.4e-05, 'entrante'], + ['chatouiller', '22', 3.1e-05, 'entrante'], + ['avoir des frissons', '42', 7.1e-05, 'entrante'], + ['rire', '42', 0.000191, 'entrante'], + ['éclater de rire', '42', 0.00021, 'entrante'], + ['fétichisme du pied', '777', 1e-05, 'entrante'], + ['podophilie', '777', 1e-05, 'entrante'], + ['hoquet', '777', 1e-05, 'entrante'], + ['myoclonie phrénoglottique', '777', 1e-05, 'entrante'], + ['plante', '777', 1e-05, 'entrante'], + ['plante des pieds', '777', 1e-05, 'entrante'], + ['soirée pyjama', '777', 1e-05, 'entrante'], + ['plante du pied', '777', 1e-05, 'entrante'] + ] + try: + assert resultat == attendu + except AssertionError: + sys.exit(filename + ' FAILED for : ' + le_mot) + print('Completed : ' + filename) diff --git a/resolution_coreferences_pronominales/tests/test-spacy.py b/resolution_coreferences_pronominales/tests/test-spacy.py new file mode 100644 index 0000000..823c22c --- /dev/null +++ b/resolution_coreferences_pronominales/tests/test-spacy.py @@ -0,0 +1,23 @@ +import sys +import os +import time + +sys.path.append(".") +from resolution_coreferences_pronominales.custom_model_training import custom_tokenizer + +if __name__ == '__main__': + phrases_test = open(os.path.dirname(os.path.dirname(__file__)) + "/data/phrases_test", "r") + start = time.time() + textes = [] + nlp = custom_tokenizer.nlp_loader() + for numero_ligne, phrase in enumerate(phrases_test): + if numero_ligne % 2 == 0: + textes.append(phrase) + docs = list(nlp.pipe(textes)) # same as, but faster than : docs = [nlp(text) for text in textes] + print(time.time() - start) + print(nlp.pipe_names) + start = time.time() + for doc in docs: + print('\n', doc.text, end='') + print([[token.text, token.pos_, token.dep_] for token in doc]) + print(str(time.time() - start)) diff --git a/run_tests.sh b/run_tests.sh new file mode 100644 index 0000000..2498b74 --- /dev/null +++ b/run_tests.sh @@ -0,0 +1,2 @@ +python3 ./resolution_coreferences_pronominales/__main__.py; +for f in resolution_coreferences_pronominales/tests/*.py; do if [ $f != resolution_coreferences_pronominales/tests/test-duree_execution.py ] && [ $f != resolution_coreferences_pronominales/tests/test-spacy.py ]; then python3 "$f"; fi; done \ No newline at end of file