实现动物专家系统 人工智能初学者课程的一个例子。
在此示例中,我们将实现一个简单的基于知识的系统,以根据某些物理特征来确定动物。该系统可以用以下AND-OR树来表示(这是整个树的一部分,我们可以轻松添加更多规则):
我们自己的带有后向推理的专家系统 shell 让我们尝试定义一种简单的基于产生式规则的知识表示语言。我们将使用 Python 类作为关键字来定义规则。基本上有 3 种类型的类:
Ask 代表需要向用户询问的问题。它包含一组可能的答案。
If 代表一条规则,它只是一个语法糖,用来存储规则的内容
AND / OR 是表示树的 AND/OR 分支的类。他们只是将参数列表存储在里面。为了简化代码,所有功能都定义在父类 Content 中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class Ask (): def __init__ (self,choices=['y' ,'n' ] ): self.choices = choices def ask (self ): if max ([len (x) for x in self.choices])>1 : for i,x in enumerate (self.choices): print ("{0}. {1}" .format (i,x),flush=True ) x = int (input ()) return self.choices[x] else : print ("/" .join(self.choices),flush=True ) return input () class Content (): def __init__ (self,x ): self.x=x class If (Content ): pass class AND (Content ): pass class OR (Content ): pass
在我们的系统中,工作记忆将包含作为属性值对的事实列表。知识库可以定义为一本大字典,它将动作(新的事实应插入工作记忆中)映射到条件,以 AND-OR 表达式表示。此外,一些事实可以被 Ask 编辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 rules = { 'default' : Ask(['y' ,'n' ]), 'color' : Ask(['red-brown' ,'black and white' ,'other' ]), 'pattern' : Ask(['dark stripes' ,'dark spots' ]), 'mammal' : If(OR(['hair' ,'gives milk' ])), 'carnivor' : If(OR([AND(['sharp teeth' ,'claws' ,'forward-looking eyes' ]),'eats meat' ])), 'ungulate' : If(['mammal' ,OR(['has hooves' ,'chews cud' ])]), 'bird' : If(OR(['feathers' ,AND(['flies' ,'lies eggs' ])])), 'animal:monkey' : If(['mammal' ,'carnivor' ,'color:red-brown' ,'pattern:dark spots' ]), 'animal:tiger' : If(['mammal' ,'carnivor' ,'color:red-brown' ,'pattern:dark stripes' ]), 'animal:giraffe' : If(['ungulate' ,'long neck' ,'long legs' ,'pattern:dark spots' ]), 'animal:zebra' : If(['ungulate' ,'pattern:dark stripes' ]), 'animal:ostrich' : If(['bird' ,'long nech' ,'color:black and white' ,'cannot fly' ]), 'animal:pinguin' : If(['bird' ,'swims' ,'color:black and white' ,'cannot fly' ]), 'animal:albatross' : If(['bird' ,'flies well' ]) }
为了执行向后推理,我们将定义 Knowledgebase 类。它将包含:
工作记忆 - 将属性映射到值的字典
知识库 rules 采用上面定义的格式
两种主要方法是:
get 获取属性的值,必要时进行推理。例如, get(‘color’) 将获取颜色槽的值(它会询问是否需要,并将该值存储在工作内存中以供以后使用)。如果我们询问 get(‘color:blue’) ,它会询问颜色,然后根据颜色返回 y / n 值。
eval 执行实际的推理,即遍历 AND/OR 树、评估子目标等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 class KnowledgeBase (): def __init__ (self,rules ): self.rules = rules self.memory = {} def get (self,name ): if ':' in name: k,v = name.split(':' ) vv = self.get(k) return 'y' if v==vv else 'n' if name in self.memory.keys(): return self.memory[name] for fld in self.rules.keys(): if fld==name or fld.startswith(name+":" ): value = 'y' if fld==name else fld.split(':' )[1 ] res = self.eval (self.rules[fld],field=name) if res!='y' and res!='n' and value=='y' : self.memory[name] = res return res if res=='y' : self.memory[name] = value return value res = self.eval (self.rules['default' ],field=name) self.memory[name]=res return res def eval (self,expr,field=None ): if isinstance (expr,Ask): print (field) return expr.ask() elif isinstance (expr,If): return self.eval (expr.x) elif isinstance (expr,AND) or isinstance (expr,list ): expr = expr.x if isinstance (expr,AND) else expr for x in expr: if self.eval (x)=='n' : return 'n' return 'y' elif isinstance (expr,OR): for x in expr.x: if self.eval (x)=='y' : return 'y' return 'n' elif isinstance (expr,str ): return self.get(expr) else : print ("Unknown expr: {}" .format (expr))
现在让我们定义我们的动物知识库并进行咨询。请注意,此通话将询问您问题。您可以通过键入 y / n 来回答是非问题,或者通过指定数字 (0..N) 来回答具有较长多项选择答案的问题。1 2 3 4 kb = KnowledgeBase(rules) kb.get('animal' )
使用 PyKnow 进行前向推理 在下一个示例中,我们将尝试使用知识表示库之一 PyKnow 来实现前向推理。 PyKnow 是一个用于在 Python 中创建前向推理系统的库,其设计类似于经典的旧系统 CLIPS。
我们也可以自己实现前向链接,不会出现很多问题,但简单的实现通常效率不高。为了更有效地匹配规则,使用了特殊算法 Rete。
1 2 3 4 5 6 7 8 import sys!{sys.executable} -m pip install git+https://github.com/buguroo/pyknow/
我们将我们的系统定义为 KnowledgeEngine 子类的类。每个规则都由带有 @Rule 注释的单独函数定义,该注释指定何时触发规则。在规则内部,我们可以使用 declare 函数添加新事实,添加这些事实将导致前向推理引擎调用更多规则。
下面这个代码片断是一个使用了expertsystem库的专家系统的例子,专门用于对动物进行分类。下面是对每一行代码及参数的详细解释: 定义类 Animals: class Animals(KnowledgeEngine): 这定义了一个名为Animals的类,它继承自KnowledgeEngine。KnowledgeEngine是构建专家系统的基础,在这里用来处理和推导知识或事实(Facts)。 定义规则: 专家系统中的每一个规则允许系统根据提供的事实(Facts)推导出新的结论。 规则定义的通用元素: @Rule: 是一个装饰器,用于定义一个规则。每个规则都会在满足特定条件时触发某些动作。 OR, AND: 是逻辑运算符,用于组合条件。AND表示所有条件都必须满足,OR表示只要满足其中一个条件即可。 Fact: 用于声明事实。事实是专家系统用来推理的基础,通过匹配规则定义中的条件来进行逻辑推导。 定义的规则: carnivor: 表示如果一个动物具有锋利的牙齿、爪子、前视眼的特征,并吃肉,那么它会被归类为食肉动物(carnivor)。 mammal: 如果一个动物有毛发或能够哺乳,它会被认为是哺乳动物(mammal)。 hooves: 如果一个哺乳动物拥有蹄子或反刍,它会被认定为有蹄类(ungulate)。 bird: 如果一个动物拥有羽毛,或者能够飞行并下蛋,它会被认为是鸟类。 monkey, tiger, giraffe, zebra, ostrich, pinguin, albatross: 这些规则根据动物的特定特征,如颜色、图案、身体特征等,将动物分类为特定种类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 class Animals (KnowledgeEngine ): @Rule(OR( AND(Fact('sharp teeth' ),Fact('claws' ),Fact('forward looking eyes' ) ), Fact('eats meat' ) ) ) def carnivor (self ): self.declare(Fact('carnivor' )) @Rule(OR(Fact('hair' ),Fact('gives milk' ) ) ) def mammal (self ): self.declare(Fact('mammal' )) @Rule(Fact('mammal' ), OR(Fact('has hooves' ),Fact('chews cud' ) ) ) def hooves (self ): self.declare('ungulate' ) @Rule(OR(Fact('feathers' ),AND(Fact('flies' ),Fact('lays eggs' ) ) ) ) def bird (self ): self.declare('bird' ) @Rule(Fact('mammal' ),Fact('carnivor' ), Fact(color='red-brown' ), Fact(pattern='dark spots' ) ) def monkey (self ): self.declare(Fact(animal='monkey' )) @Rule(Fact('mammal' ),Fact('carnivor' ), Fact(color='red-brown' ), Fact(pattern='dark stripes' ) ) def tiger (self ): self.declare(Fact(animal='tiger' )) @Rule(Fact('ungulate' ), Fact('long neck' ), Fact('long legs' ), Fact(pattern='dark spots' ) ) def giraffe (self ): self.declare(Fact(animal='giraffe' )) @Rule(Fact('ungulate' ), Fact(pattern='dark stripes' ) ) def zebra (self ): self.declare(Fact(animal='zebra' )) @Rule(Fact('bird' ), Fact('long neck' ), Fact('cannot fly' ), Fact(color='black and white' ) ) def straus (self ): self.declare(Fact(animal='ostrich' )) @Rule(Fact('bird' ), Fact('swims' ), Fact('cannot fly' ), Fact(color='black and white' ) ) def pinguin (self ): self.declare(Fact(animal='pinguin' )) @Rule(Fact('bird' ), Fact('flies well' ) ) def albatros (self ): self.declare(Fact(animal='albatross' )) @Rule(Fact(animal=MATCH.a ) ) def print_result (self,a ): print ('Animal is {}' .format (a)) def factz (self,l ): for x in l: self.declare(x)
每个@Rule装饰的方法都会在其条件被满足时执行。例如,如果一个动物有“锋利的牙齿”,“爪子”,“前视眼”并且“吃肉”,那么carnivor规则会触发,并通过执行self.declare(Fact(‘carnivor’))将这个动物分类为食肉动物(carnivor)。
一旦我们定义了知识库,我们就会用一些初始事实填充我们的工作记忆,然后调用 run() 方法来执行推理。您可以看到结果是新的推断事实被添加到工作记忆中,包括有关动物的最终事实(如果我们正确设置所有初始事实)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ex1 = Animals() ex1.reset() ex1.factz([ Fact(color='red-brown' ), Fact(pattern='dark stripes' ), Fact('sharp teeth' ), Fact('claws' ), Fact('forward looking eyes' ), Fact('gives milk' )]) ex1.run() ex1.facts