-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
1,102 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<N; i++){ | ||
const Ti = []; | ||
T.push(Ti); | ||
for (j=0; j<N; j++) Ti.push([]); | ||
//T.push((new Array(N)).fill(new Array())); //crée la même référence pour les colonnes | ||
const mot = phrase[i]; | ||
this.lex[mot].forEach(function(A){ | ||
T[i][i].push([A, -1, -1, -1]); | ||
}); | ||
} | ||
|
||
// Compléter ici : remplissage | ||
return T; | ||
} | ||
|
||
} | ||
|
||
// ====================================================== | ||
// Analyse syntaxique | ||
// ====================================================== | ||
|
||
|
||
// TODO: compléter cette fonction | ||
function evaluer_stricte(ref, sys){ | ||
return 0 | ||
} | ||
|
||
|
||
function construire(T, phrase, i, j, pos){ | ||
let [A, k, iB, iC] = T[i][j][pos]; | ||
A = A.toUpperCase(); | ||
if (k>=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<T[0][n].length; pos++){ | ||
if (T[0][n][pos][0] == 'S'){ | ||
return construire(T, phrase, 0, n, pos); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
analyser(phrase){ | ||
return this._analyser(phrase.trim().split(' ')); | ||
} | ||
|
||
charger_modele(url){ | ||
const f = fs.readFileSync(url, 'utf8'); | ||
const lex = {}, gram=[]; | ||
let i; | ||
f.split(/\r?\n/).forEach(function(l){ | ||
l = l.trim(); | ||
if ((l.length > 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(); |
Oops, something went wrong.