9 SISTEMAS EXPERTOS

Anuncio
9
SISTEMAS EXPERTOS
En este capítulo abordaremos uno de los productos típicos de la Inteligencia Artificial:
los Sistemas Expertos. Normalmente, usamos herramientas de desarrollo conocidas
con shells para construir este tipo de sistemas, pero si necesitamos configurar un shell
para una aplicación en particular, es necesario conocer como es que un sistema experto
se construye desde cero. El capítulo constituye el segundo ejemplo del uso de Prolog
para resolver problemas típicos de la Inteligencia Artificial.
Los sistemas expertos (SE) son aplicaciones de cómputo que involucran experiencia no algorítmica, para resolver cierto tipo de problema. Por ejemplo, los sistemas
expertos se usan para el diagnóstico al servicio de humanos y máquinas. Existen SE
que juegan ajedrez, que planean decisiones financieras, que configuran computadoras,
que supervisan sistemas de tiempo real, que deciden políticas de seguros, y llevan a
cabo demás tareas que requieren de experiencia humana.
Los SE incluyen componentes del sistema en sí e interfaces con individuos con
varios roles. Esto se ilustra en la figura 26. Los componentes más importantes son:
• Base de conocimientos. La representación declarativa de la experiencia, muchas
veces en forma de reglas IF-THEN.
• Almacén de trabajo. Los datos específicos al problema que se está resolviendo.
• Máquina de inferencia. El código central del SE que deriva recomendaciones
con base en la base de conocimientos y los datos específicos del problema.
• Interfaz del usuario. El código que controla el diálogo entre el usuario y el SE.
Para entender un SE es necesario entender también el rol de los usuarios que interaccionan con el sistema:
• Experto del Dominio. El o los individuos que son los expertos en resolver el
problema que el SE intentará resolver.
• Ingeniero de Conocimiento. El individuo que codifica el conocimiento de los
expertos en forma declarativa, para que pueda ser usado por el SE.
• Usuario. El individuo que consultará el SE para obtener los consejos que esperaría de un experto del dominio.
Muchos SE se producen en ambientes de desarrollo conocidos como shells. Un shell
es un sistema que contiene la interfaz del usuario, un formato de conocimiento declarativo para la base de conocimientos y una máquina de inferencia. El ingeniero de
conocimiento usa el shell para construir un SE que resuelve problemas en un dominio
particular.
Si el sistema se construye desde cero, o utilizando shells configurados para cierto
tipo de aplicaciones, otro individuo entra en escena:
89
90
�������� ��������
Usuario
Experto en
el Dominio
Interface con
el Usuario
Experiencia
Ingeniero del
Conocimieno
Máquina de
Inferencia
Base de
Conocimiento
Ingeniero en
Sistemas
Almacén de
Trabajo
Figura 26: Componentes de un sistema experto e interfases humanas
• Ingeniero de Sistemas. La persona que construye la interfaz del usuario, diseña
el formato declarativo de la base de conocimientos, e implementa la máquina
de inferencia ¿adivinan cual es su rol?
En realidad eso depende de la talla del proyecto: El ingeniero de conocimiento y
el ingeniero del sistema, pueden ser la misma persona. El diseño del formato de la
base de conocimientos y su codificación están íntimamente relacionados. Al proceso
de codificar el conocimiento de los expertos, se le conoce como ingeniería del conocimiento. Siendo ésta una tarea complicada, se espera el uso de los shells haga posible
la reutilización del conocimiento codificado. En estas sesiones nos concentraremos en
la programación en Prolog de los SE al margen del uso de las shells.
�.�
��������������� �� ��� ��
Los SE poseen las siguientes características, en menor o mayor grado:
• Razonamiento guiado por las metas y encadenamiento hacia atrás. Una técnica
de inferencia que usa las reglas IF-THEN para descomponer las metas en submetas más fáciles de probar.
• Manejo de incertidumbre. La habilidad del SE para trabajar con reglas y datos
que no son conocidos con precisión.
• Razonamiento guiado por los datos y encadenamiento hacia adelante. Una técnica de inferencia que usa las reglas IF-THEN para deducir soluciones a un
problema a partir de los datos iniciales disponibles.
• Representación de datos. La forma en que los datos específicos a un problema
dado, son almacenados y accesados por el SE.
• Interfaz del usuario. La parte del SE que se usa para una interacción más amigable con el usuario.
�.� ��������������� �� ��� ��
• Explicación. La habilidad del SE para explicar sus procesos de razonamiento y
su uso en el cómputo de recomendaciones.
�.�.� Razonamiento basado en metas
El encadenamiento hacia adelante, o razonamiento basado en metas, es una forma
eficiente de resolver problemas que pueden ser modelados como casos de “selección
estructurada”; donde la meta del SE es elegir la mejor opción de entre varias posibilidades enumeradas. Por ejemplo, los problemas de identificación caen en esta
categoría. Los problemas de diagnóstico tambien caben aquí, pues se trata de elegir
el diagnóstico adecuado.
El conocimiento se codifica en reglas que describen como es que cada caso posible
podría ser seleccionado. La regla rompe el problema en sub-problemas. Por ejemplo,
las siguientes reglas formarían parte de un SE para identificar aves:
1
IF
2
familia es albatros AND
color es blanco
THEN
ave es albatros laysan.
3
4
5
6
7
IF
8
familia es albatros AND
color es negro
THEN
ave es albatros de pies negros.
9
10
11
El sistema puede usar otras reglas para resolver las sub-metas planteadas por las
reglas de alto nivel, por ejemplo:
1
IF
2
orden es tubonasales AND
tamaño es grande AND
alas es grandes anguladas
THEN
familia es albatros.
3
4
5
6
�.�.� Incertidumbre
Es muy común en la resolución de problemas de selección estructurada, que la respuesta final no es conocida con total certeza. Las reglas del experto pueden ser vagas,
o el usuario puede estar inseguro sobre sus respuestas. Esto es fácilmente observable
en el diagnóstico médico. Los SE normalmente usan valores numéricos para representar certidumbre. Existen diveras maneras de definirlos y usarlos en el proceso de
razonamiento.
91
92
�������� ��������
�.�.� Razonamiento guiado por los datos
Para muchos problemas no es posible enumerar las soluciones alternativas a las preguntas planteadas con antelación. Los problemas de configuración caen en esta categoría. El encadenamiento hacia adelante, o razonamiento guiado por los datos, usa
reglas IF-THEN para explorar el estado actual en la solución del problema y moverse
a estados más cercanos a la solución.
Un SE para acomodar el mobiliario puede tener reglas para la ubicación de un
mueble en particular. Una vez que un mueble ha sido colocado, se puede proceder
con los demás. La regla para colocar la TV enfrente del sofá es como sigue:
1
2
3
4
5
6
IF
no_colocada tv AND
sofá en pared(X) AND
pared(Y) opuesta a pared(X)
THEN
colocar tv en pared(Y).
Esta regla toma un estado del problema con la televisión no situada y regresa un
estado nuevo, donde la televisión ya ha sido colocada. Puesto que la televisión ya ha
sido colocada en su lugar, esta regla no volverá a ser disparada por el SE. Otras reglas
serán usadas para colocar el resto de los muebles hasta terminar.
�.�
������ �� ������� �� ���������� �� ������
Como pueden haber adivinado, Prolog posee una máquina de inferencia por encadenamiento hacía atrás. Esta máquina puede usarse parcialmente para implementar
algunos SE. Las reglas de Prolog serán usadas para representar conocimiento y su
máquina de inferencia será usada para derivar conclusiones. Otras partes del sistema,
como la interfaz con el usuario deberán escribirse usando Prolog.
Usaremos el problema de identificación de aves norteamericanas para ilustrar la
construcción de un SE con Prolog. La experticia del SE se basa en un subconjunto de
las reglas reportadas en Birds of North America de Robbins, Bruum, Zim y Singer 1 . Las
reglas del SE estarán diseñadas para ilustrar como se pueden representar varios tipos
de conocimiento, en vez de buscar una identificación precisa de las aves.
�.�.� Reglas
Las reglas de un SE normalmente toman el siguiente formato:
1
2
3
4
5
6
IF
primera premisa AND
segunda premisa AND
...
THEN
conclusión
1 La versión extendida en línea de este libro puede encontrarse en http://bna.birds.cornell.edu/bna/
�.� ������ �� ������� �� ���������� �� ������
La parte IF de la regla se conoce con el lado izquierdo de la regla (LHS), y la parte
del THEN se conoce como el lado derecho de la regla (RHS). Esto es equivalente a la
semantica de la regla Prolog:
1
2
3
4
conclusión :primera premisa,
segunda premisa,
...
Esto puede ser confuso pués la regla en prolog dice más THEN-IF que IF-THEN.
Retomemos los ejemplos anteriores, si queremos representar en Prolog la regla:
1
IF
2
familia es albatros AND
color es blanco
THEN
ave es albatros laysan
3
4
5
Tendríamos que escribir:
1
2
3
ave(albatros_laysan) :familia(albatros),
color(blanco).
Las siguientes reglas distinguen entre dos tipos de albatros y cisne (Ver la figura
27). Todas son cláusulas del predicado ave/1:
1
2
3
4
5
6
7
8
9
10
11
12
ave(albatros_laysan) :familia(albatros),
color(blanco).
ave(albatros_patas_negras)
familia(albatros),
color(obscuro).
ave(cisne_silbador) :familia(cisne),
voz(suave_musical).
ave(cisne_trompetero) :famila(cisne),
voz(alta_trompeta).
:-
Para que estas reglas tengan éxito al distinguir un ave, necesitamos almacenar hechos acerca del ave que deseamos identificar con el SE. Por ejemplo, si agregamos
estos hechos al programa:
1
2
familia(albatros).
color(obscuro).
Ahora podemos usar la pregunta siguiente:
?- ave(X).
X = albatros_patas_negras
Yes
93
94
�������� ��������
Figura 27: Cisne Trompetero en ilustración de John James Audubon (1838).
Observen que aún en esta etapa temprana tenemos un SE completo, donde la experticia consiste en distinguir entre cuatro aves. La interfaz con el usuario es el REPL
de Prolog y los datos de entrada se almacenan directamente en el programa.
�.�.� Reglas para relaciones jerárquicas
El siguiente paso será representar la naturaleza jerárquica del sistema de clasificación
de un ave. Esto incluirá reglas para identificar la familia y el orden del ave. Continuando con el albatros y el cisne, los predicados para orden/1 y familia/1 son:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
orden(nariz_tubular) :fosas(externas_tubulares),
habitat(mar),
pico(gancho).
orden(acuatico) :patas(membrana),
pico(plano).
familia(albatros) :orden(nariz_tubular),
tamaño(grande),
alas(muy_largas).
familia(cisne) :orden(acuatico),
cuello(largo),
color(blanco),
vuelo(pesado).
Ahora el SE puede identificar al albatros a partir de observaciones fundamentales
sobre el ave. En la primer versión, familia/1 fue implementada como un hecho. Ahora familia/1 es implementada como una regla. Los hechos del SE ahora reflejan más
datos primitivos:
�.� ������ �� ������� �� ���������� �� ������
1
2
3
4
5
6
fosas(externas_tubulares).
habitat(mar).
pico(gancho).
tamaño(grande).
alas(muy_largas).
color(obscuro).
La consulta siguiente reporta:
?- ave(X).
X = albatros_patas_negras
Yes
�.�.� Reglas para otras relaciones
El ganso canadiense puede usarse para agregar complejidad al sistema. Debido a
que esta ave pasa los veranos en Canadá y los inviernos en los Estados Unidos, su
identificación se ve afectada por donde ha sido vista y en que estación. Dos reglas
serán necesarias para cubrir estas situaciones:
1
2
3
4
5
6
7
8
9
10
11
12
ave(ganso_canadiense) :familia(ganso),
estacion(invierno),
pais(estados_unidos),
cabeza(negra),
pecho(blanco).
ave(ganso_canadiense) :familia(ganso),
estacion(verano),
pais(canada),
cabeza(negra),
pecho(blanco).
Estas metas pueden hacer referencia a otros predicados en una jerarquía diferente:
1
2
3
4
5
6
pais(estados_unidos) :- region(oeste_medio).
pais(estados_unidos) :- region(sur_oeste).
pais(estados_unidos) :- region(nor_oeste).
pais(estados_unidos) :- region(atlantico_medio).
pais(canada) :- provincia(ontario).
pais(canada) :- provincia(quebec).
7
8
9
10
region(nueva_inglaterra) :estado(X),
member(X,[massachusetts, vermont, connecticut, maine]).
11
12
13
14
region(sur_oeste) :estado(X),
member(X,[florida, mississippi, alabama, nueva_orleans]).
95
96
�������� ��������
Figura 28: Pato común del norte (Mallard) en ilustración de John James Audubon (1838).
Otras aves necesitarán de predicados múltiples para ser identificada. Por ejemplo,
el Mallard (Anas platyrhynchos) macho, o pato común del norte (Figura 28), tiene la
cabeza verde con un anillo blanco; la hembra tiene la cabeza café moteada:
1
2
3
4
5
6
7
8
ave(mallard):familia(pato),
voz(graznido),
cabeza(verde).
ave(mallard) :familia(pato),
voz(graznido),
cabeza(cafe_moteada).
Basicamente, cualquier situación del libro de las aves norte americanas puede ser expresado fácilmente en Prolog. Las reglas expresadas forman la base de conocimientos
del SE. El único punto débil del programa es su interfaz con el usuario, que requiere
que los datos sean introducidos como hechos del programa.
�.�
�������� ��� �������
El sistema puede mejorarse considerablemente si proveemos una interfaz para el usuario, que pregunte por la información cuando esto sea necesario, en lugar de forzar al
usuario a introducirla como hechos del programa.
Antes de pensar en un predicado pregunta, es necesario entender la estructura de
los datos que serán preguntados. Todos los datos, manejandos hasta ahora, han sido
de la forma atributo–valor. Por ejemplo, los atributos del pato del norte Mallard, son
mostrados en el Cuadro 2.
atributo
familia
voz
cabeza
valor
pato
graznido
verde
�.� �������� ��� �������
Cuadro 2: Atributos valor para el mallard
Esta es una de las representaciones más simples usadas en los SE, pero es suficiente
para muchas aplicaciones. Existen representaciones más expresivas, como los tripletes
objeto–atributo–valor, o las redes semánticas, o los marcos.
Como estamos programando en Prolog, la riqueza del lenguaje puede usarse directamente en el SE. Por ejemplo, los pares atributo–valor han sido representados
como predicados unarios de la forma atributo(valor): familia(pato), voz(graznido),
cabeza(verde). Pero en region/1 usamos la membresia en listas para su definición.
Usaremos el predicado pregunta para determinar con ayuda del usuario, cuando
un par atributo–valor es verdadero. El SE debe modificarse para determinar que atributos son verificables por el usuario. Esto se logra con reglas para los atributos que
llaman a pregunta:
1
2
3
4
5
6
7
8
9
fosas(X) :- pregunta(fosas,X).
habitat(X) :- pregunta(habitat,X).
pico(X) :- pregunta(pico,X).
tamano(X) :- pregunta(tamano,X).
come(X) :- pregunta(come,X).
pies(X) :- pregunta(pies,X).
alas(X) :- pregunta(alas,X).
cuello(X) :- pregunta(cuello,X).
color(X) :- pregunta(color,X).
Ahora, si el SE tiene como meta probar color(blanco), llamará a pregunta/2 en
lugar de consultar su base de conocimientos. Si pregunta(color, blanco) tiene éxito,
entonces color(blanco) también lo tiene. La pregunta es como sigue:
1
2
3
4
pregunta(Atrib,Val):write(Atrib:Val),
write(’? ’),
read(si).
El predicado read/1 tendrá éxito sólo si el usuario responde “si” y falla si el usuario
responde cualquier otra cosa. Ahora el programa puede ser ejecutado sin datos de
trabajo iniciales. La misma llamada a ave/1 inicia la consulta al SE.
?- ave(X).
fosas:externas_tubulares? si.
habitat:mar? si.
pico:gancho? si.
tamano:grande? si.
alas:muy_largas? si.
color:blanco? si.
X = albratros_laysan
Yes.
97
98
�������� ��������
El problema con este enfoque es que si el usuario responde “no” a la última pregunta, la regla para ave(albratros_laysan) falla, llevandonos a un backtracking. De
esta manera el SE nos preguntaría nuevamente información que ya sabe. De alguna
manera deberíamos implementar un predicado pregunta que recuerde lo preguntado.
?- ave(X).
fosas:externas_tubulares? si.
habitat:mar? si.
pico:gancho? si.
tamano:grande? si.
alas:muy_largas? si.
color:blanco? no.
fosas:externas_tubulares? si.
habitat:mar? si.
pico:gancho? si.
tamano:grande? si.
alas:muy_largas? si.
color:obscuro? si.
X = albratros_patas_negras
Yes.
Definiremos un nuevo predicado conocido/3 que nos ayude a recordar las respuestas del usuario. Las respuestas no se guardarán directamente en memoria, sino
que serán guardadas dinámicamente con asserta/1 cuando pregunta provea información nueva para el SE:
1
pregunta(A,V) :- conocido(si,A,V), !.
2
3
pregunta(A,V) :- conocido(_,A,V), !, fail.
4
5
6
7
8
9
10
pregunta(A,V) :write(A:V),
write’? : ’),
read(Resp),
asserta(conocido(Resp,A,V)),
Resp == si.
También es posible utilizar menues contextuales para el caso de atributos multivariados. La idea es que para atributos de un solo valor, la interfaz por el usuario
pregunte una sola vez:
1
2
3
4
5
6
pregunta(A,V) :not(multivariado(A)),
conocido(si,A,V2),
V \== V2,
!,
fail.
Una guía sobre los valores válidos para un atributo se implementa con el predicado
menu_pregunta que trabaja de manera análoga a pregunta:
1
2
3
4
�.� �� ����� ������
tamaño(X) :menu_pregunta(tamaño, X, [grande, mediano, pequeño]).
color(X) :menu_pregunta(color,X,[blanco,verde,cafe,negro]).
La definición de menu_pregunta/3 es:
1
2
3
4
5
6
7
8
menu_pregunta(A,V,MenuLista) :write(’Cual es el valor para ’,
write(A), write(’? ’), nl,
write(MenuLista),nl,
read(Resp),
checar(Resp,A,V,MenuLista),
asserta(conocido(si,A,X)),
X == V.
9
10
11
checar(X,A,V,MenuLista) :member(X,MenuLista), !.
12
13
14
15
�.�
checar(X,A,V,MenuLista) :write(’Ese valor no es válido, intente nuevamente’), nl,
menu_pregunta(A,V,MenuLista).
�� ����� ������
El ejemplo de identificación de aves tiene dos partes: una base de conocimientos, que
incluye la información específica sobre las aves; y los predicados para controlar la
interfaz con el usuario. Al separar estas dos partes, podemos crear un shell de SE. Con
ello podemos crear un nuevo SE que identifique, por ejemplo, peces y reutilizar la
parte de control de la interfaz.
Un cambio mínimo es necesario para separar las dos partes de nuestro SE. Necesitamos un predicado de alto nivel que inicie el proceso de identificación. Puesto que
no sabemos de antemano lo que el SE va a identificar, el shell buscará satisfacer un
predicado llamado meta. Cada base de conocimiento deberá tener definido meta/1,
por ejemplo, para el caso de identificación de aves tendríamos:
1
meta(X) :- ave(X).
como primer predicado en la base de conocimientos aves.
El shell tendrá un predicado solucion/0 que llevará a cabo labores de mantenimiento del SE, para luego resolver la meta/1:
1
2
3
4
5
solucion :abolish(conocido,3),
define(conocido,3),
meta(X),
write(’La respuesta es: ’), write(X), nl.
6
7
8
solucion :write(’No se encontró una respuesta.’), nl.
99
100
�������� ��������
El predicado Prolog abolish/2 se usa para eliminar los hechos definidos previamente con conocido/3, cada vez que una consulta se va a ejecutar. Esto permite al usuario
ejecutar solucion multiples veces en una sola sesión. El predicado define/2 permite
indicarle a Prolog que conocido estará definido en el SE, de forma que no cause error
la primera utilización de este predicado. Este predicado puede variar dependiendo
de la versión de Prolog utilizada.
De esta manera tenemos que el SE ha sido dividido en dos partes. Los predicados
en el shell son:
• solucion,
• pregunta,
• menu_pregunta,
• los predicados auxiliares de éstos.
Los predicados en la base de conocimientos son:
• meta,
• las reglas sobre el conocimiento del SE,
• las reglas sobre los atributos provistos por el usuario,
• las declaraciones de los atributos multi-variados.
Para usar este shell en Prolog, tanto el shell como la base de conocimientos deben
ser cargados:
1
2
3
4
5
6
?- consult(shell).
yes
?- consult(’aves.kb’).
yes
?- solucion.
fosas_nasales : externas_tubulares ? ...
�.�.� REPL
El shell puede ser mejorado construyendo un ciclo de comandos read-eval-print loop.
Para ello definiremos el predicado se:
1
2
3
4
5
6
7
8
se :bienvenida,
repeat,
write(’> ’),
read(X),
do(X),
X == quit.
�.� �������������� ����� ����� ��� �������������
Interfaz del Usuario
se
pregunta
menu_pregunta
Máquina de inferencia
solucion
cargar
Base de Conocimientos
Memoria de trabajo
meta
reglas
mulivaluado
preguntado
conocido
Figura 29: El shell del SE.
9
10
11
bienvenida :write(’Este es el shell de su SE.’), nl,
write(’Escriba: cargar, consultar, o salir en el prompt.’), nl
12
13
14
do(cargar) :cargar_bd, !.
15
16
17
do(consultar) :solucion, !.
18
19
do(salir).
20
21
22
23
24
do(X) :write(X),
write(’ no es un comando válido.’), nl,
fail.
25
26
27
28
29
cargar_bd :write(’Nombre del archivo: ’),
read(F),
reconsult(F).
La arquitectura obtenida de esta forma se muestra en la figura 29.
�.�
�������������� ����� ����� ��� �������������
Como hemos mencionado, el encadenamiento hacía adelante resulta conveniente cuando los problemas a resolver son del tipo selección estructurada, como en el ejemplo
de la clasificación de aves. Sin embargo, en además de que hemos asumido que la
información completa está disponible para resolver el problema, también hemos asu-
101
102
�������� ��������
mido que no hay incertidumbre, ni el los datos provistos por el usuario, ni en las
reglas de los expertos. Por ejemplo, el albatros puede ser observado en la bruma, con
lo que sería difícil precisar si su color es blanco u obscuro. Es de esperar que un SE
que maneje incertidumbre, pueda contender con este tipo de problemas.
Desarrollaremos un shell que permita manejar reglas con incertidumbre y encadenamiento de ellas hacía atrás. Evidentemente, este SE tendrá un formato de reglas
propio, diferente a las reglas de Prolog, y por lo tanto, una máquina de inferencia
propia.
�.�.� Factores de certidumbre
La forma más común de trabajar con la incertidumbre consiste en asignar un factor de
certidumbre a cada pieza de información en el SE. La máquina de inferencia deberá
mantener los factores de incertidumbre conforme el proceso de inferencia se lleve a
cabo.
Por ejemplo, asumamos que los factores de certidumbre (precedidos por cf) son
enteros entre -100 (definitivamente falso) y +100 (definitivamente verdadero). La siguiente base de conocimientos en formato del SE está diseñada para diagnosticar un
auto que no enciende. Esto ilustra el comportamiento de los factores de certidumbre:
1
GOAL problema.
2
3
4
5
6
RULE 1
IF not arranca AND
bateria_mala
THEN problema is bateria.
7
8
9
10
RULE 2
IF luces_debiles
THEN bateria_mala cf 50.
11
12
13
14
RULE 3
IF radio_debil
THEN bateria_mala cf 50.
15
16
17
18
19
RULE 4
IF arranca AND
olor_gasolina
THEN problema is fuga cf 80.
20
21
22
23
24
RULE 5
IF arranca AND
indicador_gasolina is vacio
THEN problema is tanque_vacio cf 90.
25
26
27
28
29
RULE 6
IF arranca AND
indicador_gasolina is bajo
THEN problema is tanque_vacio cf 30.
30
31
32
ASK arranca
MENU (si no)
33
�.� �������������� ����� ����� ��� �������������
PROMPT ’Su motor arranca? ’.
34
35
36
37
ASK luces_debiles
MENU (si no)
PROMPT ’Sus luces están débiles? ’.
38
39
40
41
ASK radio_debile
MENU (si no)
PROMPT ’Su radio está débil? ’.
42
43
44
45
ASK olor_gasolina
MENU (si no)
PROMPT ’Huele a gasolina?’.
46
47
48
49
ASK indicador_gasolina
MENU (vacio, medio, lleno)
PROMPT ’Que indica al aguja de gasolina? ’.
Por el momento la inferencia usaría encadenamiento hacía atrás, similar al que usa
Prolog. La regla GOAL indica que el proceso buscará un valor para problema. La
regla 1 causará que la sub-meta bateria_mala sea procesada, etc. Observen que las
reglas especifican también factores de certidumbre. Las reglas 2 y 3 proveen evidencia
de que la batería está en mal estado, pero ninguna es conclusiva al respecto.
Un diálogo con este sistema sería como sigue:
1
2
3
4
5
6
7
8
9
10
11
consultar, reiniciar, cargar, listar, trazar, cómo, salida
: consultar
Su motor arranca?
: si
Huele a gasolina?
: si
Qué indica la aguja de la gasolina?
: vacio
problema-tanque-vacio-cf-90
problema-fuga-cf-80
problema resuelto
Observen que a diferencia de Prolog, el sistema no se detiene al encontrar el primer
posible valor para problema. En este caso se computan todos los valores razonables
para problema y se reporta el valor de certidumbre asociado a estas soluciones. Recordemos que estos factores de certidumbre no son probabilidades, solo ponderan
de alguna manera las respuestas.
De igual manera, el usuario podría ofrecer factores de certidumbre sobre sus respuestas, por ejemplo:
1
2
3
4
5
: consultar
...
Huele a gasolina?
si cf 50
...
Existen diversas maneras de capturar el concepto de factor de certidumbre, pero
todas ellas deben de confrontar las mismas situaciones básicas:
103
104
�������� ��������
• Reglas cuyas conclusiones son inciertas,
• Reglas cuyas premisas son inciertas,
• Datos provistos por el usuario inciertos,
• Combinación de premisas inciertas con conclusiones inciertas,
• Actualizar los factores de incertidumbre en los datos almacenados en el espacio
de trabajo,
• Establecer un umbral sobre el cual las premisas se consideran conocidas.
�.�.� Factores de certidumbre à la MYCIN
MYCIN [5], uno de los SE más conocidos en IA, introduce factores de certidumbre
diseñados para producir resultados intuitivos desde la perspectiva de los expertos.
Revisemos el uso de estos factores por casos. El más simple, sería aquel donde las
premisas son totalmente ciertas:
1
2
arranca cf 100.
olor_gas cf 100.
disparan la regla 4 y por tanto, problema fuga cf 80 deberá agregarse al almacén
de trabajo. Sin embargo, este es un caso poco probable. Normalmente no estamos
totalmente seguros de las premisas de una regla y lo normal sería tener hechos como:
1
2
arranca cf 80.
olor_gas cf 50.
Cuando esto sucede, la incertidumbre en las premisas de la regla debe combinarse
con las de la conclusión de la misma de la siguiente manera:
CF = CFregla ⇥ mı́n CFpremisa/100
Dado el ejemplo, la regla 4 se activaría con un cf = 50 (el mínimo de las dos
premisas) y dada la fórmula anterior, agregaríamos problema fuga cf 40 al almacén
de trabajo.
Para que una regla dispare, su factor de certidumbre debe superar un umbral que
normalmente se fija en 20. Así que bajo la definición anterior, la regla 4 dispararía. Si
tuviésemos olor_gas cf 15, entonces la regla no dispararía.
Ahora consideren el caso donde hay más de una regla que da soporte a cierta
conclusión. En ese caso, cada una de las reglas que disparan contribuirá al factor de
certidumbre de la conclusión. Si una regla dispara y la conclusión ya se encontraba
en el almacén de trabajo, las siguientes reglas aplican:
CF(X, Y) = X + Y(100 - X)/100.
Ambos X, Y > 0
CF(X, Y) = X + Y/1 - mı́n(|X|, |Y|).
CF(X, Y) = -CF(-X, -Y).
Uno de X, Y < 0
Ambos X, Y < 0
�.� �������������� ����� ����� ��� �������������
Por ejemplo, si disparamos la regla 2 (luces débiles) con su premisa sin incertidumbre, tendríamos que agregar al almacén de trabajo bateria_mala cf 50. Luego
si disparamos la regla 3 (radio débil), el factor de certidumbre de este hecho debe
modificarse a bateria_mala cf 75. Lo cual resulta intuitivo (hay más evidencia de
que la batería tiene problemas). Lo que también resulta intuitivo es que necesitamos
programar nuestra propia máquina de inferencia.
�.�.� Formato de las reglas
Como programaremos nuestra propia máquina de inferencia, podemos elegir la estructura de hechos y reglas. Las reglas tendrán la estructura general:
regla(Nombre, Premisas, Conclusion).
El Nombre opera solo como un identificador de la regla. El lado izquierdo de la
misma Premisas implica al lado derecho Conclusion (conclusión). Como usaremos
encadenamiento hacía atrás, cada regla será usada para validar una pieza de información, de manera el RHS contiene una meta con su factor de certidumbre asociado:
conclusion(Meta, CF).
mientras que las premisas toman la forma de una lista de metas:
premisas(ListaMetas).
Las metas serán representadas, para comenzar, como pares atributo–valor:
av(Atributo, Valor).
cuando Atributo y Valor son átomos, la estructura general de las reglas se ve
como:
1
2
3
regla(Nombre,
premisas( [av(A1,V1), av(A2,V2), ... ] ),
conclusion(av(Attr,Val), CF)).
Por ejemplo, la regla 5 quedaría representada como:
1
2
3
regla(5,
premisas([av(arranca,si), av(indicador_gasolina,vacio)]),
conclusion(av(problema,fuga), 80)).
Estas reglas no son fáciles de leer, pero tienen una estructura adecuada para ser
procesadas por Prolog. Otras herramientas de Prolog como las gramáticas de cláusula
definitivas (DCG) o la definición de operadores, puede ayudarnos a simplificar esta
representación.
105
106
�������� ��������
�.�.� La máquina de inferencia
Dado el formato de las reglas del SE deseamos que la inferencia tome en cuenta los
siguientes aspectos:
• Combine los factores de certidumbre como se indico anteriormente.
• Mantenga el espacio de trabajo con la información actualizada con las nuevas
evidencias obtenidas.
• Encontrar toda la información acerca de un atributo en particular cuando se
pregunte por él, y poner esa información en el espacio de trabajo.
Primero, los hechos serán almacenados en la memoria de trabajo de Prolog, con el
siguiente formato:
1
hecho(av(A,V),CF).
De forma que un predicado meta/2 haría la llamada para resolver un problema
dado en estos términos. Por ejemplo, en el caso del arranque del auto, tendríamos
como meta:
1
?- meta(av(problema,X),CF).
El predicado meta/2 debe de contender con tres casos:
• El atributo–valor se conoce de antemano;
• Existen reglas para deducir el atributo–valor;
• Se debe preguntar al usuario.
El sistema puede diseñarse para preguntar al usuario automáticamente por el valor
de un atributo, ante la ausencia de reglas; o bien, se puede declarar que atributos
pueden ser preguntados al usuario. Este último enfoque hace que el manejo de la
base de conocimientos sea más explícito y provee mayor control sobre los diálogos
usuario – SE. Podemos definir un predicado pregunta/2 para declarar el atributo a
preguntar y la frase para ello:
1
pregunta(pais_residencia,’¿En qué país vive? ’).
Veamos ahora los tres casos para meta/2. El primero de ellos ocurre cuando la
información ya está en la memoria de trabajo:
1
2
3
meta(av(Atr,Val),CF) :hecho( av(Atr,Val), CF),
!.
El segundo caso se da cuando el valor del atributo no se encuentra en la memoria
de trabajo, pero el es posible preguntar por ello al usuario:
�.� �������������� ����� ����� ��� �������������
1
2
3
4
5
6
meta(av(Atr,Val), CF) :\+ hecho( av(Atr,_),_),
pregunta(Atr,Msg),
preguntar(Atr,Msg),
!,
meta(av(Atr,Val), CF).
Para ello, el predicado preguntar/2 interroga al usuario. El usuario responde con
un valor para la atributo Atr y un factor de certidumbre asociado CF. El mensaje Msg
da la información necesaria para guiar al usuario en estas consultas:
1
2
3
4
5
preguntar(Atr,Msg) :write(Msg),
read(Val),
read(CF),
asserta(fact(av(Atr,Val),CF)).
El tercer caso para meta/2 es cuando el valor del atributo es desconocido, pero se
puede deducir usando las reglas definidas en el sistema, en ese caso la llamada es:
1
2
meta(Meta,CFactual) :buscaReglas(Meta,CFactual).
Esta llamada hace uso de la máquina de inferencia que diseñaremos para nuestro
SE con incertidumbre. El factor de certidumbre se etiqueta como actual, porque es
posible que cambie de valor al ir aplicando las reglas definidas en el sistema.
El predicado buscaReglas/2 se encarga de encontrar aquellas reglas cuya conclusión unifica con la Meta en cuestión y de actualizar el factor de certidumbre con base
en las premisas de estas reglas. Si la Meta es un hecho conocido, no hay nada que
hacer, sólo regresar true:
1
2
3
4
5
6
7
8
buscaReglas(Meta,CFactual) :regla(N, premisas(ListaPremisas),
conclusion(Meta,CF)),
probar(ListaPremisas,Contador),
ajustar(CF,Contador,NuevoCF),
actualizar(Meta,NuevoCF,CFactual),
CFactual == 100,
!.
9
10
11
buscaReglas(Meta,CF) :hecho(Meta,CF).
Dada una lista de premisas pertenecientes a una regla encontrada para satisfacer
la Meta del SE, es necesario que buscaReglas/2 las pruebe. Para ello definimos
probar/2:
1
2
probar(ListaPremisas, Contador) :probAux(ListaPremisas, 100, Contador).
3
4
probAux([],Contador,Contador).
107
108
�������� ��������
5
6
7
8
probAux([Premisa1|RestoPremisas],ContadorActual,Contador) :meta(Premisa1,CF,Cont),
Cont >= 20,
probAux(RestoPremisas,Cont,Contador).
El ajuste de los factores de certidumbre se lleva a cabo de la siguiente manera:
1
2
3
ajustar(CF1, CF2, CF) :X is CF1 * CF2 / 100,
int_redondear(X,CF).
4
5
6
7
int_redondear(X,I) :X >= 0,
I is integer(X + 0.5).
8
9
10
11
int_redondear(X,I) :X < 0,
I is integer(X - 0.5).
La actualización de la memoria de trabajo se lleva a cabo de la siguiente manera:
1
2
3
4
5
6
actualizar(Meta,NuevoCF,CF) :hecho(Meta,ViejoCF),
combinar(NuevoCF,ViejoCF,CF),
retract(hecho(Meta,ViejoCF)),
asserta(hecho(Meta,CF)),
!.
7
8
9
actualizar(Meta,CF,CF) :asserta(hecho(Meta,CF)).
10
11
12
13
14
15
combinar(CF1, CF2, CF) :CF1 >= 0,
CF2 >= 0,
X is CF1 + CF2*(100 - CF1)/100,
int_redondear(X,CF).
16
17
18
19
20
21
22
combinar(CF1,CF2,CF) :CF1 < 0,
CF2 < 0,
X is -( -CF1-CF2*(100+CF1)/100),
int_redondear(X,CF).
23
24
25
26
27
28
29
combinar(CF1,CF2,CF) :(CF1 < 0 ; CF2 < 0),
(CF1 > 0 ; CF2 > 0),
abs_minimum(CF1,CF2,MCF),
X is 100 * (CF1 + CF2) / (100 - MCF),
int_redondear(X,CF).
�.� �������������� ����� ����� ��� �������������
�.�.� Interfaz con el usuario
La interfaz con el usuario es muy parecida a la definida en la sección anterior. Se
incluyen predicados auxiliares necesarios en su definición:
1
2
3
4
5
6
7
se :repeat,
write(’consultar, cargar, salir’), nl,
write(’: ’),
read_line(X),
ejec(X),
X == salir.
8
9
10
11
ejec(consultar) :metas_principales,
!.
12
13
14
15
ejec(cargar) :cargar_reglas,
!.
16
17
ejec(salir).
18
19
%% % Auxiliares
20
21
22
23
24
25
26
metas_principales :meta_principal(Atr),
principal(Atr),
imprime_meta(Atr),
fail.
metas_principales.
27
28
29
30
31
principal(Atr) :meta(av(Atr,Val,CF)),
!.
principal(_) :- true.
32
33
34
35
36
37
38
39
40
41
imprime_meta(Atr) :nl,
hecho(av(Atr,Val), CF),
CF >= 20,
salidap(av(Atr,Val),CF), nl
fail.
imprime_meta(Atr) :write (’Meta: ’), write(Attr), write(’ solucionada.’),
nl, nl.
42
43
44
45
46
47
48
49
salidap(av(Atr,Val),CF) :output(Atr,Val,ListaImprimir),
write(Atr-’cf’-CF),
imprimeLista(ListaImprimir), !.
salidap(av(Atr,Val),CF) :write(Atr-Val-’cf’-CF).
109
110
�������� ��������
50
51
52
53
imprimeLista([]).
imprimeLista([X|Xs]) :write(X),
imprimeLista(Xs).
Descargar