introdución al procesamiento del lenguaje natural en prolog
DESCRIPTION
Introdución al procesamiento del lenguaje natural en PROLOG. Jorge Cabrera Gámez Departamento de Informática y Sistemas Universidad de Las Palmas de Gran Canaria. Procesamiento del lenguaje natural en Prolog. - PowerPoint PPT PresentationTRANSCRIPT
Prolog 1
Introdución al
procesamiento del
lenguaje natural en
PROLOG
Jorge Cabrera GámezDepartamento de Informática y Sistemas
Universidad de Las Palmas de Gran Canaria
Prolog 2
Procesamiento del lenguaje natural en Prolog
Prolog ofrece algunas facilidades para definir analizadores gramaticales y, en general, para diseñar sistemas orientados al procesamiento del lenguaje natural.
Supongamos que necesitamos definir un programa que sea capaz de aceptar frases como:
• Mi abuelo come papaya.• El coche rojo es muy veloz.• Tu hermano es el hijo de tus padres• Tu abuelo es el padre de tus padres• Mi primo es el hijo de mis tíos
Prolog 3
Procesamiento del lenguaje natural en Prolog
• Tu hermano es el hijo de tus padres• Tu abuelo es el padre de tus padres• Mi primo es el hijo de mis tíos
Estas frases se ajustan a la siguiente gramática BNF:
<frase>::= <sn> <sv><sn>::= <adjetivo> <nombre> | <determinante> <nombre><sv>::= <verbo> <atributo><atributo>::= <sn> <cn><adjetivo>::= tu | mi | mis | tus<nombre>::= hermano | abuelo | primo | padres | tíos | hijo | padre<verbo>::= es<determinante>::= el<cn>::= <prep> <sn><prep>::= de
Prolog 4
<frase>::= <sn> <sv><sn>::= <adjetivo> <nombre> | <determinante> <nombre><sv>::= <verbo> <atributo><atributo>::= <sn> <cn><adjetivo>::= tu | mi | mis | tus<nombre>::= hermano | abuelo | primo | padres | tíos | hijo | padre<verbo>::= es<determinante>::= el<cn>::= <prep> <sn><prep>::= de
frase(L):-append(SN,SV,L),sn(SN),sv(SV).
La idea es escribir un programa que acepte una frase si se adecúa a las reglas de la gramática o la rechace en caso contrario.
La frase en cuestión se presentará como una lista de palabras, p.e.:?- frase([mi,primo,es,el,hijo,de,mis,tíos]).
Una forma de implementar estas reglas gramaticales es emplear una estrategia de “generación_y_test” como muestra el siguiente ejemplo:
Prolog 5
sv(SV):- append(SVA,SVB,SV),verbo(SVA),atributo(SVB).
atributo(SA):-append(SN,CN,SA),sn(SN),cn(CN).
cn(SV):- append(P,SN,SV),prep(P),sn(SN).
adjetivo([mi]).adjetivo([mis]).adjetivo([tu]).adjetivo([tus]).
nombre([hermano]).nombre([abuelo]).nombre([primo]).nombre([padres]).nombre([tíos]).nombre([hijo]).nombre([padre]).
verbo([es]).
determinante([el]).
prep([de]).
<frase>::= <sn> <sv><sn>::= <adjetivo> <nombre> | <determinante> <nombre><sv>::= <verbo> <atributo><atributo>::= <sn> <cn><adjetivo>::= tu | mi | mis | tus<nombre>::= hermano | abuelo | primo | padres | tíos | hijo | padre<verbo>::= es<determinante>::= el<cn>::= <prep> <sn><prep>::= de
frase(L):- append(SN,SV,L),sn(SN),sv(SV).
sn(SN):- append(SNA,SNB,SN),adjetivo(SNA),nombre(SNB).
sn(SN):- append(SNA,SNB,SN),determinante(SNA),nombre(SNB).
Prolog 6
Listas “diferencia”
Se denominan “listas diferencia” (difference lists) a una forma de representar listas en Prolog que - como técnica de programación - puede provocar un notable incremento de eficiencia.Ejemplo: Supongamos que deseamos diseñar un procedimiento que nos permita añadir un elemento a una lista por la cabeza.
add_to_head(X,Ys,[X|Ys]).
Esto es fácil. Supongamos ahora que deseamos diseñar el procedimiento complementario add_to_back.
Prolog 7
Ejemplo (cont): Realmente no es muy difícil ...
add_to_back(X, [ ], [X]).add_to_back(X, [Y|Ys], [Y|Zs]):-
add_to_back(X,Ys,Zs).
Sin embargo, es terriblemente ineficiente por motivos evidentes.
Ejemplo: Algo similar ocurre con la definición estándar de append/3:
append([ ], Ys, Ys).append([X|Xs], Ys, [X|Zs]):-
append(Xs,Ys,Zs).
Prolog 8
Las listas diferencia permiten manipular listas de forma mucho más eficiente definiendo “patrones de listas”.
Por ejemplo:
difference_append (A-Z, Z-B, A-B).
?- difference_append([a,b,c], [d,e], R).No.
?- difference_append([a,b,c]-Z, Z-[d,e], R).Z = _G379R = [a, b, c]-[d, e]
Yes
?- difference_append([a,b,c|Z] - Z, [d,e] - [ ], R).Z = [d, e]R = [a, b, c, d, e] - [ ] Yes
?- difference_append([a,b,c|Z] - Z, [d,e] - [ ], R- [ ]).
Z = [d, e]R = [a, b, c, d, e]
Yes
Prolog 9
Las siguientes definiciones transforman una lista diferencia en una lista“normal”, pero no a la inversa
dl_to_list([ ] - _, [ ]) :- !.dl_to_list([X|Y] - Z, [X|W]) :- dl_to_list(Y - Z, W).
?- dl_to_list([1,2,3|X]-X,L).X = [ ]L = [1,2,3];No
Recíprocamente, list_to_dl transforma una lista “normal” en una lista diferencia, pero no a la inversa
list_to_dl([], X - X).list_to_dl([X|W], [X|Y] - Z) :- list_to_dl(W, Y - Z).
?- list_to_dl([a,b,c],Y-Z).Y = [a, b, c|_G167]Z = _G167 ;No
Prolog 10
Listas Diferencia
El problema con esta estrategia es que es terriblemente ineficaz como puede comprobarse fácilmente realizando una traza de la anterior definición de frase/1.
La estrategia más eficiente es evitar la etapa de generación y pasar la lista completa a los predicados que implementan las reglas gramaticales. Éstos identificarán los correspondientes elementos gramaticales procesando secuencialmente los elementos de la lista de izquierda a derecha, devolviendo el resto de la lista.
Para hacer esto podemos emplear listas diferencia como se ilustra en la siguiente versión del analizador gramatical del ejemplo anterior.
Prolog 11
sv(SV-R):- verbo(SV-SV1),atributo(SV1-R).
atributo(SA-R):-sn(SA-SA1),cn(SA1-R).
cn(SV-R):- prep(SV-SV1),sn(SV1-R).
adjetivo([mi|X]-X).adjetivo([mis|X]-X).adjetivo([tu|X]-X).adjetivo([tus|X]-X).
nombre([hermano|X]-X).nombre([abuelo|X]-X).nombre([primo|X]-X).nombre([padres|X]-X).nombre([tíos|X]-X).nombre([hijo|X]-X).nombre([padre|X]-X).
verbo([es|X]-X).determinante([el|X]-X).prep([de|X]-X).
<frase>::= <sn> <sv><sn>::= <adjetivo> <nombre> | <determinante> <nombre><sv>::= <verbo> <atributo><atributo>::= <sn> <cn><adjetivo>::= tu | mi | mis | tus<nombre>::= hermano | abuelo | primo | padres | tíos | hijo | padre<verbo>::= es<determinante>::= el<cn>::= <prep> <sn><prep>::= de
frase(S):-sn(S-S1),sv(S1-[]).
sn(SN-R):-adjetivo(SN-SN1),nombre(SN1-R).
sn(SN-R):-determinante(SN-SN1),nombre(SN1-R).
Prolog 12
sv(SV-R):- verbo(SV-SV1),atributo(SV1-R).
atributo(SA-R):-sn(SA-SA1),cn(SA1-R).
cn(SV-R):- prep(SV-SV1),sn(SV1-R).
adjetivo([mi|X]-X).adjetivo([mis|X]-X).adjetivo([tu|X]-X).adjetivo([tus|X]-X).
nombre([hermano|X]-X).nombre([abuelo|X]-X).nombre([primo|X]-X).nombre([padres|X]-X).nombre([tíos|X]-X).nombre([hijo|X]-X).nombre([padre|X]-X).
verbo([es|X]-X).determinante([el|X]-X).prep([de|X]-X).
?- nombre([hijo,de,mis,tíos] - X).X = [de, mis, tíos] Yes
?- sn([mi,abuelo,es,el,padre,de,mi,padre] - X).X = [es, el, padre, de, mi, padre] Yes
frase(S):-sn(S-S1),sv(S1-[ ]).
sn(SN-R):-adjetivo(SN-SN1),nombre(SN1-R).
sn(SN-R):-determinante(SN-SN1),nombre(SN1-R).
Prolog 13
Gramática Definida por Cláusulas
La mayoría de las implementaciones de Prolog incorporan la posibilidad de definir gramáticas mediante una sintaxis especial que oculta la presencia de las listas diferencia. A esta sintaxis se le conoce como gramática definida por cláusulas (Definite Clause Grammar, DCG).
frase --> sn, sv.
sn --> adjetivo, nombre.sn --> determinante, nombre.
sv --> verbo, atributo.atributo --> sn, cn.cn --> prep, sn.
adjetivo --> [mi].adjetivo --> [mis].adjetivo --> [tu].adjetivo --> [tus].
nombre --> [hermano].nombre --> [abuelo].nombre --> [primo].nombre --> [padres].nombre --> [tíos].nombre --> [hijo].nombre --> [padre].
verbo --> [es].determinante --> [el].prep --> [de].
Prolog 14
Gramática de Cláusulas Definidas
Las cláusulas gramaticales así definidas se analizan y “traducen” en cláusulas Prolog que emplean listas diferencias. Por ejemplo, la primera de las reglas:
frase --> sn, sv.
se traduce en:frase(A, B) :- sn(A, C), sv(C, B).
Así que para analizar una frase, hemos de invocar frase/2 con dos argumentos:
?- frase([mi,abuelo,es,el,padre,de,mi,padre],R).R = [] Yes
Evidentemente , el segundo argumento “recogerá” el resto “inaceptado” de la frase cuando éste exista.
Prolog 15
Gramática de Cláusulas Definidas
Las cláusulas gramaticales que recogen los símbolos terminales, el vocabulario, se traducen también en listas diferencias. Por ejemplo:
adjetivo --> [mi].
se traduce en:adjetivo([mi|A],A).
La gramática que hemos definido presenta algunas deficiencias como la falta de concordancia entre el número del adjetivo y el nombre. Por ejemplo, “mi padres” resultaría aceptable como sintagma nominal (sn):
?- sn([mi,padres], R).R = [] Yes
Por ello una frase como la siguiente sería aceptable:
?- frase([mi,abuelo,es,el,padres,de,mis,padre],R).R = [] Yes
Prolog 16
Uso de Variables en DCGs
Para resolver este problema podemos emplear argumentos en las cláusulas de la gramática como se muestra a continuación:
frase --> sn, sv.sn --> adjetivo(N), nombre(N).sn --> determinante(N), nombre(N).sv --> verbo, atributo.atributo --> sn, cn.cn --> prep, sn.
adjetivo(sing) --> [mi].adjetivo(plur) --> [mis].adjetivo(sing) --> [tu].adjetivo(plur) --> [tus].
nombre(sing) --> [hermano].nombre(sing) --> [abuelo].nombre(sing) --> [primo].nombre(plur) --> [padres].nombre(plur) --> [tíos].nombre(sing) --> [hijo].nombre(sing) --> [padre].
verbo --> [es].determinante(sing) --> [el].prep --> [de].
Ahora una frase como:
?- frase([mi,primo,es,el,hijo,de,mi,tíos],R).No
ya no resulta aceptable
Prolog 17
Uso de Variables en DCGs
El uso de variables no está restringido al cuerpo de las reglas. Así la versión de la gramática que se muestra en la siguiente diapositiva usa variables para devolver un análisis sintáctico (y eventualmente morfológico) de la frase.
Por ejemplo (ligeramente retocado):
?- frase(S,[mi,primo,es,el,hijo,de,mis,tíos],[]).
S = análisis( sn(adj(mi), nom(primo)), sv(v(es), atrib(sn(det(el), nom(hijo)), cn(prep(de), sn(adj(mis), nom(tíos))))))
Yes
Prolog 18
frase(análisis(S,V)) --> sn(S), sv(V).sn(sn(A,B)) --> adjetivo(A,N), nombre(B,N).sn(sn(A,B)) --> determinante(A,N), nombre(B,N).sv(sv(V,A)) --> verbo(V), atributo(A).atributo(atrib(S,C)) --> sn(S), cn(C).cn(cn(P,S)) --> prep(P), sn(S).
adjetivo(adj(mi),sing) --> [mi].adjetivo(adj(mis),plur) --> [mis].adjetivo(adj(tu),sing) --> [tu].adjetivo(adj(tus),plur) --> [tus].
nombre(nom(hermano),sing) --> [hermano].nombre(nom(abuelo),sing) --> [abuelo].nombre(nom(primo),sing) --> [primo].nombre(nom(padres),plur) --> [padres].nombre(nom(tíos),plur) --> [tíos].nombre(nom(hijo),sing) --> [hijo].nombre(nom(padre),sing) --> [padre].
verbo(v(es)) --> [es].determinante(det(el),sing) --> [el].prep(prep(de)) --> [de].
Prolog 19
Es posible incluir cláusulas “prolog” en la definición de las cláusulas gramaticales. Las cláusulas “prolog” deben encerrarse entre llaves { }, como se muestra en el siguiente ejemplo, donde se han agrupado las definiciones de los adjetivos en la gramática que se ha venido usando como ejemplo (exactamente lo mismo puede hacerse con los nombres):adjetivo(adj(X),sing) --> [X],{ member(X,[mi,tu]) }.adjetivo(adj(X),plural) --> [X], { concat_atom([Y,s],X),
adjetivo(adj(Y),sing,[Y],[])}.
?- listing(adjetivo).
adjetivo(adj(A), sing, B, C) :- 'C'(B, A, D), member(A, [mi, tu]), C=D.adjetivo(adj(A), plural, B, C) :- 'C'(B, A, D), concat_atom([E, s], A), adjetivo(adj(E), sing, [E], []), C=D.Yes
Prolog 20
frase(análisis(S,V)) --> sn(S), sv(V).sn(sn(A,B)) --> adjetivo(A,N), nombre(B,N).sn(sn(A,B)) --> determinante(A,N), nombre(B,N).sv(sv(V,A)) --> verbo(V), atributo(A).atributo(atrib(S,C)) --> sn(S), cn(C).cn(cn(P,S)) --> prep(P), sn(S).
adjetivo(adj(X),sing) --> [X],{ member(X,[mi,tu]) }.
adjetivo(adj(X),plural) --> [X], { concat_atom([Y,s],X), adjetivo(adj(Y),sing,[Y],[])}.
nombre(nom(X),sing) --> [X], { member(X,[abuelo,hermano,padre, primo,tío,hijo]) }.
nombre(nom(X),plural) --> [X], { concat_atom([Y,s],X), nombre(nom(Y),sing,[Y],[])}.
verbo(v(es)) --> [es].determinante(det(el),sing) --> [el].prep(prep(de)) --> [de].