From 049daf8d16cb48ddc16a1de33a5dacd6fe78ff8f Mon Sep 17 00:00:00 2001 From: kariminf Date: Fri, 22 Apr 2022 02:20:41 +0100 Subject: [PATCH] TP 04 analyse CKY --- TP/CH05/CKY/TALN_TP04_syntaxe.tex | 128 +++++++++++ TP/CH05/CKY/commonJS/syntaxe.js | 243 ++++++++++++++++++++ TP/CH05/CKY/data/gram1.txt | 50 ++++ TP/CH05/CKY/data/test1.txt | 9 + TP/CH05/CKY/java/Syntaxe.java | 364 ++++++++++++++++++++++++++++++ TP/CH05/CKY/python/syntaxe.py | 212 +++++++++++++++++ cours/app.tex | 24 +- cours/basique.tex | 4 +- cours/coherence.tex | 8 +- cours/coref.tex | 2 +- cours/pos.tex | 8 +- cours/sens_mots.tex | 29 ++- cours/sens_phrases.tex | 2 +- cours/syntaxe.tex | 32 +-- img/app/app-mt01.odg | Bin 0 -> 13518 bytes pres/TALN05C-syntaxe.tex | 54 ++--- 16 files changed, 1102 insertions(+), 67 deletions(-) create mode 100644 TP/CH05/CKY/TALN_TP04_syntaxe.tex create mode 100644 TP/CH05/CKY/commonJS/syntaxe.js create mode 100644 TP/CH05/CKY/data/gram1.txt create mode 100644 TP/CH05/CKY/data/test1.txt create mode 100644 TP/CH05/CKY/java/Syntaxe.java create mode 100644 TP/CH05/CKY/python/syntaxe.py create mode 100644 img/app/app-mt01.odg diff --git a/TP/CH05/CKY/TALN_TP04_syntaxe.tex b/TP/CH05/CKY/TALN_TP04_syntaxe.tex new file mode 100644 index 0000000..27f28b5 --- /dev/null +++ b/TP/CH05/CKY/TALN_TP04_syntaxe.tex @@ -0,0 +1,128 @@ +% !TEX TS-program = pdflatex +% !TeX program = pdflatex +% !TEX encoding = UTF-8 +% !TEX spellcheck = fr + +\documentclass[11pt, a4paper]{article} +%\usepackage{fullpage} +\usepackage[left=1cm,right=1cm,top=1cm,bottom=2cm]{geometry} +\usepackage[fleqn]{amsmath} +\usepackage{amssymb} +%\usepackage{indentfirst} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[french,english]{babel} +\usepackage{txfonts} +\usepackage[]{graphicx} +\usepackage{multirow} +\usepackage{hyperref} +\usepackage{parskip} +\usepackage{multicol} +\usepackage{wrapfig} + +\usepackage{turnstile}%Induction symbole + +\usepackage{tikz} +\usetikzlibrary{arrows, automata} +\usetikzlibrary{decorations.pathmorphing} + +\renewcommand{\baselinestretch}{1} + +\setlength{\parindent}{24pt} + + +\begin{document} + +\selectlanguage {french} +%\pagestyle{empty} + +\noindent +\begin{tabular}{ll} +\multirow{3}{*}{\includegraphics[width=2cm]{../../../img/esi-logo.png}} & \'Ecole national Supérieure d'Informatique\\ +& 2\textsuperscript{ème} année cycle supérieure (2CSSID)\\ +& Traitement automatique du langage naturel (2021-2022) +\end{tabular}\\[.25cm] +\noindent\rule{\textwidth}{1pt}\\%[-0.25cm] +\begin{center} +{\LARGE \textbf{TP04 : Analyse syntaxique (CKY)}} +\begin{flushright} + ARIES Abdelkrime +\end{flushright} +\end{center} +\noindent\rule{\textwidth}{1pt} + +Afin de bien comprendre l'analyse syntaxique, on veut implémenter l'algorithme CKY de zéro ainsi que l'algorithme d'évaluation naïve. +Il faut savoir que la structure de l'arbre varie selon le langage de programmation utilisé. +Donc, il faut lire le code pour comprendre la structure ; sinon, demander d'explication pendant la séance du TP. + +\section*{1. Implémentation} + +L'algorithme est implémenté dans sa totalité sauf les fonctions à compléter. +I existe deux classes : +\begin{itemize} + \item CKY : créée en passant la grammaire en forme normale de Chomsky (FNC) et la lexique (un mot et la liste des catégories grammaticales). + Elle applique l'analyse sur un phrase passée comme liste de mots. + \item Syntaxe : Elle utilise la classe CKY pour faire l'analyse syntaxique. + Elle génère un arbre syntaxique graphique (basé sur le langage Dot). + Aussi, elle évalue notre modèle d'analyse syntaxique. +\end{itemize} + +Dans ce TP, vous devez implémenter la méthode "analyser" vue en cours. +Aussi, vous devez implémenter la méthode de comparaison entre deux arbres. + +\subsection*{1.1. analyser} + +La méthode prend en entrée une liste des mots. +Elle doit utiliser la grammaire en FNC et la lexique qui sont introduits dans le constructeur. +A la fin, elle doit retourner la matrice d'analyse vue en cours : +\begin{itemize} + \item une liste des listes : pour représenter les lignes et les colonnes + \item le contenu est une liste : pour représenter les différentes règles possibles + \item le contenu est un tuple (A, k, iB, iC) : A est la variable, k est utilisé pour calculer la position de B (avec iB) et de C (avec iC) de la règle $A \leftarrow B C$. +\end{itemize} + +\subsection*{1.2. evaluer\_stricte} + +Cette fonction prend un arbre de référence (qui est un nœud) et un arbre généré par le système. +Elle compare les deux structures et rend 1 s'elles sont équivalentes, sinon 0. + +Remarque : si l'un des deux arbres existe et l'autre non, la fonction devrait avoir 0 comme résultat. +Si les deux n'existent pas, la fonction devait rendre 1. + +%\section*{2. Expérimentation} +% +%Ici, on va décrire comment on va évaluer un tel modèle. +% +%\subsection*{2.1. Dataset} +% +%Nous avons utilisé Penn treebank, mais les étiquettes ont été transformées vers universal dependencies. +% +%\subsection*{2.1. Évaluation du modèle} +% +%Pour chaque phrase, on calcule l'exactitude (accuracy). +%Le résultat est la moyenne des exactitudes. +% +\section*{2. Questions} + +Il faut rendre des réponses de ces questions sous forme d'un fichier texte (.txt) avec le code. + +\begin{enumerate} + \item On peut remarquer que l'évaluation stricte ne prend pas en considération les arbres syntaxiques qui sont proches de celles de références. Elle les considère comme erronées. Proposer (sans implémentation) une méthode pour affecter un score entre 0 et 1 selon la ressemblance entre l'arbre générée par le système et celle de référence. +\end{enumerate} + +\newpage +\section*{3. Procédure d'évaluation} + +Ici, on va discuter l'évaluation du TP. + +\begin{itemize} + \item Durée : 1h (il faut rendre le TP à la fin de la séance) + \item Note = Note\_analyser + Note\_evaluer\_stricte + Note\_questions + \begin{itemize} + \item \textbf{Note\_analyser} (10pts) + \item \textbf{Note\_evaluer\_stricte} (8pts), la méthode doit pouvoir rendre le résultat au plus tôt possible. + \item \textbf{Note\_questions} (2pts) + \end{itemize} +\end{itemize} + +\end{document} diff --git a/TP/CH05/CKY/commonJS/syntaxe.js b/TP/CH05/CKY/commonJS/syntaxe.js new file mode 100644 index 0000000..7eb2518 --- /dev/null +++ b/TP/CH05/CKY/commonJS/syntaxe.js @@ -0,0 +1,243 @@ +#!/usr/bin/env node + +const { log } = require('console'); +const fs = require('fs'); +const path = require('path'); +const _ = require('underscore'); + + +function formater_liste(l){ + if (l == null) return 'None'; + let resultat = '('; + for (const e of l){ + resultat += (e instanceof Array)? formater_liste(e): "'" + e.toString() + "'"; + resultat += ', '; + } + resultat = resultat.slice(0, -2); + resultat += ')'; + return resultat; +} + + + +// ====================================================== +// Analyse CKY +// ====================================================== + +class CKY { + constructor(gram, lex){ + // + this.gram = gram; + // + this.lex = lex; + } + + // TODO: Compléter cette méthode + analyser(phrase){ + const T = [], + N = phrase.length; + let i,j,k,pos; + for(i=0; i=0){ + const gauche = construire(T, phrase, i, k, iB); + const droit = construire(T, phrase, k+1, j, iC); + return [A, gauche, droit]; + } + return [A, phrase[i]]; +} + +function parse_tuple(string){ + if (! string.startsWith('(')) return null; + return eval(string.replace(/([^\s(),]+)/g, "'$1'").replace(/\(/g, '[').replace(/\)/g, ']')); +} + +class Syntaxe { + constructor(){ + this.eval = []; + } + + _analyser(phrase){ + const T = this.modele.analyser(phrase), + n = phrase.length - 1; + let pos; + + for (pos=0; pos 4) && (!l.startsWith('#'))){ + const info = l.split(' '); + if (info.length == 2){ + if (! (info[1] in lex)) lex[info[1]] = []; + lex[info[1]].push(info[0]); + } else if (info.length==3){ + gram.push([info[0], info[1], info[2]]); + } + } + }); + this.modele = new CKY(gram, lex); + } + + sauvegarder_modele(url){ + fs.writeFileSync(url, this.modele.exporter_json()); + } + + charger_evaluation(url){ + const f = fs.readFileSync(url, 'utf8'); + let i; + f.split(/\r?\n/).forEach(function(l){ + l = l.trim(); + if ((l.length > 4) && (!l.startsWith('#'))){ + const info = l.split(' '); + this.eval.push([info[0], parse_tuple(info[1])]); + } + }, this); + } + + evaluer(n){ + const S = (n == -1)? this.eval: _.sample(this.eval, n); + let score = 0.0; + S.forEach(function(test){ + console.log('======================='); + console.log('phrase :', test[0]); + console.log('arbre de référence :', formater_liste(test[1])); + const arbre = this.analyser(test[0]); + console.log('arbre du système :', formater_liste(arbre)); + const score_i = evaluer_stricte(test[1], arbre); + console.log('score :', score_i); + score += score_i; + }, this); + if (n == -1){ n = this.eval.length;} + score = score/n; + console.log('---------------------------------'); + console.log('score total : ', score); + return score; + } +} + +// ====================================================== +// Génération du graphique (langage Dot) +// ====================================================== + +function generer_noeud(noeud){ + // Si le noeud est final, + let id = -1; + let pid = 0; + let pile = []; + let n = noeud; + let res = ''; + while(pile.length > 0 || n != null){ + id += 1; + if (n==null){ + [n, pid] = pile.pop(); + } else if (n.length < 3){ + res += 'N' + id +'[label="' + n[0] + "=" + n[1] + '" shape=box];\n'; + if (pid < id) res += 'N' + pid + ' -> N' + id + ';\n'; + n = null; + } else { + res += 'N' + id + '[label="' + n[0] + '"];\n'; + if (pid < id) res += 'N' + pid + ' -> N' + id + ';\n'; + //ce noeud sera un parent + pid = id; + //empiler la droite + pile.push([n[2], pid]); + //aller toujours vers la gauche + n = n[1]; + } + } + return res; +} + +function generer_graphviz(racine, url){ + let res = 'digraph Tree {\n'; + res += generer_noeud(racine); + res += '}'; + fs.writeFileSync(url, res); +} + +// ====================================================== +// TESTES +// ====================================================== + +// le test du cours pour tester l'analyseur CKY +function tester_cky(){ + const parser = new Syntaxe(); + parser.charger_modele('data/gram1.txt'); + const phrase = "la petite forme une petite phrase"; + const resultat = "('S', ('NP', ('DET', 'la'), ('N', 'petite')), ('VP', ('V', 'forme'), ('NP', ('DET', 'une'), ('AP', ('ADJ', 'petite'), ('N', 'phrase')))))"; + const arbre = parser.analyser(phrase); + console.log('résultat attendu : ', resultat); + console.log('mon résultat : ', formater_liste(arbre)); + generer_graphviz(arbre, 'arbre_js.gv') +} + +// tester l'évaluation stricte +function tester_evaluer_arbre(){ + console.log('résultat attendu : 0, résultat trouvé : ', evaluer_stricte(null, ['A', 'd'])); + console.log('résultat attendu : 1, résultat trouvé : ', evaluer_stricte(null, null)); + console.log('résultat attendu : 0, résultat trouvé : ', evaluer_stricte(['A', 'a'], ['A', ['B', 'b'], ['C', 'c']])); + console.log('résultat attendu : 0, résultat trouvé : ', evaluer_stricte(['A', ['B', 'b'], ['C', 'c']], ['A', 'a'])); + console.log('résultat attendu : 0, résultat trouvé : ', evaluer_stricte(['A', ['B', 'b'], ['C', 'c']], ['A', ['B', 'd'], ['C', 'c']])); + console.log('résultat attendu : 1, résultat trouvé : ', evaluer_stricte(['A', ['B', 'b'], ['C', 'c']], ['A', ['B', 'b'], ['C', 'c']])); +} + +// tester l'évaluation stricte +// Le résultat doit être 1 +function tester_evaluation(){ + const parser = new Syntaxe(); + parser.charger_modele('data/gram1.txt'); + parser.charger_evaluation('data/test1.txt'); + parser.evaluer(-1); +} + + + +// MAIN + +tester_cky(); +// tester_evaluer_arbre(); +// tester_evaluation(); diff --git a/TP/CH05/CKY/data/gram1.txt b/TP/CH05/CKY/data/gram1.txt new file mode 100644 index 0000000..bd92187 --- /dev/null +++ b/TP/CH05/CKY/data/gram1.txt @@ -0,0 +1,50 @@ +# grammaire en FNC: A \t B \t C ( A --> B C ) +# =========================================== + +S NP VP +S VINF NP +S PRON VP +S VINF PRON +S DET N +VP V NP +VP V PRON +VP MODE NP +VP VMODE VINF +NP DET AP +NP DET N +AP ADJ N +AP ADJ AP +MODE VMODE VINF + + +# lexique : A \t mot ( A --> mot ) +# ================================= + +PRON je +PRON tu +PRON il +PRON elle +V forme +V veut +V veux +V mange +V est +VINF manger +VINF nager +VMODE veut +VMODE veux +VMODE peut +VMODE peux +DET un +DET une +DET la +DET le +ADJ petite +ADJ petit +ADJ grand +ADJ bleu +N petite +N forme +N phrase +N chat +N poisson diff --git a/TP/CH05/CKY/data/test1.txt b/TP/CH05/CKY/data/test1.txt new file mode 100644 index 0000000..ab744b5 --- /dev/null +++ b/TP/CH05/CKY/data/test1.txt @@ -0,0 +1,9 @@ +le grand chat veut manger le petit poisson (S, (NP, (DET, le), (AP, (ADJ, grand), (N, chat))), (VP, (MODE, (VMODE, veut), (VINF, manger)), (NP, (DET, le), (AP, (ADJ, petit), (N, poisson))))) +le poisson poisson poisson RIEN +je veux manger un grand poisson (S, (PRON, je), (VP, (MODE, (VMODE, veux), (VINF, manger)), (NP, (DET, un), (AP, (ADJ, grand), (N, poisson))))) +il mange un petit poisson bleu RIEN +elle mange un petit poisson (S, (PRON, elle), (VP, (V, mange), (NP, (DET, un), (AP, (ADJ, petit), (N, poisson))))) +tu forme une petite phrase (S, (PRON, tu), (VP, (V, forme), (NP, (DET, une), (AP, (ADJ, petite), (N, phrase))))) +je forme une forme (S, (PRON, je), (VP, (V, forme), (NP, (DET, une), (N, forme)))) +la phrase est une mange un poisson RIEN +un petit poisson veut nager (S, (NP, (DET, un), (AP, (ADJ, petit), (N, poisson))), (VP, (VMODE, veut), (VINF, nager))) diff --git a/TP/CH05/CKY/java/Syntaxe.java b/TP/CH05/CKY/java/Syntaxe.java new file mode 100644 index 0000000..f351e08 --- /dev/null +++ b/TP/CH05/CKY/java/Syntaxe.java @@ -0,0 +1,364 @@ +import java.util.regex.*; +import java.io.*; +import java.util.*; +import java.lang.Math; +import java.util.stream.*; + +public class Syntaxe { + private CKY modele; + private List> eval = new ArrayList<>(); + + public Noeud analyser(String[] phrase){ + List>> T = this.modele.analyser(phrase); + int N = phrase.length - 1; + List lastCell = T.get(0).get(N); + for(int pos=0; pos> lex = new HashMap<>(); + List gram = new ArrayList<>(); + while((l = f.readLine()) != null) { + l = l.strip(); + if ((l.length() < 5)) continue; + String[] info = l.split(" "); + if (info.length == 2){ + if (! lex.containsKey(info[1])) lex.put(info[1], new ArrayList()); + lex.get(info[1]).add(info[0]); + } else if (info.length==3){ + gram.add(new Regle(info[0], info[1], info[2])); + } + } + f.close(); + this.modele = new CKY(gram, lex); + } catch(IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + + public void charger_evaluation(String url){ + try { + BufferedReader f = new BufferedReader(new FileReader(url)); + String l; + while((l = f.readLine()) != null) { + l = l.strip(); + if ((l.length() < 5)) continue; + String[] info = l.split(" "); + this.eval.add(new Tuple(info[0], Outils.parse_tuple(info[1]))); + } + f.close(); + } catch(IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + public void evaluer(int n){ + if (n==-1) n = this.eval.size(); + else Collections.shuffle(this.eval); + + double score = 0.0; + for (int i=0; i test = this.eval.get(i); + System.out.println("======================="); + System.out.println("phrase : " + test.k); + System.out.println("arbre de référence : " + test.v); + Noeud arbre = analyser(test.k); + System.out.println("arbre du système : " + arbre); + int score_i = Outils.evaluer_stricte(test.v, arbre); + System.out.println("score :" + score_i); + score += score_i; + } + + score /= n; + System.out.println("---------------------------------"); + System.out.println("score total : " + score); + } + + + // ====================================================== + // TESTES + // ====================================================== + + public static void tester_cky(){ + Syntaxe parser = new Syntaxe(); + parser.charger_modele("data/gram1.txt"); + String phrase = "la petite forme une petite phrase"; + Noeud arbre = parser.analyser(phrase); + String resultat = "('S', ('NP', ('DET', 'la'), ('N', 'petite')), ('VP', ('V', 'forme'), ('NP', ('DET', 'une'), ('AP', ('ADJ', 'petite'), ('N', 'phrase')))))"; + arbre = parser.analyser(phrase); + System.out.println("résultat attendu : " + resultat); + System.out.println("mon résultat : " + arbre); + generer_graphviz(arbre, "arbre_java.gv"); + } + + public static void tester_evaluer_arbre(){ + List resultat = new ArrayList<>(); + List test1 = new ArrayList<>(); + List test2 = new ArrayList<>(); + + test1.add(null); + test2.add(new Noeud("A", new Noeud("d", null, null), null)); + resultat.add(0); + + test1.add(null); + test2.add(null); + resultat.add(1); + + test1.add(new Noeud("A", new Noeud("a", null, null), null)); + test2.add(new Noeud("A", new Noeud("B", new Noeud("b", null, null), null), new Noeud("C", new Noeud("c", null, null), null))); + resultat.add(0); + + test1.add(new Noeud("A", new Noeud("B", new Noeud("b", null, null), null), new Noeud("C", new Noeud("c", null, null), null))); + test2.add(new Noeud("A", new Noeud("a", null, null), null)); + resultat.add(0); + + test1.add(new Noeud("A", new Noeud("B", new Noeud("b", null, null), null), new Noeud("C", new Noeud("c", null, null), null))); + test2.add(new Noeud("A", new Noeud("B", new Noeud("d", null, null), null), new Noeud("C", new Noeud("c", null, null), null))); + resultat.add(0); + + test1.add(new Noeud("A", new Noeud("B", new Noeud("b", null, null), null), new Noeud("C", new Noeud("c", null, null), null))); + test2.add(new Noeud("A", new Noeud("B", new Noeud("b", null, null), null), new Noeud("C", new Noeud("c", null, null), null))); + resultat.add(1); + + for (int i=0; i gram; + private Map> lex; + + public CKY(List gram, Map> lex){ + this.gram = gram; + this.lex = lex; + } + + //TODO: Compléter cette méthode + public List>> analyser(String[] phrase){ + int N = phrase.length; + List>> T = new ArrayList<>(); + for(int i=0; i> Ti = new ArrayList<>(); + T.add(Ti); + for(int j=0; j()); + String mot = phrase[i]; + for (String A : this.lex.get(mot)){ + T.get(i).get(i).add(new CKYRef(A, -1, -1, -1)); + } + } + + // Compléter ici : remplissage + return T; + } + +} + +class Outils { + + //TODO: compléter cette fonction + public static int evaluer_stricte(Noeud ref, Noeud sys){ + return 0; + } + + public static Noeud construire(List>> T, String[] phrase, int i, int j, int pos){ + CKYRef ref = T.get(i).get(j).get(pos); + String A = ref.A.toUpperCase(); + if (ref.k >= 0){ + Noeud gauche = construire(T, phrase, i, ref.k, ref.iB); + Noeud droit = construire(T, phrase, ref.k + 1, j, ref.iC); + return new Noeud(A, gauche, droit); + } + return new Noeud(A, new Noeud(phrase[i], null, null), null); + } + + public static String generer_noeud(Noeud noeud){ + int id = -1; + int pid = 0; + Deque pile = new ArrayDeque<>(); + Noeud n = noeud; + String res = ""; + while(pile.size() > 0 || n != null){ + id += 1; + if (n==null){ + NoeudPere npid = pile.pop(); + n = npid.n; + pid = npid.pid; + } else if (n.gauche == null){ + res += "N" + id +"[label=\"" + n.val + "\" shape=box];\n"; + if (pid < id) res += "N" + pid + " -> N" + id + ";\n"; + n = null; + } else { + res += "N" + id + "[label=\"" + n.val + "\"];\n"; + if (pid < id) res += "N" + pid + " -> N" + id + ";\n"; + //ce noeud sera un parent + pid = id; + //empiler la droite + pile.push(new NoeudPere(n.droit, pid)); + //aller toujours vers la gauche + n = n.gauche; + } + } + return res; + } + + public static void generer_graphviz(Noeud racine, String url){ + String res = "digraph Tree {\n"; + res += generer_noeud(racine); + res += "}"; + try { + BufferedWriter f = new BufferedWriter(new FileWriter(url)); + f.write(res); + f.close(); + } catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + } + + private static int trouver_par_ferme(String string){ + LinkedList pile = new LinkedList<>(); + for(int i = 0;i " + B + " " + C; + } +} + +class Noeud { + public final String val; + public final Noeud gauche; + public final Noeud droit; + public Noeud(String val, Noeud gauche, Noeud droit) { + this.val = val; + this.gauche = gauche; + this.droit = droit; + } + + public int len(){ + if (gauche != null && droit != null) return 3; + if (gauche != null) return 2; + if (gauche == null && droit == null) return 1; + return 0; + } + + public String toString(){ + if (gauche == null && droit == null) return "'" + val + "'"; + + String res = "('" + val + "'"; + res += ", " + gauche; + if (droit != null) res += ", " + droit; + res += ")"; + return res; + } +} + +class NoeudPere { + public final Noeud n; + public final int pid; + public NoeudPere(Noeud n, int pid) { + this.n = n; + this.pid = pid; + } +} + +class Tuple { + public final K k; + public final V v; + public Tuple(K k, V v) { + this.k = k; + this.v = v; + } + + public String toString(){ + return "(" + k + ", " + v + ")"; + } +} \ No newline at end of file diff --git a/TP/CH05/CKY/python/syntaxe.py b/TP/CH05/CKY/python/syntaxe.py new file mode 100644 index 0000000..792da41 --- /dev/null +++ b/TP/CH05/CKY/python/syntaxe.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import re +import random +from typing import Dict, List, Tuple, Set + + +# ====================================================== +# Analyse CKY +# ====================================================== + +class CKY: + def __init__(self, gram: List[Tuple[str, str, str]], lex: Dict[str, List[str]]): + # Grammaire + self.gram = gram # liste((A, B, C)) où A -> B C + self.lex = lex # mot --> liste(PoS) + + # TODO: Compléter cette méthode + def analyser(self, phrase: List[str]) -> List[List[List[Tuple[str, int, int, int]]]]: + T = [] + N = len(phrase) + for i in range(N): + T.append([[] for i in range(N)]) + mot = phrase[i] + for A in self.lex[mot]: # ici, on suppose que le mot existe dans la lexique ; sinon, on aura une erreur + T[i][i].append((A, -1, -1, -1)) # On ajoute toutes les variables possibles + + # Compléter ici : remplissage + + return T + + def exporter_json(self): + return self.__dict__.copy() + + def importer_json(self, data): + for cle in data: + self.__dict__[cle] = data[cle] + +# ====================================================== +# Analyse syntaxique +# ====================================================== + +# TODO: compléter cette fonction +def evaluer_stricte(ref, sys): + return 0 + +def construire(T, phrase, i, j, pos): + A, k, iB, iC = T[i][j][pos] + #A = A.upper() + if k >= 0: + gauche = construire(T, phrase, i, k, iB) + droit = construire(T, phrase, k+1, j, iC) + return (A, gauche, droit) + return (A, phrase[i]) + +def parse_tuple(string): + string = re.sub('([^\s(),]+)', "'\\1'", string) + #print(string) + try: + s = eval(string) + if type(s) == tuple: + return s + return + except: + return + +class Syntaxe(): + def __init__(self): + self.eval = [] + + def _analyser(self, phrase): + r = None + T = self.modele.analyser(phrase) + n = len(phrase) - 1 + # for i in range(n+1): + # for j in range(i, n+1): + # print(i+1, j+1, T[i][j]) + for pos in range(len(T[0][n])): + if T[0][n][pos][0] == 'S': + # print('bingo') + r = construire(T, phrase, 0, n, pos) + break + return r + + def analyser(self, phrase: str): + return self._analyser(phrase.strip().lower().split()) + + def charger_modele(self, url): + f = open(url, 'r') + lex = {} + gram = [] + for l in f: # la lecture ligne par ligne + l = l.strip() + if len(l) < 5 or l.startswith('#') : + continue + info = l.split(' ') + if len(info) == 2: + if not info[1] in lex: + lex[info[1]] = [] + lex[info[1]].append(info[0]) + elif len(info) == 3: + gram.append((info[0], info[1], info[2])) + self.modele = CKY(gram, lex) + f.close() + + def charger_evaluation(self, url): + f = open(url, 'r') + for l in f: # la lecture ligne par ligne + l = l.strip() + if len(l) < 5 or l.startswith('#'): + continue + info = l.split(' ') + + self.eval.append((info[0], parse_tuple(info[1]))) + + def evaluer(self, n): + if n == -1: + S = self.eval + n = len(S) + else : + S = random.sample(self.eval, n) + score = 0.0 + for i in range(n): + test = S[i] + print('=======================') + print('phrase :', test[0]) + print('arbre de référence :', test[1]) + arbre = self.analyser(test[0]) + print('arbre du système :', arbre) + score_i = evaluer_stricte(test[1], arbre) + print('score :', score_i) + score += score_i + + score = score/n + print('---------------------------------') + print('score total : ', score) + + + +# ====================================================== +# Génération du graphique (langage Dot) +# ====================================================== + +def generer_noeud(noeud, id=0): + # Si le noeud n'existe pas + if noeud is None: + return 0, '' + # Si le noeud est final, + nid = id + 1 + if len(noeud) < 3: + return nid, 'N' + str(id) + '[label="' + noeud[0] + "=" + noeud[1] + '" shape=box];\n' + # Sinon, + # s'il y a des fils, on boucle sur les fils et on imprime des SI ... ALORS + res = 'N' + str(id) + '[label="' + noeud[0] + '"];\n' + nid_g = nid + nid, code = generer_noeud(noeud[1], id=nid_g) + res += code + res += 'N' + str(id) + ' -> N' + str(nid_g) + ';\n' + nid_d = nid + nid, code = generer_noeud(noeud[2], id=nid_d) + res += code + res += 'N' + str(id) + ' -> N' + str(nid_d) + ';\n' + return nid, res + +def generer_graphviz(racine, url): + res = 'digraph Tree {\n' + id, code = generer_noeud(racine) + res += code + res += '}' + f = open(url, 'w') + f.write(res) + f.close() + + +# ====================================================== +# TESTES +# ====================================================== + +# le test du cours pour tester l'analyseur CKY +def tester_cky(): + parser = Syntaxe() + parser.charger_modele('data/gram1.txt') + phrase = 'la petite forme une petite phrase' + resultat = "('S', ('NP', ('DET', 'la'), ('N', 'petite')), ('VP', ('V', 'forme'), ('NP', ('DET', 'une'), ('AP', ('ADJ', 'petite'), ('N', 'phrase')))))" + arbre = parser.analyser(phrase) + print('résultat attendu : ', resultat) + print('mon résultat : ', arbre) + generer_graphviz(arbre, 'arbre_python.gv') + +# tester l'évaluation stricte +def tester_evaluer_arbre(): + print('résultat attendu : 0, résultat trouvé : ', evaluer_stricte(None, ('A', 'd'))) + print('résultat attendu : 1, résultat trouvé : ', evaluer_stricte(None, None)) + print('résultat attendu : 0, résultat trouvé : ', evaluer_stricte(('A', 'a'), ('A', ('B', 'b'), ('C', 'c')))) + print('résultat attendu : 0, résultat trouvé : ', evaluer_stricte(('A', ('B', 'b'), ('C', 'c')), ('A', 'a'))) + print('résultat attendu : 0, résultat trouvé : ', evaluer_stricte(('A', ('B', 'b'), ('C', 'c')), ('A', ('B', 'd'), ('C', 'c')))) + print('résultat attendu : 1, résultat trouvé : ', evaluer_stricte(('A', ('B', 'b'), ('C', 'c')), ('A', ('B', 'b'), ('C', 'c')))) + +# tester l'évaluation stricte +# Le résultat doit être 1 +def tester_evaluation(): + parser = Syntaxe() + parser.charger_modele('data/gram1.txt') + parser.charger_evaluation('data/test1.txt') + parser.evaluer(-1) + + +if __name__ == '__main__': + tester_cky() + #tester_evaluer_arbre() + #tester_evaluation() diff --git a/cours/app.tex b/cours/app.tex index 97400e6..eb99f0c 100644 --- a/cours/app.tex +++ b/cours/app.tex @@ -1336,8 +1336,8 @@ \section{Synthèse de la parole} \label{fig:tts-arch} \end{figure} - -\begin{discussion} +\sectioni{Discussion} +%\begin{discussion} Quelle est l'intérêt d'étudier un domaine sans applications ? Le traitement automatique du langage naturel est un domaine qui vise à émuler la capacité des être humains à communiquer soit par parole ou par texte. Dans le passé, ce domaine a été motivé seulement par la tâche de traduction automatique (surtout entre russe et anglais). @@ -1361,14 +1361,28 @@ \section{Synthèse de la parole} La première application prend une parole en entrée et la transforme à un texte, et la deuxième fait l'opération inverse. Il existe plusieurs applications qui sont implémentées en utilisant plusieurs approches et méthodes. Ce chapitre ne peut pas présenter le tout ; c'est juste une goutte dans l'océan. -\end{discussion} +%\end{discussion} -\sectioni{Ressources complémentaires} +\sectioni{Ressources supplémentaires} \subsubsection*{Exercices} \begin{enumerate} - \item ... + \item On veut développer un système de traduction automatique français-anglais. Nous avons opté pour une solution basée sur l'approche directe. Dans cette approche, on traduit mot par mot et on applique une phase de post-traitement afin d'ajuster l'ordre des mots, etc. Cette approche se situe dans le niveau morphologique du traitement de la langue. Proposer une solution afin d'introduire le niveau sémantique sans changer d'approche. + + \item On veut développer un système de résumé automatique translingue (Texte long en une langue et le résumé généré en une autre langue). Pour ce faire, nous avons utilisé deux systèmes basés sur l'architecture encodeur-décodeur (seq2seq avec des RNNs) : un système de résumé automatique de la langue française et un autre de traduction automatique du français vers l'anglais. + \begin{center} + \hgraphpage[.5\textwidth]{app-mt01.pdf} + \end{center} + \begin{enumerate} + \item Nous avons deux datasets : un premier qui contient des phrases alignées (français-anglais) et un deuxième qui contient des textes en français et leurs résumés en anglais. On peut entraîner le deuxième système (traduction) en utilisant le premier dataset. Comment peut-on entraîner le système de résumé automatique (français-français) ? + \item Proposer une nouvelle architecture qui nous assure ces améliorations : + \begin{itemize} + \item Moins de consommation de temps, d'effort et de données lors d'entraînement ; + \item Prendre en considération des dépendances à-postériori lors de la génération du contexte ; + \item Prendre en considération des variations morphologiques d'une manière automatique. + \end{itemize} + \end{enumerate} \end{enumerate} diff --git a/cours/basique.tex b/cours/basique.tex index ed1f1e6..049cd3a 100644 --- a/cours/basique.tex +++ b/cours/basique.tex @@ -680,7 +680,7 @@ \subsection{Réduction des formes} \KwResult{liste des lemmes possibles} \Si{mot $ \in $ list\_exceptions[catégorie]}{ - \Return chercher\_dans\_dictionnaire(\{mot\} $ \cup $ list\_exceptions[catégorie])\; + \Return chercher\_dans\_dictionnaire(\{mot\} $ \cup $ list\_exceptions[catégorie][mot])\; } formes = \{mot\} @@ -728,7 +728,7 @@ \subsection{Réduction des formes} %\end{discussion} -\sectioni{Ressources supplémentaire} +\sectioni{Ressources supplémentaires} %\begin{ressources} \subsubsection*{Exercices} diff --git a/cours/coherence.tex b/cours/coherence.tex index e4aee9e..3e6559e 100644 --- a/cours/coherence.tex +++ b/cours/coherence.tex @@ -674,8 +674,8 @@ \subsection{Entity Grid model} \item Reconstruction de l'ordre des phrases : apprendre à ordonner les phrases \end{itemize} - -\begin{discussion} +\sectioni{Discussion} +%\begin{discussion} Une phrase doit être bien formée ; ceci est garanti par la grammaire. Elle doit, aussi, avoir un sens ; ceci est garanti par la sémantique. Lorsque nous fusionnons plusieurs phrases qui ont un sens, ce n'est pas une garantie que ce texte soit compréhensible ou au moins naturel. @@ -687,9 +687,9 @@ \subsection{Entity Grid model} Les deux approches ont deux visions différentes ; qui sont complémentaires à mon avis. Afin de juger la cohérence d'un texte, il vaut mieux utiliser les deux afin de vérifier deux aspects différents. -\end{discussion} +%\end{discussion} -\sectioni{Ressources complémentaires} +\sectioni{Ressources supplémentaires} \subsubsection*{Exercices} diff --git a/cours/coref.tex b/cours/coref.tex index b9a14dc..22fbd25 100644 --- a/cours/coref.tex +++ b/cours/coref.tex @@ -444,7 +444,7 @@ \subsection{Reconnaissance des entités nommées} Plusieurs mesures ont été présentées dans ce chapitre, ainsi que des tâches similaires à la résolution des coréférences. \end{discussion} -\sectioni{Ressources complémentaires} +\sectioni{Ressources supplémentaires} \subsubsection*{Exercices} diff --git a/cours/pos.tex b/cours/pos.tex index eb9c227..a5c6566 100644 --- a/cours/pos.tex +++ b/cours/pos.tex @@ -34,7 +34,7 @@ \chapter{Étiquetage morpho-syntaxique} \begin{itemize} \item c'est une étape préliminaire pour l'analyse syntaxique (chapitre suivant). \item les catégories grammaticales peuvent être considérée comme une caractéristique importante dans des tâche de classification des textes. - Par exemple, la tâche de détection de l'auteur peut bénéficier de cette caractéristiques ; certains auteurs utilisent plus de noms dans les phrases que d'autres. + Par exemple, la tâche de détection de l'auteur peut bénéficier de cette caractéristique ; certains auteurs utilisent plus de noms dans les phrases que d'autres. \end{itemize} @@ -256,10 +256,10 @@ \subsection{Modèle de Markov caché} Prenons un exemple d'un corpus d'entraînement avec quatre phrases. Nous avons 4 catégories : DET, PRON, VERB et NOUN. \begin{itemize} - \item un/DET ordianteur/NOUN peut/VERB vous/PRON aider/VERB + \item un/DET ordinateur/NOUN peut/VERB vous/PRON aider/VERB \item il/PRON veut/VERB vous/PRON aider/VERB \item il/PRON veut/VERB un/DET ordinateur/NOUN - \item il/PRON peut/VERB nager/VERB + \item il/PRON peut/VERB nager/VERB \end{itemize} Un exemple de calcul d'une transition est le suivant : $P(VERB | NOUN) = \frac{C(NOUN,\ VERB)}{C(NOUN)} = \frac{1}{2}$. La probabilité d'émission du mot ``veut" lorsque le tag est ``VERB" est calculée comme suit : $P(veut | VERB) = \frac{C(VERB,\ veut)}{C(VERB)} = \frac{2}{7}$. @@ -317,7 +317,7 @@ \subsection{Modèle de Markov caché} $viterbi[s, 1] = \pi_s * b_s(w_1);\, backpointer[s, 1] = 0$ \; } - \Pour{$ t = 1 \ldots T$}{ + \Pour{$ t = 2 \ldots T$}{ \Pour{état $ s = 1 \ldots N$}{ $viterbi[s, t] = \max\limits_{s'=1}^N viterbi[s', t-1] * a_{s',s} * b_s(w_t)$\; $backpointer[s, t] = \arg\max\limits_{s'=1}^N viterbi[s', t-1] * a_{s',s} * b_s(w_t)$\; diff --git a/cours/sens_mots.tex b/cours/sens_mots.tex index 98adb65..607eeb5 100644 --- a/cours/sens_mots.tex +++ b/cours/sens_mots.tex @@ -848,12 +848,37 @@ \subsection{Basée sur l'apprentissage automatique} %\end{discussion} -\sectioni{Ressources complémentaires} +\sectioni{Ressources supplémentaires} \subsubsection*{Exercices} \begin{enumerate} - \item ... + \item Étant donné deux mots M1 et M2, on veut savoir le type de relation qu'on peut extraire en utilisant des différentes représentations. Notons HYP(M1, M2) une mesure basée sur le chemin le plus court qui connecte M1 et M2 en utilisant la relation hyponyme/hyperonyme de Wordnet. Notons COS(X,Y) le cosinus entre deux vecteurs X et Y. Sélectionner le type de relation capturée par chaque mesure : + + \begin{tabular}{|llll|} + \hline + Mesure & Similarité (sens) & Association & Aucune\\ + \hline + HYP(M1, M2) & \Square & \Square & \Square \\ + COS(OneHot(M1), OneHot(M2)) & \Square & \Square & \Square \\ + COS(Word2Vec(M1), Word2Vec(M2)) & \Square & \Square & \Square \\ + \hline + \end{tabular} + + \item Voici trois phrases : + + \begin{tabular}{|lll|} + \hline + I fish a fish in the river & the river where I fish is far & the fish is far in the river\\ + \hline + \end{tabular} + + \begin{enumerate} + \item En utilisant TF, encoder les deux premières phrases et calculer Cos(S1, S2) + \item En appliquant un filtrage des mots vides dont la liste est [ I, a, in, the, is, where ], refaire la même chose + \item Trouver la représentation Mot-Mot du mot "fish" avec une co-occurrence de 2-2. + \end{enumerate} + \end{enumerate} diff --git a/cours/sens_phrases.tex b/cours/sens_phrases.tex index 24e18e4..c6668b4 100644 --- a/cours/sens_phrases.tex +++ b/cours/sens_phrases.tex @@ -877,7 +877,7 @@ \section{Analyse sémantique} Aussi, elle pourrait être une commande, si nous voulions implémenter un assistant personnel intelligent comme Alexa, Cortana ou Siri. \end{discussion} -\sectioni{Ressources complémentaires} +\sectioni{Ressources supplémentaires} \subsubsection*{Exercices} diff --git a/cours/syntaxe.tex b/cours/syntaxe.tex index 0a668ee..3ab7805 100644 --- a/cours/syntaxe.tex +++ b/cours/syntaxe.tex @@ -421,7 +421,7 @@ \subsection{Algorithme CKY probabiliste} \Pour{$ j = (i+1) \ldots n $}{ \Pour{$ k = i \ldots (j-1) $}{ \PourTous{$A$ tel que $(A \rightarrow B C) \in P $ et $T[i, k, B] > 0$ et $T[k+1, j, C] > 0$}{ - $p \rightarrow P(A \rightarrow B C) * T[i, k, B][1] * T[k+1, j, C][1]$\; + $p \leftarrow P(A \rightarrow B C) * T[i, k, B][1] * T[k+1, j, C][1]$\; \Si{$p > T[i, j, A][1]$}{ $T[i, j, A] \leftarrow (p, k, B, C)$ \; } @@ -487,12 +487,12 @@ \subsection{Par transition} \item $A$ est la liste des arcs créés (dépendances) \end{itemize} L'analyse commence avec la configuration $C_{initiale} = ([ROOT], w, \emptyset)$ et termine avec la configuration $C_{finale} = ([ROOT], \varnothing, A)$. -Si nous arrivions à la fin du mot avec une pile non vide (on considère le sommet $ROOT$ comme vide) sans actions de vidage, nous pourrions conclure que ce mot n'appartient pas au langage. +Si nous arrivions à la fin du mot avec une pile non vide (ici, nous considérons le sommet $ROOT$ comme vide) sans actions de vidage, nous pourrions conclure que ce mot n'appartient pas au langage. Une transition d'un état vers un autre peut être une action sur : \begin{itemize} - \item $\sigma$ : empiler ou dépiler un mot - \item $\beta$ : retirer un mot ou ajouter un au début - \item $A$ : ajouter une dépendance entre 2 mots + \item $\sigma$ : empiler ou dépiler un mot ; + \item $\beta$ : retirer un mot ou ajouter un au début ; + \item $A$ : ajouter une dépendance entre 2 mots. \end{itemize} \textbf{oracle} est un modèle entraîné pour décider la transition suivante. @@ -503,7 +503,7 @@ \subsection{Par transition} \end{figure} -Afin de décrire l'algorithme d'analyse, nous utilisons deux fonctions : ``$oracle$" qui choisit une transition ``$t$", et ``$Appliquer$" qui exécute ``$t$" sur la configuration. +Afin de décrire l'algorithme d'analyse, nous utilisons deux fonctions : ``$Oracle$" qui choisit une transition ``$t$", et ``$Appliquer$" qui exécute ``$t$" sur la configuration. L'algorithme \ref{algo:anal-dep-trans} décrit l'analyse des dépendances par transitions. \begin{algorithm}[ht] @@ -514,7 +514,7 @@ \subsection{Par transition} \Tq{$\sigma \ne [ROOT]$ OU $\beta \ne \varnothing$}{ - $t \leftarrow oracle(C)$\; + $t \leftarrow Oracle(C)$\; $C \leftarrow Appliquer(C, t)$\; } @@ -527,7 +527,7 @@ \subsection{Par transition} \begin{equation}\label{eq:orancle-psi} \hat{t} = \arg\max\limits_{t \in T} \Psi (t, C, w; \theta) \end{equation} -Pour entraîner le modèle ``oracle", nous devons annoter le texte en le transformant à une séquence de transitions. +Pour entraîner le modèle ``Oracle", nous devons annoter le texte en le transformant à une séquence de transitions. Le texte original est annoté avec les relations de dépendance. Pour transformer la sortie à une séquence de transitions, nous utilisons le système d'analyse en tel sorte à générer toutes les dépendances possibles. En utilisant des caractéristiques sur la configuration courante, nous essayons d'estimer la transition suivante par la fonction $\Psi$. @@ -565,7 +565,7 @@ \subsection{Par transition} Dans Arc-Eager, nous essayons de détecter les relations de dépendance le plus tôt possible. Pour ce faire, les relations doivent être détectées entre le sommet de la pile et le mot courant. Dans ce cas, nous n'avançons pas la tête de lecture automatiquement lorsque nous ajoutons une nouvelle relation. -Dans ce cas, la seule méthode pour le faire est d'empiler le mot dans la pile en utilisant la transition ``SHIFT". +La seule méthode pour le faire est d'empiler le mot dans la pile en utilisant la transition ``SHIFT". Un exemple de l'analyse de la phrase ``\expword{they like bagels with lox}" en utilisant Arc-eager est illustré dans la figure \ref{fig:arc-eager-exp}. Les transitions possibles dans cette approches sont : \begin{itemize} @@ -711,18 +711,7 @@ \subsection{Par graphe} %\end{discussion} -Les tutoriels sont accessibles via le répertoire Github. -Dans le premier tutoriel, Stanford CoreNLP est utilisé pour analyser deux phrases syntaxiquement. -Deux types d'analyse syntaxique sont utilisés : analyse des constituants et analyse des dépendances. -Il faut mentionner que CoreNLP est un outil implémenté en java destiné pour TALN. - -Le deuxième tutoriel utilise NLTK ; un outil en python très connu pour TALN. -Dans ce tutoriel, nous appliquons des différentes opérations sur les grammaires à contexte libre (CFG). -Nous montrons comment créer une nouvelle CFG, la lacer à partir d'un fichier et utiliser des treebanks. -Nous testons des différents algorithmes d'analyse syntaxique (des constituants) ainsi que la génération de texte. - - -\sectioni{Ressources complémentaires} +\sectioni{Ressources supplémentaires} \subsubsection*{Exercices} @@ -743,6 +732,7 @@ \subsubsection*{Tutoriels} Nous montrons comment créer une nouvelle CFG, la lacer à partir d'un fichier et utiliser des treebanks. Nous testons des différents algorithmes d'analyse syntaxique (des constituants) ainsi que la génération de texte. + \subsubsection*{TP : Analyse syntaxique CKY} On veut concevoir un petit programme pour l'analyse syntaxique à partir de zéro. diff --git a/img/app/app-mt01.odg b/img/app/app-mt01.odg new file mode 100644 index 0000000000000000000000000000000000000000..46950545d16f390ff2336a525d09e1e67482980e GIT binary patch literal 13518 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 0$ et $T[k+1, j, C] > 0$}{ - $p \rightarrow P(A \rightarrow B C) * T[i, k, B][1] * T[k+1, j, C][1]$\; + $p \leftarrow P(A \rightarrow B C) * T[i, k, B][1] * T[k+1, j, C][1]$\; \Si{$p > T[i, j, A][1]$}{ $T[i, j, A] \leftarrow (p, k, B, C)$ \; } @@ -511,13 +511,13 @@ \subsection{Par transition} \end{minipage} \begin{itemize} - \item \textbf{Transition} : passer d'un état vers un autre en utilisant des actions sur + \item \textbf{Transition} : passer d'un état vers un autre en utilisant des actions sur : \begin{itemize} - \item $\sigma$ : empiler ou dépiler un mot - \item $\beta$ : retirer un mot ou ajouter un au début - \item $A$ : ajouter une dépendance entre 2 mots + \item $\sigma$ : empiler ou dépiler un mot ; + \item $\beta$ : retirer un mot ou ajouter un au début ; + \item $A$ : ajouter une dépendance entre 2 mots. \end{itemize} - \item \textbf{Oracle} : un système qui décide la transition suivante + \item \textbf{Oracle} : un modèle entraîné pour décider la transition suivante \end{itemize} \end{frame} @@ -527,7 +527,7 @@ \subsection{Par transition} \framesubtitle{Par transition : Algorithme d'analyse} \begin{itemize} - \item Le système ``$Oracle$" choisit une transition ``$t$" + \item Le modèle ``$Oracle$" choisit une transition ``$t$" \item La fonction ``$Appliquer$" exécute ``$t$" sur la configuration \end{itemize} @@ -552,50 +552,50 @@ \subsection{Par transition} \begin{frame} \frametitle{Analyse syntaxique : Analyse des dépendances} -\framesubtitle{Par transition : Système Oracle (entraînement)} +\framesubtitle{Par transition : Modèle Oracle (entraînement)} \begin{itemize} - \item Le système Oracle apprend à inférer la transition $\hat{t}$ étant donné + \item Le modèle ``Oracle" apprend à inférer la transition $\hat{t}$ étant donné : \begin{itemize} - \item la configuration actuelle $ C = (\sigma, \beta, A) $ - \item l'ensemble $T$ des transactions possibles - \item une fonction $\Psi$ qui calcule un score en utilisant sur des caractéristiques basées sur la configuration - \item un texte annoté (TreeBank) + \item la configuration actuelle $ C = (\sigma, \beta, A) $ ; + \item l'ensemble $T$ des transactions possibles ; + \item une fonction $\Psi$ qui calcule un score en utilisant des caractéristiques basées sur la configuration ; + \item un texte annoté (TreeBank). \end{itemize} \[ \hat{t} = \arg\max\limits_{t \in T} \Psi (t, C, w; \theta) \] - \item Le texte annoté doit être transformé à une séquence de transactions - \item Pour entraîner l'Oracle, on utilise l'algorithme d'analyse - \item La fonction $\Psi$ peut être MaxEnt (le plus utilisé), SVM ou les réseaux de neurones + \item Le texte annoté doit être transformé à une séquence de transactions. + \item Pour entraîner le modèle ``Oracle", on utilise l'algorithme d'analyse. + \item La fonction $\Psi$ peut être MaxEnt (le plus utilisé), SVM ou les réseaux de neurones. \end{itemize} \end{frame} \begin{frame} \frametitle{Analyse syntaxique : Analyse des dépendances} -\framesubtitle{Par transition : Système Oracle (caractéristiques)} +\framesubtitle{Par transition : Modèle Oracle (caractéristiques)} \begin{itemize} - \item \textbf{La pile $\sigma$} + \item \textbf{La pile $\sigma$} : \begin{itemize} - \item Le mot dans le sommet de la pile - \item La catégorie grammaticale de ce mot + \item Le mot dans le sommet de la pile ; + \item La catégorie grammaticale de ce mot. \end{itemize} - \item \textbf{Le tampon d'entrée $\beta$} + \item \textbf{Le tampon d'entrée $\beta$} : \begin{itemize} - \item Les trois premiers mots - \item Leurs catégories grammaticales + \item Les trois premiers mots ; + \item Leurs catégories grammaticales. \end{itemize} - \item \textbf{La liste des dépendances $A$} + \item \textbf{La liste des dépendances $A$} : \begin{itemize} - \item Les dépendances qui ont été estimées + \item Les dépendances qui ont été estimées. \end{itemize} - \item \textbf{La phrase analysée $w$} + \item \textbf{La phrase analysée $w$} : \begin{itemize} - \item La distance entre le mot du sommet de la pile et le premier mot dans le tampon (nombre des mots entre eux dans la phrase $w$) + \item La distance entre le mot du sommet de la pile et le premier mot dans le tampon (nombre des mots entre eux dans la phrase $w$). \end{itemize} \end{itemize}