Vous êtes sur la page 1sur 8

Notion de la grammaire

Afin de développer des outils informatiques de traitement de langue naturelle, nous avons
d'abord besoin d’algorithmes permettant de déterminer automatiquement si une phrase répond à
une structure prédéfinie.
Les premières étapes effectuées pour aboutir à ce but ont été dans le cadre des grammaires
formelles utilisées pour la définition de langages de programmation. Au contraire des langages
artificiels, dans le cas des langues naturelles, il faut tenir compte de phénomènes complexes et
donc les structures des grammaires ou des analyseurs deviennent plus compliquées.
Un formalisme qui a prouvé sa valeur est celui des grammaires logiques basées essentiellement
sur le langage Prolog. “ Les programmes Prolog sont spécifiés sous forme de relations et de
règles autour des relations définies par la programmation en logique. Les programmes peuvent
être interrogés pour déterminer la véracité des faits ou pour déduire de nouveaux faits à l'aide des
règles.”
Une grammaire G est caractérisée par quatre composants :

G = < VT, VN, S, R >


VT : Vocabulaire terminal
C’est-à-dire l’alphabet sur lequel est défini le langage.
VT: Vocabulaire non terminal
C’est-à-dire l’ensemble des symboles qui n’apparaissent pas dans les mots générés, mais
qui sont utilisés au cours de la génération. Un symbole non terminal désigne une catégorie
syntaxique.
VT ∩ V T = ∅
S ∈ N : Le symbole de départ ou Axiome.
C’est à partir de ce symbole non terminal que l’on
commencera la génération de mots au moyen des règles de la grammaire.
R est un ensemble de règles dites de réécriture ou de production de la forme :
u1 → u2, avec u1 ∈ (N ∪ T)+ et u2 ∈ (N ∪ T)∗
La signification intuitive de ces règles est que la suite non vide de symboles terminaux ou
non terminaux u1 peut être remplacée par la suite éventuellement vide de symboles terminaux ou
non terminaux u2.
Terminologie :
→ Une suite de symboles terminaux et non terminaux (un élément de (N ∪ T) ∗ ) est
appelée une forme.
→ Une règle u1 → u telle que u ∈ T ∗ est appelée une règle terminale.

Grammaire en forme logique


Soit la structure suivante : une phrase (p) est composée d’un syntagme nominal (sn) et d’un
syntagme verbal (sv) ; un syntagme nominal (sn) est composé d’un déterminant (det) et d’un
nom (n) ; un syntagme verbal (sv) est composé d’un verbe (v) ou d’un verbe et d’un syntagme
nominal (sn).
Cette structure peut être exprimée sous forme d’énoncés logiques :
p = sn ∧ sv
sn = det ∧ n
sv = v ∨ (v ∧ sn)

Prenons l’exemple “ le garçon mange la pomme”, si on décortique la structure logique d’une


phrase, çà sera : p = (det ∧ n) ∧ (v ∨ (v ∧ det ∧ n)), on vérifie alors chaque élément.
on a la première partie de la phrase “le garçon” qui vérifie le composant ‘syntagme nominal’
puisque “le garçon” est composée de “ le ” ; un determinant; et “garçon” ;un nom; et ensuite
“mange la pomme” qui vérifie le composant ‘syntagme verbal’ puisque “mange la pomme” est
composée d’un verbe “manger” et un syntagme nominal “la pomme” (la : determinant, pomme :
nom).
Evidemment, il faut remarquer que le et (∧) est assez spécial car en plus d’indiquer la
conjonction, il indique la juxtaposition; il n’est donc pas commutatif. On peut représenter ce
traitement avec le schéma suivant :
| le garçon | mange la pomme |
| <-- sn --> | <------- sv -----------> |
| <- v -> | <--- sn --->|
Codage de la grammaire en Prolog
Afin d’analyser et vérifier si la phrase donnée est conforme à la grammaire définie, il faut d’abord
représenter la phrase. La phrase est considérée comme un ensemble structuré d'unités discrètes,
les mots, considérées comme atomiques. Ainsi, en utilisant les prédicats prédéfinis en Prolog, la
phrase “le garçon mange la pomme” sera représentée comme suit :
?-maplist(atom_string,L,["bonjour","Marcel","comment","allez",
"vous"]).
L = [bonjour, 'Marcel', comment, allez, vous].
Le prédicat atom_string permet la conversion d’une chaîne en atome ou inversement un atome
en une chaîne.
Il reste maintenant à coder les règles : nous choisissons la représentation suivante : un symbole
non-terminal sera représenté par un prédicat à deux arguments : le premier indique la chaîne
d'entrée et le second, ce qu’il reste de la chaîne après son traitement par le prédicat. Cette
représentation est communément appelée liste de différences. (Pereira Shieber 87) (Giannesini
85) montrent comment il est possible de dériver “naturellement” cette représentation. Ici nous la
prenons tout simplement comme acquise et elle date d’ailleurs des premiers travaux de
(Colmerauer 78) sur les grammaires de métamorphose. La définition d’un syntagme verbal peut
donc être exprimée ainsi :
sv(L0,L) :- v(L0,L).
sv(L0,L) :- v(L0,L1), sn(L1,L).
Le prédicat Prolog est la méthode pour contenir l'argument et renvoyer les valeurs booléennes
telles que true ou false.
Dans l’exemple ci-dessus, le prédicat v() vérifie la liste d’entrée “L0” et renvoie le résultat en liste
“L”. Dans le deuxième cas, la liste d'entrée du verbe est celle du syntagme mais sa sortie est
donnée en entrée au syntagme nominal pour qu’il fasse l’analyse de la suite et sa propre sortie
sera celle du syntagme.
Dans le cas d’un symbole terminal (caractères littéraux qui peuvent apparaître dans les règles de
production (en entrée ou sortie) d'une grammaire formelle et ne peuvent pas être subdivisés en
éléments plus petits), il suffit d’avoir une clause qui vérifie la présence du terminal au début de la
chaîne et qui donne en sortie la suite de la chapine.
terminal(Mot, [Mot | L], L).
De telle façon, pour vérifier qu’un nom peut être garçon, il suffit d’écrire :
n(L0,L) :- terminal(garçon,L0,L).
Il faut aussi faire attention à la conformité des genres et la transitivité du verbe qui sont des
vulnérabilités que notre programme peut avoir en cas d’inattention.
La conformité de genre peut être faite alors dans la clause sn où on force le déterminant et le
nom à avoir la même valeur pour Genre:
sn(L0,L) :- det(Genre,L0,L1), n(Genre,L1,L).
La transitivité est vérifiée dans sv où dans la première clause, nous décidons ici d’accepter un
verbe transitif ou intransitif comme verbe sans complément et nous donnons tout simplement
une variable muette notée par un souligné (_); dans la deuxième clause de sv, nous exigeons
toutefois la caractéristique transitif.
Le programme final est écrit alors sous la forme :
%%%
%%% Grammaire avec v´erifications
%%% (grammaire1.pro)
%%%
p(L0,L) :- sn(L0,L1), sv(L1,L).
sn(L0,L) :- det(Genre,L0,L1), n(Genre,L1,L).
sv(L0,L) :- v(_,L0,L).
sv(L0,L) :- v(transitif,L0,L1), sn(L1,L).
det(masculin,L0,L) :- terminal(le,L0,L).
det(feminin,L0,L) :- terminal(la,L0,L).
n(feminin,L0,L) :- terminal(pomme,L0,L).
n(masculin,L0,L) :- terminal(garçon,L0,L).
v(transitif,L0,L) :- terminal(mange,L0,L).
v(intransitif,L0,L) :- terminal(aller,L0,L).
Est on reçoit le résultat suivant :
| ?- p([la,garçon,mange,le,pomme],[]).
no
| ?- p([le,garçon,mange,la,pomme],[]).
yes
Cette méthode de représentation nous limite juste à l’analyse syntaxique de la phrase entrée en
entrée. Pour pouvoir analyser la structure de dépendance entre les constituants de la phrase, on
introduit alors l’outil déjà expliqué dans la section des structures en Prolog, c’est la structure
d’arborescence.

On aura alors la structure suivante :


ph

snm svb
dt nm vb snm

le garçon mange dt nm

la pomme

qui sera représentée comme suit :


ph(snm(dt(le),nm(garçon)),svb(vb(mange),
snm(dt(la),nm(pomme))))

Cet arbre est construit via un paramètre véhiculant l’information structurelle. Un foncteur Prolog
est très facile à construire car il suffit d'écrire sa forme et le processus d’unification le complète
ou vérifie son adéquation. Ainsi la structure d’une phrase est donnée par le foncteur ph avec
comme paramètre la structure du syntagme nominal et celle du syntagme verbal :
p(ph(SN_Struct,SV_Struct),L0,L) :
- sn(SN_Struct,L0,L1), sv(SV_Struct,L1,L),
Il faudra aussi vérifier qu’on ne retrouve pas le même syntagme nominal avant et après le verbe.
Nous pouvons ajouter ce test dans p pour s’assurer qu’on ne retrouve pas la même structure de
syntagme nominal comme sujet et comme complément du verbe. Pour refuser une phrase
comme “La pomme mange le garçon”. Il faut vérifier que celui qui est mangé par un autre est
bien une proie potentielle... On ajoutera donc de l'information sémantique à notre programme et
on le vérifiera dans la clause sv. Notre nouvelle grammaire devient donc :

%%% %%% grammaire avec construction de structures


%%% (grammaire2.pro)
%%%
p(ph(SN_Struct,SV_Struct),L0,L) :-
sn(SN_Struct,L0,L1), sv(SV_Struct,L1,L),
% s’assurer que les SN avant et apres le verbe sont
differents
\+(SV_Struct = svb(_,SN_Struct)).
sn(snm(Det_Struct,N_Struct),L0,L) :-
det(Det_Struct,Genre,L0,L1), n(N_Struct,Genre,L1,L).

sv(svb(vb(Mot)),L0,L) :- v(vb(Mot,_),_,L0,L).
sv(svb(vb(Mot),SN_Struct),L0,L) :-
v(vb(Mot,Comp),transitif,L0,L1), sn(SN_Struct,L1,L),
% verifier que le complement est semantiquement
acceptable
SN_Struct = snm(_,nm(Nom)), % extrait le nom
T =.. [Comp,Nom],
% construit le predicat de verification semantique
call(T). % on l’appelle
det(dt(le),masculin,L0,L) :- terminal(le,L0,L).
det(dt(la),feminin,L0,L) :- terminal(la,L0,L).
n(nm(pomme),feminin,L0,L) :- terminal(pomme,L0,L).
n(nm(garçon),masculin,L0,L) :- terminal(garçon,L0,L).
v(vb(mange,proie),transitif,L0,L) :-terminal(mange,L0,L).
v(vb(aller,_),intransitif,L0,L):-terminal(aller,L0,L).
terminal(Mot,[Mot|L],L).
% la semantique
proie(pomme).

Dans cet exemple, le programme est simple et on peut le faire à la main mais dans le cas
des programmes avec un niveau de complexité plus élevée, Prolog offre la possibilité
d’utiliser une notation DCG (Definite Clause Grammars)
où la clause s(L1, L2) :- sn(L1, L3), sv(L3, L2). peut s’écrire sous la
forme : s --> sn, sv.
Exemple de la transformation du code normale en utilisant le grammaire DCG :

La notation DCG de Prolog


➔ Les symboles de prédicats et de fonctions, les variables et les constantes obéissent
à la syntaxe habituelle de Prolog.
➔ Les symboles adjacents dans une partie droite de règle sont séparés par une
virgule, comme pour les littéraux en partie droite d'une clause
➔ La flèche est le symbole "-->"
➔ Les terminaux sont écrits entre "[" et "]"
➔ La chaîne vide ε est représentée par "[]".
➔ On peut insérer en partie droite des règles, des buts autres que les symboles de la
grammaire ; dans ce cas, ils figurent entre "{" et "}".

Le programme devient alors :


%%%
%%% Version DCG de la grammaire avec construction
%%% de structures
%%% (grammaire2dcg.pro)
%%%
p(ph(SN_Struct,SV_Struct)) →
sn(SN_Struct), sv(SV_Struct),
% s’assurer que les SN avant et apr`es le verbe sont
différents
{not(SV_Struct = svb(_,SN_Struct))}.

sn(snm(Det_Struct,N_Struct)) -->
det(Det_Struct,Genre), n(N_Struct,Genre).
sv(svb(vb(Mot))) --> v(vb(Mot,_),_).
sv(svb(vb(Mot),SN_Struct)) -->
v(vb(Mot,Comp),transitif), sn(SN_Struct),
% verifier que le compl´ement est s´emantiquement
acceptable
{SN_Struct = snm(_,nm(Nom)), (T =.. [Comp,Nom]),
call(T)}.

det(dt(le),masculin) --> [le].


det(dt(la),feminin) --> [la].

n(nm(pomme),feminin) --> [pomme].


n(nm(garçon),masculin) --> [garçon].

v(vb(mange,proie),transitif) --> [mange].


v(vb(aller,_),intransitif) --> [aller].
% la semantique
proie(pomme).

Les grammaires DCG sont alors des moyens pratiques de représenter les relations grammaticales
pour diverses applications d'analyse. Ils peuvent être utilisés pour le travail en langage naturel,
pour créer des langages de commande et de programmation formels.
Prolog est un outil très puissant pour implémenter des analyseurs et des compilateurs. La
notation Definite Clause Grammar (DCG) fournit un moyen pratique d'exploiter cette puissance
en traduisant automatiquement les règles de grammaire en clauses Prolog.

Vous aimerez peut-être aussi