ajouts de commentaires
This commit is contained in:
parent
eb62721320
commit
1e59a902fb
4 changed files with 87 additions and 67 deletions
|
@ -18,20 +18,20 @@ Comme on souhaite garder la forme la plus générale possible pour ces expressio
|
|||
|
||||
L'approche utilisée a été de trouver d'abord les mots interrogatifs, ensuite la structure générale de la phrase, dont le verbe principal et les noms à remplacer, le cas échéant. Puis, on a identifié les différents types de réponses requises, et lorsqu'un même motif de question nécessitait deux types de réponses différentes, on a séparé en deux expressions consécutives. Ensuite, on a construit quelques règles qui appliquent des corrections mineures au niveau des signes et de la ponctuation.
|
||||
|
||||
Toutes ces règles sont enchainées les unes à la suite des autres. Comme la conversion élimine le point d'interrogation à la fin de la phrase, les motifs subséquents au premier qui a donné une correspondance sont ainsi ignorés. Il n'y a donc aucun traitement conditionnel en dehors de ceux inclus dans les expressions régulières.
|
||||
Toutes ces règles sont enchainées les unes à la suite des autres. Comme la conversion élimine le point d'interrogation à la fin de la phrase, les motifs subséquents au premier qui a donné une correspondance sont ainsi ignorés. Il n'y a donc aucun traitement conditionnel en dehors de ceux inclus dans les expressions régulières. Si on n'est pas parvenu à transformer l'affirmation, on retourne une réponse par défaut. Ce qui ne se produit pas avec les questions de cet exercice.
|
||||
|
||||
### Description des motifs utilisés
|
||||
|
||||
Les motifs suivent cet ordre. Sauf indication contraire, le groupe affirmatif à convertir est situé après le motif recherché :
|
||||
|
||||
1. Mot interrogatif **qui**, suivi d'un verbe auxiliaire, sous forme d'énumération partielle ;
|
||||
2. Mot interrogatif **qu'est-ce que **;
|
||||
2. Mot interrogatif **qu'est-ce que**;
|
||||
3. Mot interrogatif **que** suivi d'un verbe d'état ou d'action. On a ici utilisé une énumération partielle pour identifier les verbes présents dans les questions en échantillon ;
|
||||
4. Mot interrogatif **où** ou de la forme **dans quelle** suivi d'une catégorie géographique, sous forme d'énumération partielle. On a aussi capturé la forme particulière **quelle est la capitale** où on remplace le mot **quelle** par où, afin de correspondre au motif précédent. Ceci permet d'éviter une règle additionnelle ;
|
||||
5. Mot interrogatif **quel** suivi soit du mot âge et du verbe avoir, soit du verbe auxiliaire et d'un nom représentant une quantité, sous forme d'énumération ;
|
||||
6. Mot interrogatif **quel** suivi du verbe être (forme générique du motif précédent) ;
|
||||
7. mot interrogatif **quand** ou groupe **en quelle année **;
|
||||
8. mot interrogatif **pourquoi **;
|
||||
8. mot interrogatif **pourquoi**;
|
||||
9. mot interrogatif **combien** suivi du mot **de **;
|
||||
10. mot interrogatif **combien** suivi de **y a-t-il**. La différence ici est que la réponse conserve l'ordre de la question et qu'il faut transformer **y a-t-il** en **il y a **;
|
||||
11. Mot interrogatif **quel** à la fin de la question. Ce motif sert à attraper une forme moins fréquente de questions où la question est posée après le groupe affirmatif.
|
||||
|
|
|
@ -7,6 +7,7 @@ results_filename = "./t1_results.txt"
|
|||
test_questions_fn = "./data/test-questions-t1.txt"
|
||||
test_results_filename = "./test-t1_results.txt"
|
||||
|
||||
|
||||
# Mettre dans cette partie les expressions régulières
|
||||
# que vous utilisez pour analyser les questions
|
||||
# et en faire la conversion en affirmations
|
||||
|
@ -19,10 +20,11 @@ def run_task1(filename):
|
|||
results = convert_all_questions(questions)
|
||||
save_results(results)
|
||||
|
||||
def run_task_test(filename, test_results_filename):
|
||||
|
||||
def run_task_test(filename, res_filename):
|
||||
questions = load_questions(filename)
|
||||
results = convert_all_questions(questions)
|
||||
save_results(results, test_results_filename)
|
||||
save_results(results, res_filename)
|
||||
|
||||
|
||||
def convert_all_questions(questions):
|
||||
|
@ -50,22 +52,25 @@ def convert(question):
|
|||
# En cas de doute, me consulter.
|
||||
# Votre code...
|
||||
|
||||
# Mot interrogatif **qui**
|
||||
regex_qui = re.compile(r"(qui)\s"
|
||||
r"(est|sont|était|étaient|sera|seront)\s"
|
||||
r"(.*)\?",
|
||||
re.IGNORECASE)
|
||||
modif1_0 = re.sub(regex_qui,
|
||||
r"\3 \2 Luc Lamontagne.",
|
||||
question)
|
||||
r"\3 \2 Luc Lamontagne.",
|
||||
question)
|
||||
|
||||
|
||||
regex_qui_a = re.compile(r"(qui)\s"
|
||||
r"(a)\s"
|
||||
r"(.*)\?",
|
||||
re.IGNORECASE)
|
||||
r"(a)\s"
|
||||
r"(.*)\?",
|
||||
re.IGNORECASE)
|
||||
modif1 = re.sub(regex_qui_a,
|
||||
r"Luc Lamontagne \2 \3.",
|
||||
modif1_0)
|
||||
|
||||
|
||||
regex_quel_nom = re.compile(r"quel\s"
|
||||
r"(est)\s"
|
||||
r"(le nom du chef)"
|
||||
|
@ -75,6 +80,7 @@ def convert(question):
|
|||
r"\2 \3 \1 Luc Lamontagne.",
|
||||
modif1)
|
||||
|
||||
# Mot interrogatif ** qu'est-ce que**
|
||||
regex_questce = re.compile(r"(qu'est-ce\squ[ie'])\s?"
|
||||
r"(.*)\?",
|
||||
re.IGNORECASE)
|
||||
|
@ -82,6 +88,7 @@ def convert(question):
|
|||
r"\2 est X.",
|
||||
modif1_1)
|
||||
|
||||
# Mot interrogatif **que**
|
||||
regex_quesont = re.compile(r"(qu[e'])\s"
|
||||
r"(est|sont|signifie|fait)-?(on)?"
|
||||
r"(.*)\?",
|
||||
|
@ -90,6 +97,7 @@ def convert(question):
|
|||
r"\4 \3 \2 X.",
|
||||
modif2)
|
||||
|
||||
# Mot interrogatif **où** ou de la forme **dans quelle**
|
||||
regex_ou = re.compile(r"((dans\squel(le)?\s(ville|région|pays))|où)\s"
|
||||
r"(est|sont)\s"
|
||||
r"(.*)\?",
|
||||
|
@ -101,8 +109,9 @@ def convert(question):
|
|||
regex_capitale = re.compile(r"(quel[le]*)\s(est)\s(la\scapitale|l'emplacement)(.*)\?", re.IGNORECASE)
|
||||
|
||||
modif4_1 = re.sub(regex_capitale,
|
||||
r"\3 \4 \2 Québec.", modif4)
|
||||
r"\3 \4 \2 Québec.", modif4)
|
||||
|
||||
# Mot interrogatif **quel**
|
||||
regex_mesure = re.compile(r"([aà]\s)?qu[el]+\s(âge\s)?"
|
||||
r"(est|avait)?\s?"
|
||||
r"(l[a']\s?)?"
|
||||
|
@ -117,18 +126,21 @@ def convert(question):
|
|||
re.IGNORECASE)
|
||||
modif6 = re.sub(regex_quelle_est, r"\1\3 \2 X.", modif5)
|
||||
|
||||
# mot interrogatif **quand**
|
||||
regex_quand = re.compile(r"(quand|en\squelle\sannée)\s(est\sné\s)?(.*)\?",
|
||||
re.IGNORECASE)
|
||||
modif7 = re.sub(regex_quand,
|
||||
r"\3 \2en 2000.",
|
||||
modif6)
|
||||
|
||||
# mot interrogatif **pourquoi **
|
||||
regex_pourquoi = re.compile(r"pourquoi\s(.*)\?",
|
||||
re.IGNORECASE)
|
||||
modif8 = re.sub(regex_pourquoi,
|
||||
r"\1 parce que X.",
|
||||
modif7)
|
||||
|
||||
# mot interrogatif **combien**
|
||||
regex_combien1 = re.compile(r"combien\sde\s(\w+)(.*)\?",
|
||||
re.IGNORECASE)
|
||||
modif9 = re.sub(regex_combien1,
|
||||
|
@ -143,27 +155,31 @@ def convert(question):
|
|||
r"\1 1000 \2.",
|
||||
modif9))
|
||||
|
||||
# Mot interrogatif **quel** à la fin
|
||||
regex_quelle_fin = re.compile(r"(.*?)(qu\w+)\s(\w+)\?",
|
||||
re.IGNORECASE)
|
||||
modif11 = re.sub(regex_quelle_fin,
|
||||
r"\1 \3 X.",
|
||||
modif10)
|
||||
|
||||
# Modifications non liées aux mots
|
||||
# Inversions de pronoms
|
||||
modif12 = re.sub(r"(\w+)(-t)?-(il|elle)", r"\1", modif11)
|
||||
modif12 = re.sub(r"(\w+)(-t)?-(vous)", r"\3 \1", modif12)
|
||||
modif12 = re.sub(r"\s{2,}", " ", modif12)
|
||||
modif12 = re.sub(r"^\s", "", modif12)
|
||||
modif12 = re.sub(r"\'\s", "'", modif12)
|
||||
modif13 = re.sub(r"(\w+)(-t)?-(vous)", r"\3 \1", modif12)
|
||||
|
||||
# Suppression des espaces
|
||||
modif14 = re.sub(r"\s{2,}", " ", modif13)
|
||||
modif15 = re.sub(r"^\s", "", modif14)
|
||||
modif16 = re.sub(r"\'\s", "'", modif15)
|
||||
|
||||
# Retourner la question seulement si on a réussi à la modifier en affirmation
|
||||
if question != modif11:
|
||||
return modif12
|
||||
return modif16
|
||||
else:
|
||||
return "la terre est ronde."
|
||||
|
||||
|
||||
def save_results(results, results_filename=results_filename):
|
||||
with open(results_filename, 'w') as output_file:
|
||||
def save_results(results, res_filename=results_filename):
|
||||
with open(res_filename, 'w') as output_file:
|
||||
for question, sentence in results:
|
||||
output_file.write("Q: " + question + "\n")
|
||||
output_file.write("A: " + sentence + "\n")
|
||||
|
@ -171,7 +187,7 @@ def save_results(results, results_filename=results_filename):
|
|||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Vous pouvez modifier cette section
|
||||
|
||||
print("Conversion des questions du fichier {} ".format(questions_fn))
|
||||
run_task1(questions_fn)
|
||||
run_task_test(test_questions_fn, test_results_filename)
|
||||
|
|
|
@ -21,11 +21,9 @@ all_origins = [] # la liste des 18 langues d'origines de noms
|
|||
|
||||
BOS = "~" # character used to pad the beginning of a name
|
||||
EOS = "!" # character used to pad the end of a name
|
||||
|
||||
v = 26
|
||||
kgram_models = {}
|
||||
|
||||
V = 26
|
||||
|
||||
|
||||
def find_files(path):
|
||||
"""Retourne le nom des fichiers contenus dans un répertoire.
|
||||
|
@ -52,8 +50,8 @@ def unicode_to_ascii(s):
|
|||
|
||||
def read_names(filename):
|
||||
"""Retourne une liste de tous les noms contenus dans un fichier."""
|
||||
with open(filename, encoding='utf-8') as f:
|
||||
names = f.read().strip().split('\n')
|
||||
with open(filename, encoding='utf-8') as file_handle:
|
||||
names = file_handle.read().strip().split('\n')
|
||||
return [unicode_to_ascii(name) for name in names]
|
||||
|
||||
|
||||
|
@ -67,6 +65,7 @@ def load_names():
|
|||
names_by_origin[origin] = names
|
||||
|
||||
|
||||
# Cette fonction normalise les mots en les convertissant en ASCII, mettant en minuscules et supprimant les espaces
|
||||
def normalize_word(word):
|
||||
word = unicode_to_ascii(word)
|
||||
word = word.lower()
|
||||
|
@ -74,10 +73,14 @@ def normalize_word(word):
|
|||
return word
|
||||
|
||||
|
||||
# On génère les unigrammes à l'aide d'une liste
|
||||
def make_unigram(word):
|
||||
return list(word)
|
||||
|
||||
|
||||
# On génère les k-grammes en ajoutant du padding et
|
||||
# en traversant la chaîne de caractères de gauche à droite
|
||||
# avec un pas de 1 et une largeur de k
|
||||
def make_kgram(unigram_word, k):
|
||||
# Ajouter padding
|
||||
unigram_with_padding = [BOS for i in range(1, k)] + unigram_word + [EOS for i in range(1, k)]
|
||||
|
@ -85,6 +88,7 @@ def make_kgram(unigram_word, k):
|
|||
return kgrams
|
||||
|
||||
|
||||
# Cette fonction crée un modèle de langue pour une origine en prenant en entrée des k-grammes
|
||||
def make_count_dict(kgrams_origin):
|
||||
kgram_count_dict = {}
|
||||
for kgram_name in kgrams_origin:
|
||||
|
@ -100,10 +104,6 @@ def train_models():
|
|||
#
|
||||
# Vous pouvez ajouter au fichier toutes les fonctions que vous jugerez nécessaire.
|
||||
# Merci de ne pas modifier les fonctions présentes dans ce fichier.
|
||||
#
|
||||
# À compléter - Fonction pour la construction des modèles unigrammes, bigrammes et trigrammes.
|
||||
#
|
||||
# Votre code à partir d'ici...
|
||||
|
||||
# Construire un tableau de fréquence par langue. Chaque tableau est une entrée dans un dictionnaire
|
||||
for origin in names_by_origin.keys():
|
||||
|
@ -121,7 +121,6 @@ def train_models():
|
|||
def most_probable_origin(name, n=3, use_logprob=True):
|
||||
# Retourne la langue d'origine la plus probable du nom.
|
||||
# n désigne la longueur des N-grammes. Par ex n=3 --> trigramme
|
||||
# À compléter...
|
||||
|
||||
# Calculer les log-prob et les perplexités
|
||||
log_prob_origin = {}
|
||||
|
@ -143,25 +142,24 @@ def most_probable_origin(name, n=3, use_logprob=True):
|
|||
def logprob(name, origin, n=3):
|
||||
# Retourne la valeur du logprob d'un nom étant donné une origine
|
||||
# Utilisez une fonction logarithme en base 2.
|
||||
# À compléter...
|
||||
|
||||
unigram_name = make_unigram(name)
|
||||
kgram_name = make_kgram(unigram_name, n)
|
||||
k_counts = kgram_models[(origin, n)]
|
||||
logprob_name = 0
|
||||
if n == 1:
|
||||
N = sum(k_counts.values())
|
||||
prob_n = sum(k_counts.values())
|
||||
for kgram in kgram_name:
|
||||
C = k_counts.get(kgram, 0)
|
||||
L = math.log((C + 1) / (N + V))
|
||||
logprob_name = logprob_name + L
|
||||
prob_c = k_counts.get(kgram, 0)
|
||||
prob_l = math.log((prob_c + 1) / (prob_n + v))
|
||||
logprob_name = logprob_name + prob_l
|
||||
else:
|
||||
k1_counts = kgram_models[(origin, n - 1)]
|
||||
for kgram in kgram_name:
|
||||
N = k1_counts.get(kgram[:-1], 0)
|
||||
C = k_counts.get(kgram, 0)
|
||||
L = math.log((C + 1) / (N + V))
|
||||
logprob_name = logprob_name + L
|
||||
prob_n = k1_counts.get(kgram[:-1], 0)
|
||||
prob_c = k_counts.get(kgram, 0)
|
||||
prob_l = math.log((prob_c + 1) / (prob_n + v))
|
||||
logprob_name = logprob_name + prob_l
|
||||
return logprob_name
|
||||
|
||||
|
||||
|
@ -172,26 +170,24 @@ def prod(iterable):
|
|||
def perplexity(name, origin, n=3):
|
||||
# Retourne la valeur de perplexité d'un nom étant donné une origine
|
||||
# À compléter...
|
||||
|
||||
unigram_name = make_unigram(name)
|
||||
kgram_name = make_kgram(unigram_name, n)
|
||||
length_name = len(kgram_name)
|
||||
k_counts = kgram_models[(origin, n)]
|
||||
V = 26
|
||||
probs_name = []
|
||||
if n == 1:
|
||||
N = sum(k_counts.values())
|
||||
prob_n = sum(k_counts.values())
|
||||
for kgram in kgram_name:
|
||||
C = k_counts.get(kgram, 0)
|
||||
P = (C + 1) / (N + V)
|
||||
probs_name.append(P)
|
||||
prob_c = k_counts.get(kgram, 0)
|
||||
prob_p = (prob_c + 1) / (prob_n + v)
|
||||
probs_name.append(prob_p)
|
||||
else:
|
||||
k1_counts = kgram_models[(origin, n - 1)]
|
||||
for kgram in kgram_name:
|
||||
N = k1_counts.get(kgram[:-1], 0)
|
||||
C = k_counts.get(kgram, 0)
|
||||
P = (C + 1) / (N + V)
|
||||
probs_name.append(P)
|
||||
prob_n = k1_counts.get(kgram[:-1], 0)
|
||||
prob_c = k_counts.get(kgram, 0)
|
||||
prob_p = (prob_c + 1) / (prob_n + v)
|
||||
probs_name.append(prob_p)
|
||||
|
||||
perp = math.exp(-sum([math.log(x) for x in probs_name]) / length_name)
|
||||
|
||||
|
@ -229,14 +225,14 @@ def evaluate_models(filename, n=3, use_logprob=True):
|
|||
|
||||
|
||||
def compute_v():
|
||||
global V
|
||||
global v
|
||||
vocab = {}
|
||||
for origin in all_origins:
|
||||
modele_courant = kgram_models[(origin, 1)]
|
||||
for k in modele_courant.keys():
|
||||
vocab[k] = vocab.get(k, 0) + modele_courant.get(k, 0)
|
||||
V = len(vocab.keys()) + 1
|
||||
print("la valeur de V est "+str(V))
|
||||
v = len(vocab.keys()) + 1
|
||||
print("la valeur de V est " + str(v))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -261,22 +257,25 @@ if __name__ == '__main__':
|
|||
# print("\nLes données pour tester vos modèles sont:")
|
||||
# for org, name_list in test_names.items():
|
||||
# print("\t", org, name_list)
|
||||
for n in range(1, 4):
|
||||
res_logprob, succ_logprob = evaluate_models(test_filename, n, True)
|
||||
res_perp, succ_perp = evaluate_models(test_filename, n, False)
|
||||
for current_n in range(1, 4):
|
||||
res_logprob, succes_logprob = evaluate_models(test_filename, current_n, True)
|
||||
res_perp, succes_perp = evaluate_models(test_filename, current_n, False)
|
||||
|
||||
print(res_logprob)
|
||||
|
||||
# Résultats pour le rapport
|
||||
|
||||
df_succ_logprob = pd.DataFrame.from_dict(succ_logprob).fillna(0).transpose()
|
||||
df_succ_logprob.columns = ['echec_logprob', 'succes_logprob']
|
||||
df_succ_perp = pd.DataFrame.from_dict(succ_perp).fillna(0).transpose()
|
||||
df_succ_perp.columns = ['echec_perp', 'succes_perp']
|
||||
df_succ = df_succ_logprob.join(df_succ_perp)
|
||||
df_succes_logprob = pd.DataFrame.from_dict(succes_logprob).fillna(0).transpose()
|
||||
df_succes_logprob.columns = ['echec_logprob', 'succes_logprob']
|
||||
df_succes_perp = pd.DataFrame.from_dict(succes_perp).fillna(0).transpose()
|
||||
df_succes_perp.columns = ['echec_perp', 'succes_perp']
|
||||
df_succ = df_succes_logprob.join(df_succes_perp)
|
||||
|
||||
f, ax = plt.subplots(figsize=(9, 9))
|
||||
hm_echec = sn.heatmap(df_succ, annot=True, fmt="d", linewidths=.5, ax=ax, cmap='Oranges')
|
||||
hm_echec.set(title="Performance de modèles "+{1:"uni",2:"bi",3:"tri"}[n]+"grammes \nsur les données de test")
|
||||
hm_echec.set(
|
||||
title="Performance de modèles " +
|
||||
{1: "uni", 2: "bi", 3: "tri"}[current_n] +
|
||||
"grammes \nsur les données de test")
|
||||
plt.tight_layout()
|
||||
hm_echec.get_figure().savefig("images/2_seaborn_hm_echec_"+str(n)+".png")
|
||||
hm_echec.get_figure().savefig("images/2_seaborn_hm_echec_" + str(current_n) + ".png")
|
||||
|
|
|
@ -14,6 +14,7 @@ from sklearn.utils.multiclass import unique_labels
|
|||
training_questions_fn = "./data/questions-t3.txt"
|
||||
test_questions_fn = "./data/test-questions-t3.txt"
|
||||
|
||||
|
||||
# Cette fonction provient de https://stackoverflow.com/a/7716358
|
||||
def mean(numbers):
|
||||
return float(sum(numbers)) / max(len(numbers), 1)
|
||||
|
@ -25,6 +26,7 @@ def run_question_classification(training_fn, test_fn):
|
|||
print("Accuracy on test set: {0:.4f}".format(accuracy_test))
|
||||
|
||||
|
||||
# Fonction de décompte personnalisée avec les trois paramètres optimisés
|
||||
def custom_count_vectorize_questions(min_df, max_df, use_stop_words):
|
||||
stop_words = [None, 'english'][use_stop_words]
|
||||
count_vec = CountVectorizer(analyzer='word',
|
||||
|
@ -42,9 +44,7 @@ def train_and_test_classifier(training_fn, test_fn):
|
|||
# test_questions, test_labels = load_dataset(test_questions_fn)
|
||||
print("Nb questions de test:", len(test_questions))
|
||||
|
||||
# Insérer ici votre code pour la classification des questions.
|
||||
# Votre code...
|
||||
|
||||
# Multinomial Naive Bayes
|
||||
mnb = MultinomialNB()
|
||||
|
||||
# Sélection des meilleurs paramètres pour le CountVectorizer
|
||||
|
@ -74,18 +74,22 @@ def train_and_test_classifier(training_fn, test_fn):
|
|||
count_questions_train_opt = count_vec_opt.fit_transform(questions)
|
||||
count_questions_test_opt = count_vec_opt.transform(test_questions)
|
||||
|
||||
# Optimisation de l'hyperparamètre alpha
|
||||
|
||||
print("\nRecherche de l'hyperparamètre alpha:")
|
||||
|
||||
gvc = GridSearchCV(mnb, param_grid={'alpha': [x / 10.0 for x in range(1, 21)]}, cv=5)
|
||||
|
||||
mnb_fit = gvc.fit(count_questions_train_opt, labels)
|
||||
|
||||
print("Score: {0:.4f}".format(str(mnb_fit.best_score_)))
|
||||
print("Score: {0:.4f}".format(mnb_fit.best_score_))
|
||||
|
||||
print("Paramètres: " + str(mnb_fit.get_params()))
|
||||
|
||||
u_labels = unique_labels(labels, test_labels)
|
||||
|
||||
# Calcul des prédictions et de la performance
|
||||
|
||||
labels_predict_train = mnb_fit.predict(count_questions_train_opt)
|
||||
labels_predict_test = mnb_fit.predict(count_questions_test_opt)
|
||||
|
||||
|
@ -112,7 +116,8 @@ def train_and_test_classifier(training_fn, test_fn):
|
|||
f, ax4 = plt.subplots(figsize=(9, 9))
|
||||
sn.reset_defaults()
|
||||
sn.set(style="darkgrid")
|
||||
sn_plot_count_category_test = sn.countplot(y='category', data=df_count_test_labels, palette="deep", ax=ax4, order=u_labels)
|
||||
sn_plot_count_category_test = sn.countplot(y='category', data=df_count_test_labels, palette="deep", ax=ax4,
|
||||
order=u_labels)
|
||||
sn_plot_count_category_test.set(title="Fréquence des catégories de questions\ndans l'échantillon de test",
|
||||
xlabel="Fréquence",
|
||||
ylabel="Catégorie")
|
||||
|
|
Loading…
Add table
Reference in a new issue