UD05 · Notebook 1 — Sistema experto con Python y Experta¶
Introducción¶
Filosofía¶
Nuestro objetivo es implementar una alternativa Python a CLIPS, lo más compatible posible. Con el objetivo de facilitar al programador CLIPS la transferencia de todos sus conocimientos a esta plataforma.
Características¶
- Compatible con Python 3.
- Implementación pura de Python.
- Matcher basado en el algoritmo RETE.
Diferencia entre CLIPS y Experta¶
- CLIPS es un lenguaje de programación, Experta es una biblioteca de Python. Esto impone algunas limitaciones a las construcciones que podemos hacer (especialmente al LHS de una regla).
- CLIPS está escrito en C, Experta en Python. Es de esperar un impacto notable en el rendimiento.
- En CLIPS agregas hechos usando
assert, en Pythonassertes una palabra clave, por lo que usamosdeclareen su lugar.
Lo básico¶
Un sistema experto es un programa capaz de emparejar un conjunto de hechos con un conjunto de reglas para esos hechos y ejecutar algunas acciones basadas en las reglas de coincidencia.
Hechos¶
Los hechos son la unidad básica de información de Experta. Son utilizados por el sistema para razonar sobre el problema.
Enumeremos algunos datos sobre Hechos, entonces… metahechos;)
Necesitamos preparar el Notebook importando experta
%pip install git+https://github.com/openmotics/om-experta.git
from experta import *
- La clase Fact es una subclase de dict.
f = Fact(a=1, b=2)
f['a']
- Por lo tanto un Hecho no mantiene un orden interno de elementos.
Fact(a=1, b=2) # Order is arbirary :O
- A diferencia de dict , puedes crear un Hecho sin claves (solo valores), y
Factcreará un índice numérico para tus valores.
f = Fact('x', 'y', 'z')
f[0]
- Puede mezclar valores autonuméricos con valores-clave, pero primero se deben declarar los autonuméricos:
f = Fact('x', 'y', 'z', a=1, b=2)
f[1]
f['b']
Reglas¶
En Experta una regla es invocable.
Las reglas tienen dos componentes, LHS (lado izquierdo) y RHS (lado derecho).
- El LHS describe (usando patrones) las condiciones en las que la regla debe ejecutarse (o activarse).
- El RHS es el conjunto de acciones a realizar cuando se activa la regla.
Para que un Hecho coincida con un Patron, todas las restricciones del patrón deben ser True cuando el Hecho se evalúa con respecto a él.
class MyFact(Fact):
pass
@Rule(MyFact()) # This is the LHS
def match_with_every_myfact():
"""This rule will match with every instance of `MyFact`."""
# This is the RHS
pass
@Rule(Fact('animal', family='felinae'))
def match_with_cats():
"""
Match with every `Fact` which:
* f[0] == 'animal'
* f['family'] == 'felinae'
"""
print("Meow!")
Puede utilizar operadores lógicos para expresar condiciones LHS complejas.
class User(Fact):
pass
@Rule(
AND(
OR(User('admin'),
User('root')),
NOT(Fact('drop-privileges'))
)
)
def the_user_has_power():
"""
The user is a privileged one and we are not dropping privileges.
"""
enable_superpowers()
Hechos vs Patrones¶
La diferencia entre hechos y patrones es pequeña. De hecho, los patrones son solo hechos que contienen elementos condicionales de patrón (Pattern Conditional Elements PCE) en lugar de datos regulares. Se utilizan únicamente en el LHS de una regla.
Si no proporciona el contenido de un patrón como PCE, Experta incluirá el valor en un Literal PCE automáticamente.
Además, no puede declarar ningún Hecho que contenga un PCE; si lo hace, recibirá una excepción.
ke = KnowledgeEngine()
ke.declare(Fact(L("hi")))
DefFacts (Hechos iniciales?)¶
La mayoría de las veces, los sistemas expertos necesitan que esté presente un conjunto de hechos para que el sistema funcione. Este es el propósito del decorador DefFacts.
@DefFacts()
def needed_data():
yield Fact(best_color="red")
yield Fact(best_body="medium")
yield Fact(best_sweetness="dry")
Todos los DefFacts dentro de KnowledgeEngine se llamarán cada vez que se llame al método de reset().
Base de hechos (KnowledgeEngine)¶
Este es el lugar donde ocurre toda la magia. El primer paso es crear una subclase y usar Rule para decorar sus métodos. Después de eso, puede crear una instancia, completarlo con hechos y finalmente ejecutarlo.
class Greetings(KnowledgeEngine):
@DefFacts()
def _initial_action(self):
yield Fact(action="greet")
@Rule(Fact(action='greet'),
NOT(Fact(name=W())))
def ask_name(self):
self.declare(Fact(name=input("What's your name? ")))
@Rule(Fact(action='greet'),
NOT(Fact(location=W())))
def ask_location(self):
self.declare(Fact(location=input("Where are you? ")))
@Rule(Fact(action='greet'),
Fact(name=MATCH.name),
Fact(location=MATCH.location))
def greet(self, name, location):
print("Hi %s! How is the weather in %s?" % (name, location))
engine = Greetings()
engine.reset() # Prepare the engine for the execution.
engine.run() # Run it!
engine = KnowledgeEngine()
engine.reset()
engine.declare(Fact(score=5))
engine.facts
El mismo hecho no se puede declarar dos veces a menos que Facts.duplication esté establecido en True.
retract¶
Elimina un hecho existente de la lista de hechos.
Tanto el índice como el hecho se pueden utilizar con
retract.
engine.retract(1)
engine.facts
modify¶
Retira algún hecho de la lista de hechos y declara uno nuevo con algunos cambios. Los cambios se pasan como argumentos.
engine.declare(Fact(color="red"))
engine.facts
engine.modify(engine.facts[2], color='yellow', blink=True)
engine.facts
duplicate¶
Agrega un hecho nuevo a la lista de hechos utilizando un hecho existente como plantilla y agregando algunas modificaciones.
engine.facts
engine.duplicate(engine.facts[2], color="orange", blink=False)
engine.facts
Procedimiento de ejecución del motor.¶
Este es el proceso habitual para ejecutar knowledgeEngine.
- Por supuesto, se debe crear una instancia de la clase.
- Se debe llamar al método de reinicio (
reset()):- Esto declara el hecho especial
InitialFact, necesario para que algunas reglas funcionen correctamente. - Declare todos los hechos obtenidos por los métodos decorados con
@DefFacts.
- Esto declara el hecho especial
- Se debe llamar al método de ejecución (
run()). Esto inicia el ciclo de ejecución.
Ciclo de ejecución¶
En un estilo de programación convencional, el programador define explícitamente el punto de inicio, el punto de finalización y la secuencia de operaciones. Con Experta, no es necesario definir el flujo del programa de forma tan explícita. El conocimiento (Reglas) y los datos (Hechos) se separan y KnowledgeEngine se utiliza para aplicar el conocimiento a los datos.
El ciclo de ejecución básico es el siguiente:¶
- Si se ha alcanzado el límite de activación de la regla, se detiene la ejecución.
- Se selecciona para su ejecución la regla superior de la agenda. Si no hay reglas en el orden del día, se detiene la ejecución.
- Se ejecutan las acciones RHS de la regla seleccionada (se llama al método). Como resultado, las reglas pueden activarse o desactivarse. Las reglas activadas (aquellas reglas cuyas condiciones se cumplen actualmente) se colocan en la agenda. La ubicación en la agenda está determinada por la prominencia de la regla y la estrategia actual de resolución de conflictos. Las reglas desactivadas se eliminan de la agenda.
Diferencia entre DefFacts y declarar¶
Ambos se utilizan para declarar hechos en la instancia del motor, pero:
declareagrega los hechos directamente a la memoria de trabajo.- Los generadores declarados con
DefFactsse llaman mediante el método reset y todos los hechos generados se agregan a la memoria de trabajo usandodeclare.
Referencia¶
El siguiente diagrama muestra todos los componentes del sistema y las relaciones entre ellos.

from experta import *
class Nested(KnowledgeEngine):
@DefFacts()
def _initial_action(self):
yield Fact(name="scissors", against={"scissors": 0, "rock": -1, "paper": 1})
yield Fact(name="paper", against={"scissors": -1, "rock": 1, "paper": 0})
yield Fact(name="rock", against={"scissors": 1, "rock": 0, "paper": -1})
@Rule(Fact(name=MATCH.name, against__scissors=1, against__paper=-1))
def what_wins_to_scissors_and_losses_to_paper(self, name):
print(name)
engine = Nested()
engine.reset() # Prepare the engine for the execution.
engine.run() # Run it!