UD05 · Notebook 3 — Clasificación de animales¶
En esta práctica implementaremos el ejemplo de un sistema experto visto en teoría, donde de acuerdo con las características dadas, el sistema experto debe adivinar qué animal es. En este caso, el sistema experto solo tendrá en cuenta los animales que se muestran en el esquema de teoría, es decir, los animales que se muestran en la siguiente imagen:

Utilizaremos el módulo experta de Python para implementar un sistema experto encadenamiento hacia adelante. Este módulo nos permitirá crear un sistema experto de una manera muy simple, ya que solo tendremos que definir las reglas y hechos que nuestro sistema experto utilizará.
Si implementamos un motor de inferencia, tendremos problemas de rendimiento cuando el sistema experto tenga muchas reglas, ya que debemos verificar todas las reglas para cada hecho. Por el contrario, experta tiene una implementación muy optimizada (utilizando el algoritmo RETE) que nos permitirá crear sistemas expertos con muchas reglas y hechos.
A continuación instalaremos e importaremos las librerías necesarias para la práctica.
%pip install experta
# experta (2019) rompe en Python 3.10+ porque `collections.Mapping` se eliminó.
# Parche de tres líneas antes de importar (ya documentado en la UD00):
import collections, collections.abc
if not hasattr(collections, 'Mapping'):
collections.Mapping = collections.abc.Mapping
collections.Iterable = collections.abc.Iterable
collections.MutableMapping = collections.abc.MutableMapping
from experta import *
Requirement already satisfied: experta in ./venv-experta/lib/python3.14/site-packages (1.9.4) Requirement already satisfied: frozendict==1.2 in ./venv-experta/lib/python3.14/site-packages (from experta) (1.2) Requirement already satisfied: schema==0.6.7 in ./venv-experta/lib/python3.14/site-packages (from experta) (0.6.7)
Note: you may need to restart the kernel to use updated packages.
Definición del sistema experto¶
Definiremos nuestro sistema experto como una clase que herede de KnowledgeEngine. Cada regla se define mediante una función con la anotación @Rule, que especifica cuándo debe ejecutarse la regla. Dentro de la regla, podemos agregar nuevos hechos a través de la función declare, y agregar estos hechos causará más reglas que se ejecutan a través del motor de la inferencia.
El contenido de @Rule es una expresión lógica que se evalúa a True o False. Esta expresión lógica puede contener hechos, operadores lógicos (AND, OR, NOT) y operadores de comparación (==,!=,<,>,<=, >=). Los hechos son objetos de la clase de Fact, que pueden tener atributos que se especifican como parámetros de la clase Fact. Por ejemplo, el hecho Fact('mamífero', pelo=True) tiene el atributo 'pelo' con valor 'True'.
Adicionalmente, podemos usar la función MATCH para especificar que un atributo puede tener cualquier valor.Por ejemplo, Fact('mamífero', pelo=MATCH.pelo) es un hecho que tiene el atributo pelopara cualquier valor.
class Animales(KnowledgeEngine):
# Reglas
@Rule(OR(
AND(Fact('dientes afilados'), Fact('uñas'), Fact('ojos mirando al frente')),
Fact('carnivoro')))
# Si el animal tiene dientes afilados, uñas y ojos mirando hacia adelante, o come carne, entonces es un carnivoro
def carnivoro(self):
# Agregamos el hecho de 'Carnivoro' al sistema experto
self.declare(Fact('carnivoro'))
@Rule(OR(Fact('pelo'), Fact('da leche')))
# Si el animal tiene pelo o da leche, entonces es un mamífero
def mamifero(self):
self.declare(Fact('mamifero'))
@Rule(Fact('mamifero'),
OR(Fact('tiene pezuñas'), Fact('rumia')))
# Si el animal es un mamífero y tiene pezuñas o rumia entonces es un ungulado
def ungulado(self):
self.declare('ungulado')
@Rule(OR(Fact('plumas'), AND(Fact('vuela'), Fact('pone huevos'))))
# Si el animal tiene plumas o vuela y pone huevos, entonces es un pájaro
def pajaro(self):
self.declare('pajaro')
@Rule(Fact('mamifero'), Fact('carnivoro'),
Fact(color='marron-rojizo'),
Fact(pattern='manchas oscuras'))
# Si el animal es un mamífero de color marron-rojizo con manchas oscuras, entonces es un mono
def mono(self):
self.declare(Fact(animal='mono'))
@Rule(Fact('mamifero'), Fact('carnivoro'),
Fact(color='marron-rojizo'),
Fact(pattern='rayas oscuras'))
#Si el animal es un mamífero marron-rojizo con rayas oscuras, entonces es un tigre
def tigre(self):
self.declare(Fact(animal='tigre'))
@Rule(Fact('ungulado'),
Fact('cuello largo'),
Fact('largas piernas'),
Fact(pattern='manchas oscuras'))
# Si el animal tiene un cuello largo, ungulado, piernas largas y con manchas oscuras, entonces es una jirafa
def jirafa(self):
self.declare(Fact(animal='jirafa'))
@Rule(Fact('ungulado'),
Fact(pattern='rayas oscuras'))
# Si el animal es ungulado y tiene rayas oscuras, entonces es una cebra
def cebra(self):
self.declare(Fact(animal='cebra'))
@Rule(Fact('pajaro'),
Fact('cuello largo'),
Fact('no vuela'),
Fact(color='blanco y negro'))
# Si el animal es un pájaro, con cuello largo, no vuela, es de color blanco y negro, entonces es un avestruz
def avestruz(self):
self.declare(Fact(animal='avestruz'))
@Rule(Fact('pajaro'),
Fact('nada'),
Fact('no vuela'),
Fact(color='blanco y negre'))
# Si el animal es un pájaro, nada, no vuela, es de color blanco y negro, entonces es un pingüino
def pinguino(self):
self.declare(Fact(animal='pinguino'))
@Rule(Fact('pajaro'),
Fact('vuela bien'))
# Si el animal es un pájaro y vuela bien, entonces es un albatros
def albatros(self):
self.declare(Fact(animal='albatros'))
@Rule(Fact(animal=MATCH.a))
# Si està definit l'animal, llavors el mostrem
def print_result(self, a):
print(f"El animal es un {a}")
def añadir_hechos(self, hechos):
# Añadiremos los hechos pasados como parámetro (hechos) al sistema experto
# Usaremos esta función para inicializar el sistema experto.
for f in hechos:
self.declare(f)
Pruebas del sistemas experto¶
Una vez que hemos definida la base de conocimiento, inicializamos la memoria de trabajo con los hechos iniciales, y luego llamamos al método run() para llevar a cabo la inferencia. Podemos ver como resultado que se añaden nuevos hechos inferido a la memoria de trabajo, incluido el evento final sobre el animal (si hemos configurado todos los hechos iniciales correctamente).
Veamos un ejemplo en el que inicializamos el sistema experto con los hechos pelo, dientes afilados, uñas i ojos mirando al frente. Estos hechos son suficientes para inferir que el animal es un carnivoro.
# Creamos una instancia de nuestro sistema experto
animales = Animales()
# Restablecer el sistema experto. Requerido para volver al estado inicial.
animales.reset()
# Añadimos los hechos iniciales.
animales.añadir_hechos(
[Fact('pelo'), Fact('dientes afilados'), Fact('uñas'), Fact('ojos mirando al frente')]
)
# Ejecutar el sistema experto
animales.run()
# Mostramos la base de hechos hasta ahora.
animales.facts
FactList([(0, InitialFact()),
(1, Fact('pelo')),
(2, Fact('dientes afilados')),
(3, Fact('uñas')),
(4, Fact('ojos mirando al frente')),
(5, Fact('carnivoro')),
(6, Fact('mamifero'))]) Podemos ver cómo se ha inferido el hecho carnivoro de los hechos iniciales y cómo se ha inferido el hecho mamífero del hecho pelo.
Agregaremos los hechos Marrón-Rojizo primero y Manchas Oscuras más tarde. Veremos cómo primero no se infiere ningún animal, y luego se infiere el hecho de los hechos Mamífero, Carnivoro, Marrón-Rojizo y Manchas Oscuras.
animales.añadir_hechos(
[Fact(color='marron-rojizo')]
)
animales.run()
animales.facts
FactList([(0, InitialFact()),
(1, Fact('pelo')),
(2, Fact('dientes afilados')),
(3, Fact('uñas')),
(4, Fact('ojos mirando al frente')),
(5, Fact('carnivoro')),
(6, Fact('mamifero')),
(7, Fact(color='marron-rojizo'))]) animales.añadir_hechos(
[Fact(pattern='manchas oscuras')]
)
animales.run()
animales.facts
El animal es un mono
FactList([(0, InitialFact()),
(1, Fact('pelo')),
(2, Fact('dientes afilados')),
(3, Fact('uñas')),
(4, Fact('ojos mirando al frente')),
(5, Fact('carnivoro')),
(6, Fact('mamifero')),
(7, Fact(color='marron-rojizo')),
(8, Fact(pattern='manchas oscuras')),
(9, Fact(animal='mono'))])