实验简介
本次实验题目是《智能系统》课程实验2-产生式系统推理。仅供学习和参考,希望能对你思路有所启发。实验算法仅是个人理解,不保证正确,请勿抄袭。
一 实验题目
产生式系统推理
二 实验目的
1 熟悉和掌握产生式系统的构成和运行机制;
2 掌握基于规则推理的基本方法和技术,掌握正确的正向推理和逆向推理方法;
3 熟悉在具体问题中如何实现正向推理和逆向推理的求解流程 。
三 实验要求
1 以产生式推理模式为基础,实现小型动物分类系统,推理方法可以采用正向推理或反向推理;
2 要求表示规则的语言必须能体现出规则前提和结论的对应关系,必须能体现出前提和结论中的逻辑关系;
3 要求能对规则库进行动态地增加、删除和修改操作;
4 要求用界面显示要查询的初始事实、推理方法、推理中用到的规则和结论。
四 数据结构
用Python字典数据类型存储规则库数据rules。
用Python列表数据类型存储动物特征选项。
算法中的facts,conclusions用Python集合数据类型。
五 实验算法
1 正向推理
初始化facts,conclusions为空集合;
选择特征,选择的特征同时加到facts集合和conclusions集合里;
循环:
遍历规则库rules每个规则rule:
如果rule前提是facts的子集且rule没被用过:
使用规则rule进行推导并输出
移除conclusions在rule前提里有的特征
将rule结论放入facts和conclusions
如果本次循环没有使用到任何规则,就跳出循环
输出事实集合facts
输出结论结合conclusions
2 反向推理
初始化facts,conclusions为空集合;
选择目标特征,加到facts集合里;
循环:
遍历规则库rules每个规则rule:
如果rule结论属于facts且rule没被用过:
使用规则rule进行反向推导并输出
将rule前提放入facts和conclusions
如果本次循环没有使用到任何规则,就跳出循环
输出事实集合facts
输出结论结合conclusions
增加、删除、修改规则较简单,直接在rules集合增加、删除、修改即可。
六 实验结果
1 运行测试
初始选择操作界面如下。
因为“修改规则”可由“增加规则”和“删除规则”组合操作得到,所以略去了“修改规则”选项,以简化项目,更专注于算法的设计。
我们先测试“正向推理”,输入1,回车,进入正向推理界面,输入编号选择动物特征。
输入2 10 14 15(有毛发,吃肉,黄褐色,有黑色条纹)q,正向推理输出结果如下。显然,根据推理规则得出,输入的特征完全符合老虎的特征,输出的结论是老虎。
输入2 10 14 15 4(有毛发,吃肉,黄褐色,有黑色条纹,会飞)q,正向推理输出结果如下。输入的这几个特征,比老虎多了一个“会飞”,但规则数据里又没有完美符合这些特征的选项,于是推出了两个结论“老虎”和“会飞”,可以解释为拥有这个特征的动物是“会飞的老虎”。由于没有更好的结论,只能输出这样的组合结论。
输入2 10 14(有毛发,吃肉,黄褐色)q,正向推理输出结果如下。输入的这几个特征,比老虎少了一个“有黑色条纹”,我们无法断定它是老虎,推理得到两个结论“黄褐色”和“食肉动物”,可以解释为拥有这个特征的动物是“黄褐色的食肉动物”。由于没有更好的结论,只能输出这样的组合结论。
接下来我们尝试一下反向推理。
我们选择特征时界面和正向推理一样。我们输入86(鹰)试试。得到的结论有“鸟”、“吃肉”、“会飞”、“上嘴鹰钩”、“卵生”、“有羽毛”、“生蛋”、“有爪”。反向推理比较简单,输入其它的特征进行反向推理的情况也类似。
接下来我们来增加规则。输入“中分 背带裤 打篮球”,再输入结论“坤”。显示规则添加成功。
再看看现有规则,可以看到尾部增加了“(‘中分’, ‘背带裤’, ‘打篮球’): ‘坤’”这么一个规则,说明添加成功。
我们来尝试删除它。
显示删除成功,再看一次现有规则,可以看到结尾已经没有“坤”的判定规则。
实验结果显示,程序能够顺利地进行正向推理、反向推理、增加规则、删除规则,以及由增加删除操作可以组合出修改规则操作。
2 性能分析
本算法的时间复杂度主要取决于两个循环:选择动物特征的循环和推理的循环。
选择动物特征的循环:这个循环的时间复杂度取决于用户的输入次数,我们可以将其表示为O(n),其中n是用户输入的次数。
正向推理和反向推理的循环:这两个循环都遍历所有的规则,对于每个规则,它检查该规则是否满足特定条件,这个操作的时间复杂度是O(m),其中m是规则的数量。然后,它可能会从结论集中删除元素,这个操作的时间复杂度是O(k),其中k是结论集的大小。因此,这两个循环的总时间复杂度是O(m*k)。
因此,整个代码的时间复杂度是O(n + m*k)。这是一个粗略的估计,实际的时间复杂度可能会因为Python内部的优化和数据结构的特性而有所不同。
七 实验总结及体会
正向推理是最复杂的,因为它可能会出现自相矛盾的事实。比如在老虎的特征之上再加上“会飞”,出现了一个规则库中不存在的生物体,对此,我们只能输出组合结论“会飞的老虎”。
写完代码后,我给助教老师检查了两次。反向推理以及规则的增删改没有什么问题,就只有正向推理出问题。
第一次在老虎的特征基础上加上“会飞”特征,我的程序推导出“老虎”,但现实中显然不存在有“会飞”特征的“老虎”,我的算法需要修改。
第二次给助教老师检查,我使用了“概率”或“特征符合率”来描述推导出最优结论与特征的符合率。如果完全符合“老虎”的特征,结论是“老虎”,特征符合率是1.0。在老虎的特征基础上加上“会飞”特征,结论是“老虎”,“特征符合率”是0.875,可以解释为拥有这个特征的动物最接近“老虎”,符合87.5%“老虎”的特征。但助教老师又指出这个方法有缺点。输入特征“有毛发”、“吃肉”、“黄褐色”,推出结论是0.8特征符合率的“食肉动物”结论,而显然符合这个特征的动物必定是“食肉动物”。这时候特征符合率就并不能很好地进行推理了。
下课后,我决定再换一种算法,推导出组合结论,让“会飞”和“老虎”的结论同时存在,而不只给出最优结论,因为根本无法推导出一个正确的最优结论。将结论解释为“会飞的老虎”或者“黄褐色的食肉动物”之类的组合结论,从而弥补规则库的局限。实验证明效果确实不错,我决定将它作为最终算法。
本次实验共设计了三种算法,既锻炼了我的算法设计能力,也让我更深刻地理解了产生式系统推理的过程。
实验代码
# 创建规则库
rules = {
("有奶",): "哺乳动物",
("有毛发",): "哺乳动物",
("有羽毛",): "鸟",
("会飞", "生蛋"): "鸟",
("哺乳动物", "有爪", "有犬齿", "目盯前方"): "食肉动物",
("哺乳动物", "吃肉"): "食肉动物",
("哺乳动物", "有蹄"): "有蹄动物",
("有蹄动物", "反刍食物"): "偶蹄动物",
("食肉动物", "黄褐色", "有黑色条纹"): "老虎",
("食肉动物", "黄褐色", "有黑色斑点"): "金钱豹",
("有蹄动物", "长腿", "长脖子", "黄褐色", "有暗斑点"): "长颈鹿",
("有蹄动物", "白色", "有黑色条纹"): "斑马",
("鸟", "不会飞", "长腿", "长脖子", "黑白色"): "驼鸟",
("鸟", "不会飞", "会游泳", "黑白色"): "企鹅",
("鸟", "善飞", "不怕风浪"): "海燕",
# 扩充规则库
("有毛发", "有奶", "善跳跃", "唇裂"): "兔子",
("有毛发", "有奶", "善捕鼠", "脚有肉垫"): "猫",
("有毛发", "有奶", "鼻子上有角", "褐色", "皮糙肉后", "皮糙肉厚", "有蹄"): "犀牛",
("有毛发", "有奶", "黑眼圈", "四肢短小"): "熊猫",
("鸟", "上嘴鹰钩", "会模仿人说话"): "鹦鹉",
("鸟", "腿短", "嘴扁平", "善潜水游泳"): "鸭子",
("鸟", "上嘴鹰钩", "有爪", "吃肉"): "鹰",
("有羽毛", "卵生", "善游泳", "嘴扁平", "腿短"): "鸭子",
("有羽毛", "卵生", "善潜水游泳", "白色或黑色", "颈长", "嘴大", "腿长", "颈部有肉只凸起"): "鹅",
("有羽毛", "卵生", "黑色", "嘴大"): "鸦",
("有羽毛", "卵生", "有爪", "吃肉", "上嘴鹰钩"): "鹰",
("有羽毛", "卵生", "上嘴鹰钩", "能模仿人说话"): "鹦鹉",
("卵生", "生活在水中", "生活在陆地", "有皮肤呼吸", "用肺呼吸", "皮肤光滑", "吃昆虫", "会变色"): "青蛙",
("卵生", "生活在水中", "生活在陆地", "有皮肤呼吸", "用肺呼吸", "吃昆虫", "皮肤粗糙", "四肢扁", "背部黑色"): "蝾螈",
("卵生", "生活在水中", "生活在陆地", "有皮肤呼吸", "用肺呼吸", "吃昆虫", "皮肤粗糙"): "蟾蜍",
("用鳃呼吸", "身体有鳍", "生活在海洋中", "身体扁平", "两眼在头部同侧"): "比目鱼",
("用鳃呼吸", "身体有鳍", "生活在淡水中", "身体扁平", "头高尾部窄"): "鲫鱼",
("生活在陆地", "用肺呼吸", "胎生", "身体有鳞或甲", "身体圆而细长", "吃小动物"): "蛇",
("生活在陆地", "用肺呼吸", "胎生", "身体有鳞或甲", "有四肢", "尾巴细长易断", "吃昆虫"): "壁虎",
("生活在陆地", "用肺呼吸", "胎生", "身体有鳞或甲", "身体圆而扁", "有坚硬的壳"): "乌龟",
("生活在陆地", "用肺呼吸", "胎生", "身体有鳞或甲", "壳为黄褐色", "皮肤光滑", "有黑斑"): "玳瑁",
("生活在陆地", "用肺呼吸", "胎生", "身体有鳞或甲", "有四肢", "善游泳", "皮硬黑褐色"): "鳄鱼",
}
# 动物特征选择列表
choices = ["有奶","有毛发","有羽毛","会飞","生蛋","哺乳动物", "有爪", "有犬齿", "目盯前方",
"吃肉", "有蹄", "反刍食物", "有蹄动物", "黄褐色", "有黑色条纹", "有黑色斑点", "长腿", "长脖子", "有暗斑点",
"白色", "鸟", "不会飞", "黑白色", "善飞", "不怕风浪", "有奶", "善跳跃", "唇裂", "善捕鼠", "脚有肉垫",
"鼻子上有角", "皮糙肉后", "皮糙肉厚", "有蹄", "黑眼圈", "四肢短小", "上嘴鹰钩", "会模仿人说话", "腿短",
"嘴扁平", "善潜水游泳", "有爪", "吃肉", "卵生", "生活在水中", "生活在陆地", "有皮肤呼吸", "用肺呼吸",
"皮肤光滑", "吃昆虫", "会变色", "皮肤粗糙", "四肢扁", "背部黑色", "身体有鳍", "生活在海洋中", "身体扁平",
"两眼在头部同侧", "生活在淡水中", "头高尾部窄", "身体有鳞或甲", "身体圆而细长", "吃小动物", "有四肢",
"尾巴细长易断", "吃昆虫", "身体圆而扁", "有坚硬的壳", "壳为黄褐色", "皮肤光滑", "有黑斑", "皮硬黑褐色",
"生活在陆地", "用肺呼吸", "胎生", "有鳞或甲", "有四肢", "善游泳", "鳄鱼", "兔子", "猫", "犀牛", "熊猫",
"鹦鹉", "鸭子", "鹰", "鹅", "鸦", "青蛙", "蝾螈", "蟾蜍", "比目鱼", "鲫鱼", "蛇", "壁虎", "乌龟", "玳瑁"
]
# 选择动物特征
def select():
print("请选择动物特征:")
for i in range(len(choices)):
print("%d. %s" % (i + 1, choices[i]), end=", ")
print("q. 退出")
choice = input("请输入编号:")
if choice.isdigit() and 0 < int(choice) <= len(choices):
return choices[int(choice) - 1]
elif choice == "q":
return choice
else:
print("输入错误,请重新输入!")
return select()
# 正向推理
def forward():
# 初始事实
facts = set()
# 选择动物特征
while True:
choice = select()
if choice == "q":
break
facts.add(choice)
# 推理
used_rules = set()
conclusions = set()
for fact in facts:
conclusions.add(fact)
while True:
flag = False
for rule in rules:
if set(rule).issubset(facts) and rule not in used_rules:
print("使用规则:%s -> %s" % (rule, rules[rule]))
for feature in rule:
if feature in conclusions:
conclusions.remove(feature)
facts.add(rules[rule])
conclusions.add(rules[rule])
used_rules.add(rule)
flag = True
if not flag:
break
# 显示最终的事实
print("推理过程中得到的事实:", facts)
print("推理过程中得到的结论:", conclusions)
# 反向推理
def backward():
# 目标条件
facts = set()
# 选择动物特征
while True:
choice = select()
if choice == "q":
break
facts.add(choice)
# 推理
used_rules = set()
conclusions = set()
while True:
flag = False
for rule in rules:
if rules[rule] in facts and rule not in used_rules:
print("使用规则:%s -> %s" % (rule, rules[rule]))
for feature in rule:
facts.add(feature)
conclusions.add(feature)
used_rules.add(rule)
flag = True
if not flag:
break
# 显示最终的事实
print("推理过程中得到的事实:", facts)
print("推理过程中得到的结论:", conclusions)
# 增加规则
def insert():
# 显示现有规则
print("现有规则:")
print(rules)
# 增加规则
while True:
print("请输入规则(例:“哺乳动物 有蹄”),输入q退出:")
rule = input()
if rule == "q":
break
rule = tuple(rule.split())
if rule in rules:
print("规则已存在!")
else:
print("请输入结论:")
result = input()
rules[rule] = result
print("规则添加成功!")
# 自动添加之前不存在的选项
for choice in rule:
if choice not in choices:
choices.append(choice)
# 删除规则
def remove():
# 显示现有规则
print("现有规则:")
print(rules)
# 删除规则
while True:
print("请输入要删除的规则(例:“哺乳动物 有蹄”),输入q退出:")
rule = input()
if rule == "q":
break
rule = tuple(rule.split())
print(rule)
if rule not in rules:
print("规则不存在!")
else:
del rules[rule]
print("规则删除成功!")
# 主函数
def main():
while True:
print("请选择推理方式:")
print("1. 正向推理")
print("2. 反向推理")
print("3. 增加规则")
print("4. 删除规则")
print("5. 退出")
choice = input("请输入编号:")
if choice == "1":
forward()
elif choice == "2":
backward()
elif choice == "3":
insert()
elif choice == "4":
remove()
elif choice == "5":
break
else:
print("输入错误,请重新输入!")
if __name__ == "__main__":
main()