机器学习入门:数学原理解析及算法实践
上QQ阅读APP看书,第一时间看更新

1.6 动手实践

专家系统有很多开源的实现,比如CLIPS[1]和PyKe[2]。由于专家系统的核心是谓词逻辑描述的规则,所以,各种实现都离不开描述和解析这些规则。用于描述计算机执行过程的一般程序设计语言并不适用于描述专家系统中的规则,因此,人们设计了专门的语言用于专家系统。比如,逻辑程序设计语言Prolog就是一种用来描述谓词逻辑的计算机语言。在专家系统流行的年代,Prolog成为人工智能的专属计算机语言。在PyKe的实现中,规则和知识库的描述方式就受到了Prolog的启发。而CLIPS则采用了类似函数式语言LISP的语法来描述专家系统中的规则。

作为计算机语言的研究者和学习者,这些语言还是值得了解的。如果希望使用上述这些专家系统来完成某种工程产品上的需求,那就更加值得深入了解这些语言和系统了。然而,作为动手实践的入门过程,我们希望更加专注于理解专家系统的原理,而避免陷于一种全新的而且并不常用的计算机语言。为此,我们实现一个极为简化的专家系统。

1.6.1 简化的专家系统

在这个简化的专家系统的逻辑规则中,只包含“而且”这种关系(与运算)。所有谓词都只描述单一个体的属性,每个谓词只接受一个个体变元。量词也只有全称量词。因此,我们在描述规则的时候,可以省去变元和量词。

下面的一组字符串描述了前面示例中的专家系统。由于没有“非”这个用于否定的逻辑运算符,我们略去了与“不会飞行”相关的规则。

rules=['有羽毛=>鸟类',
   '产乳=>哺乳动物',
   '鸟类and会飞行=>飞禽',
   '飞禽and脖子长=>仙鹤',
   '哺乳动物and吃草=>食草动物',
   '食草动物and脖子长=>长颈鹿']

这些规则的格式非常固定,很容易解析。我们写一个函数用来解析这些规则,以便用于后续的推理过程。

def parse_rules(rules):
   parsed_rules=[]
   for rule in rules:
       conditions, result=rule.split('=>')
       conditions=conditions.split(' and ')
       parsed_rules.append((conditions, result))
   return parsed_rules

1.6.2 正向推理

在上面规则的基础上,就可以进行正向推理了。正向推理的过程就是,不断尝试应用规则产生新的事实,直到无法产生新的事实为止。

#正向推理过程,以规则和事实为输入
def forward_chain(rules, facts):
   has_new_fact=True
   #如果有新的事实产生
   #就可以不断重复正向推理的过程
   while has_new_fact:
      has_new_fact=False
      for rule in rules:
         #检查前置条件是否都在已知事实之中
         condition_met=all([x in facts for x in rule[0]])
         if not condition_met:continue
         has_new_fact=rule[1] not in facts
         #如果可以推出新的事实,把它打印出来
         if has_new_fact:
            facts.append(rule[1])
            print(rule[1])
            break

下面我们就可以试验正向推理的过程了。输入“会飞行,有羽毛,脖子长”,我们得到,这是“鸟类,飞禽,仙鹤”。

forward_chain(parse_rules(rules), ['会飞行','有羽毛','脖子长'])
#下面是输出的结果
#鸟类
#飞禽
#仙鹤

1.6.3 逆向推理

逆向推理的过程以验证某个假设为目标,所以,需要增加一个参数作为假设的目标。

#逆向推理过程,以规则、事实和假设的目标为输入
def backward_chain(rules, facts, hypo):
   #如果假设已经在事实之中,可以终止推理
   if hypo in facts:return
   some_rule_applies=False
   for rule in rules:
      if rule[1] !=hypo:continue
      some_rule_applies=True
      condition_met=all([x in facts for x in rule[0]])
      #如果条件已经满足,可以终止推理
      if condition_met:
         facts.append(rule[1])
         return
      #否则,递归检查不满足的条件
      for fact in rule[0]:
         if fact in facts:continue
         backward_chain(rules, facts, fact)
   #如果没有任何规则可以应用,需要向用户求证假设
   if not some_rule_applies:
      print('{0}?'.format(hypo))

在逆向推理过程中,我们从假设的目标出发,寻找能够产生该目标的规则,递归地验证规则的前置条件是否成立。如果前置条件可以由另外的规则生成,则递归检查那些规则;如果前置条件不能由任何规则生成,就需要向用户问询确认,以求证假设。

下面是一个应用的实例。假设我们知道某种动物脖子长,我们想验证它是否是长颈鹿,那么需要验证哪些条件呢?逆向推理的结果告诉我们,需要检查该动物是否产乳,是否吃草。

backward_chain(parse_rules(rules), ['脖子长'], '长颈鹿')
#下面是输出的结果
#产乳?
#吃草?

以上,我们实现了在这个简化的专家系统上进行正向和逆向推理。读者可以尝试修改规则解析和推理引擎,引入逻辑“或”和“非”运算。读者还可以尝试修改规则定义,将它应用于其他场景。这是专家系统的优势,移植到其他场景不需要修改推理引擎,只需要重新定义规则。然而,对于很多场景来说,完善自洽的规则并不容易定义出来,这个时候,统计规律就变得更加有效。于是我们就要进入机器学习的世界了。


[1]CLIPS开源软件地址:https://sourceforge.net/projects/clipsrules

[2]PyKe开源软件地址:http://pyke.sourceforge.net