INTELIGENCIA EN REDES DE COMUNICACIONES INMACULADA LUENGO LÓPEZ Mª ESTHER MARTÍN REBOLLO IRC Práctica final ÍNDICE Introducción_______________________________________________3 Objetivo de la práctica _____________________________________3 Desarrollo de la práctica____________________________________4 Resultados obtenidos ______________________________________7 Aprendizaje _______________________________________________9 Código del programa______________________________________11 Bibliografía_______________________________________________15 Página 2 de 15 IRC Práctica final Introducción En esta práctica hemos decidido implementar un programa en PROLOG para adivinar animales a partir de preguntas que se le van haciendo al usuario. La decisión de utilizar PROLOG para el desarrollo de esta práctica se debe a que es un lenguaje sencillo que proporciona una sintaxis para la construcción de sistemas expertos (en nuestro caso un sistema experto que identifique a los animales a partir de sus características). PROLOG es un lenguaje lógico declarativo que se basa en cláusulas de Horn. Sus principales características son que se trata de una programación dinámica, y tanto las reglas como los objetivos, son también datos; y la asunción de un mundo cerrado, todo lo que no figura explícitamente como un hecho y tampoco se puede deducir es falso. Otra característica es que el orden de las cláusulas cambia el resultado final. En esta práctica vamos a analizar el problema de un árbol de decisión con aprendizaje. Objetivo de la práctica El fin de esta práctica ha sido desarrollar un programa en PROLOG que adivine el animal que ha pensado el usuario mediante el empleo de una serie de preguntas sobre las características del mismo a las que el jugador debe responder afirmativa o negativamente. Para ello, hemos definido un conjunto de reglas que debe verificar cada uno de los animales que el programa conoce y, a partir de estas reglas, se le formularán al usuario preguntas sobre las características que tiene el animal que ha pensado. También se ha contemplado la posibilidad de que el programa aprenda nuevos animales, que el usuario podría introducir cuando el programa no adivinase el animal que éste había pensado. Para ello se debe introducir, además de dicho animal, una pregunta que lo diferencie del animal que ha respondido erróneamente el programa. Página 3 de 15 IRC Práctica final Desarrollo de la práctica Para la implementación del programa deseado, en primer lugar nos tendremos que plantear la estructura que va a tener nuestra lista de animales. Esta estructura será un árbol, en el que en las hojas se encontraran los distintos animales, y llegaremos a ellos por medios de las distintas características diferenciadoras que tiene cada uno. Siendo una de las principales características su clasificación en: reptiles, anfibios, mamíferos, aves y peces. Para realizar esta clasificación se tienen que tener en cuenta que las preguntas a realizar nos tienen que caracterizar una de las hojas frente a otra, y así poder llegar a la hoja deseada sin ningún tipo de ambigüedad. La lista de los animales que se han tenido en cuenta para el desarrollo de esta práctica es: rana, salamandra, serpiente, tortuga, cocodrilo, jirafa, conejo, hombre, oso, tigre, león, águila, pato, gallina, avestruz, pingüino, carpa y pez espada. En primer lugar, y para mostrar con mayor claridad el fundamento del programa, mostramos el árbol que vamos a desarrollar: Página 4 de 15 IRC Práctica final Página 5 de 15 IRC Práctica final Comenzamos analizando el código desarrollado. En primer lugar, vamos a utilizar una función que se llamará sera(Animal), que determinará principalmente el orden en el que se deben ir comprobando los distintos animales (¿será una rana?...), de izquierda a derecha en la figura del árbol. Para cada uno de los animales se realizarán las preguntas sobre las características que debe cumplir, comenzando por el animal situado más a la izquierda del árbol y por la pregunta del nodo raíz. Se seguirán verificando las características para un mismo animal siempre que se responda afirmativamente a las preguntas y se pasará a comprobar las características del animal siguiente (el situado inmediatamente a la derecha del animal actual) cada vez que se responda negativamente a una de las preguntas. De esta forma, cuando lleguemos a una hoja del árbol el programa dará como solución un animal que cumpla todas las preguntas anteriormente realizadas. El animal que no cumpla ninguna de las características preguntadas será el que está en la hoja situada más a la derecha del árbol, que en nuestro caso es el pez espada, y no necesitará verificar ninguna condición, ya que simplemente es el que no cumple ninguna de las anteriores. Mediante estas reglas podremos realizar una primera clasificación de los animales en anfibios, reptiles, mamíferos, aves y peces. Las características que nos permiten clasificarlos dentro de estos grupos se harán aparte para que el código del programa sea más claro y más general. Ahora pasamos a comentar la función verificar(caracteristica) que ya hemos nombrado anteriormente. En esta función decimos que si se cumple la característica el resultado será true, y si no se cumple será fail, y pasamos a llamar a la función preguntar(caracteristica). En la función preguntar será donde realizaremos la pregunta sobre la característica determinada a verificar y en la que almacenaremos la respuesta. Según ésta sea afirmativa o negativa, avanzaremos hacia una u otra rama del árbol. Esto lo implementaremos con la ayuda de la función assert, que nos ayudara a ir almacenando las respuestas a las distintas características, para evitar repetir al usuario preguntas ya realizadas anteriormente, como las relacionadas con el tipo de animal (anfibio, reptil…), y así poder seguir avanzando en el árbol. Debido a la utilización de la función assert en este punto, necesitaremos que las funciones cumple y no_cumple, con las que la utilizamos sean dinámicas. Además tendremos que borrar los valores almacenamos, con la ayuda Página 6 de 15 IRC Práctica final de la función retract, una vez que hayamos mostrado al usuario el animal adivinado, para lo que llamaremos a la función borrar en la parte principal del programa. Si olvidásemos realizar este borrado, en la siguiente ejecución comenzaríamos directamente dando como animal adivinado el obtenido en el caso anterior, sin haber realizado ninguna pregunta. Además una vez adivinado el animal, se nos ofrecerá la oportunidad de seguir jugando, en lugar de tener que volver a ejecutar el programa. Resultados obtenidos Ahora vamos a probar el funcionamiento de nuestro programa. Vamos a pensar un animal de los que conoce, por ejemplo la jirafa. El programa nos irá haciendo preguntas recorriendo el árbol de la siguiente forma: Página 7 de 15 IRC Práctica final Página 8 de 15 IRC Práctica final La captura del programa obtenida es la siguiente: Podemos ver que el programa va haciendo las preguntas en el orden indicado en el árbol hasta adivinar que el animal que se había pensado era la jirafa. Aprendizaje Nos hemos planteado también la posibilidad de que el programa pueda aprender nuevos animales. Para implementarlo, hemos empleado la función asserta que nos permite introducir nuevas reglas al principio de la base de datos. El programa sólo podrá aprender cuando no acierte el animal que había pensado el usuario. Por lo tanto, lo primero que tenemos que hacer una vez identificado el animal por el programa es preguntarle si hemos acertado dicho animal. De no ser así, tendremos que preguntarle al usuario cuál era el animal que había pensado y que nos indique una característica de éste que permita al programa diferenciarlo del animal que el había ofrecido como solución. Una vez conocidos el animal y una característica que lo distinga, lo añadimos mediante la función asserta. Esta nueva regla se tratará de igual forma que las definidas de forma explícita en el programa. Página 9 de 15 IRC Práctica final Veamos un ejemplo de aprendizaje de nuestro programa. Para ello, hemos pensado en el mono, que no está incluido en la base de datos del programa. La captura del programa es la siguiente: Ahora podemos comprobar que ha insertado mono en la base de datos: Página 10 de 15 IRC Práctica final Y además, que puede identificar este animal del ofrecido anteriormente como solución: En mayor inconveniente que tiene el método que hemos seguido es que ya no nos permite realizar las preguntas en el orden inicial, pero no es demasiado importante porque sigue adivinando correctamente los animales inicialmente introducidos. Código del programa /* ADIVINO EL ANIMAL EN EL QUE ESTAS PENSANDO Para empezar el juego, teclea: " ?- start." */ /*Comienza el juego*/ /*Para implementar el juego partiremos de un árbol en el que tendremos todos los animales que podemos adivinar y las relaciones entre ellos*/ start :nl, write('PARA ADIVINAR EL ANIMAL EN EL QUE ESTAS PENSANDO, '), nl, write('RESPONDE A LAS SIGUIENTES PREGUNTAS'), nl, nl, sera(Animal), /*En primer lugar llamamos a la función hipótesis. Página 11 de 15 IRC Práctica final Con esta función empezaremos en el nodo raíz e iremos avanzando en el árbol */ /*Cuando lleguemos a una hoja del árbol, habremos encontrado la respuesta*/ nl, nl, write('****************************************'), nl, write('Creo que el animal es '), write(Animal), nl, write('****************************************'), nl, nl, /*Aprendizaje de nuestro programa*/ /*Comprobamos si hemos acertado el animal*/ write('¿He acertado? (s/n) '), read(Respuesta), nl, /*En el caso de no haber acertado, intentamos introducir una regla para su aprendizaje*/ ( (Respuesta == s) -> borrar, seguir_jugando ; write('¿Qué animal es? '), read(Respuesta1), nl, write('Dime una pregunta para '), write(Respuesta1), write(' que lo diferencie de '), write(Animal), write(': '), read(Respuesta2), nl, nl, asserta( (sera(Respuesta1) :- Animal, verificar(Respuesta2)) ) , borrar, seguir_jugando). /* Hipótesis de animales que se van a comprobar */ sera(rana) :- rana, !. sera(salamandra) :- salamandra, !. sera(serpiente) :- serpiente, !. sera(tortuga) :- tortuga, !. sera(cocodrilo) :- cocodrilo, !. sera(jirafa) :- jirafa, !. sera(conejo) :- conejo, !. sera(hombre) :- hombre, !. Página 12 de 15 IRC Práctica final sera(oso) :- oso, !. sera(tigre) :- tigre, !. sera(leon) :- leon, !. sera(aguila) :- aguila, !. sera(pato) :- pato, !. sera(gallina) :- gallina, !. sera(avestruz) :- avestruz, !. sera(pinguino) :- pinguino, !. sera(carpa) :- carpa, !. sera(pez_espada):- pez_espada. /* Reglas con las que identificaremos los distintos animales */ /*Anfibios*/ rana :- sangre_fria, anfibio, verificar(salta). salamandra :- sangre_fria, anfibio. /*Reptiles*/ serpiente :- sangre_fria, verificar(se_arrastra). tortuga :- sangre_fria, verificar(tiene_caparazon). cocodrilo :- sangre_fria. /*Mamíferos*/ jirafa :- mamifero, herbivoro, verificar(tiene_cuello_largo). conejo :- mamifero, herbivoro. hombre :- mamifero, omnivoro, verificar(razona). oso :- mamifero, omnivoro. tigre :- mamifero, verificar(tiene_rayas). leon :- mamifero. /*Aves*/ Página 13 de 15 IRC Práctica final aguila :- ave, voladora, verificar(es_ave_rapaz). pato :- ave, voladora. gallina :- ave, verificar(es_domestico). avestruz :- ave, verificar(corre_veloz). pinguino :- ave. /*Peces*/ carpa :- verificar(vive_rio). pez_espada. /* Reglas con las que clasificaremos los animales en: anfibios, reptiles, mamíferos, aves y peces */ /* Para ello utilizaremos la función verificar */ sangre_fria :- verificar(tiene_sangre_fria). anfibio :- verificar(vive_tierra_y_agua). mamifero :- verificar(tiene_pelo). herbivoro :- verificar(es_herbivoro). omnivoro :- verificar(es_omnivoro). ave :- verificar(tiene_alas). voladora :- verificar(vuela). /* Función verificar, desde aquí llamaremos a la función que preguntara por cada característica */ verificar(S) :(cumple(S) -> true ; (no_cumple(S) -> fail ; preguntar(S))). /* Para avanzar por el árbol, se irán realizando preguntas con las distintas características. Esto lo haremos con la función preguntar*/ preguntar(Pregunta) :write('¿Tiene el animal la siguiente característica: '), write(Pregunta), write('? (s/n) '), Página 14 de 15 IRC Práctica final read(Respuesta), nl, /*Avanzamos en función de la respuesta a la característica, hacia una rama u otra del árbol */ /*Con assert, guardamos el camino seguido */ ( (Respuesta == s) -> assert(cumple(Pregunta)) ; assert(no_cumple(Pregunta)), fail). seguir_jugando :/*Ofrecemos la oportunidad de volver a jugar otra vez*/ write('¿Quieres seguir jugando? '), read(Respuesta3), ( (Respuesta3 == s) -> start ; nl, nl, write('ESPERO QUE HAYAS DISFRUTADO JUGANDO CONMIGO'), nl, nl, write('ADIOS'), nl). /*Hace dinámicas las funciones que se van a añadir con assert*/ :- dynamic cumple/1,no_cumple/1,sera/1,verificar/1. /* Borramos todos los valores almacenados con assert que indican el camino seguido en el árbol */ borrar :- retract(cumple(_)),fail. borrar :- retract(no_cumple(_)),fail. borrar. Bibliografía - Prolog Francis Giannesini, Henry Kanoui y otros. Ed. Addison-Wesley Iberoamericana, 1989 - Manual online de swi-prolog Jan Wielemaker - Principles of programming languages XXI Wael Aboelsaadat Página 15 de 15