15307130185 王鹏
[TOC]
我的项目idea来源自微软小冰两年前推出的应用——读心术,这是一款基于人物知识库进行问答并猜出相应人物的专家系统。
我在本项目中设计开发的是一款类似于微软小冰读心术的专家系统——Pokemon Know,只是把范围限制在了第一世代的Pokemon(或称:神奇宝贝,精灵宝可梦)中。
目前知识库涵盖了第一世代的59只宝可梦,具体名单见这里。
如果用户了解足够的事实,符合条件的宝可梦一般能在8个问题内找出来。
- 基于框架(frame)的专家系统
- 支持框架的继承
- 规则的隐式表达
- 基于确信因子的不确定性管理
- 基于决策树的ID3算法
- 友好的用户界面
- 友好的知识库扩展机制
需要本机装有Python3,界面依赖第三方Python-GUI库PyQt5。
如果不想知道专家系统运行时的内部部分信息,在source目录下运行命令:
python Play.py
如果想知道专家系统运行时的内部部分信息,在source目录下运行命令:
python Main.py
(注意:程序在最后的流程中会调用网络上的图片资源,因此要求该专家系统在有网络的条件下使用。)
###组成与逻辑
1.用户界面作为用户与系统的交互媒介,向用户展示问题,并获得事实。
2.基于框架的知识数据库储存已有的Pokemon知识,以皮卡丘为例,其知识库内容(目前)为:
{
"名字": {"皮卡丘": "defi"},
"整体颜色": {"黄色": "prob"},
"继承": {"四足兽": "prob", "鼠宝可梦": "prob"},
"眼睛颜色": {"黑色": "prob", "褐色": "prob"},
"耳朵颜色": {"黄色": "prob", "黑色": "prob"},
"肢数": {"4": "prob"},
"翅膀数": {"0": "prob"},
"地区": {"关都":"prob", "城都":"prob", "丰缘":"prob", "神奥":"prob", "卡洛斯":"prob", "阿罗拉": "prob"},
"特点": {"前爪有五个\"手指\"": "defi", "有像锯齿状闪电的尾巴": "defi"}
}
3.候选的Pokemon类维护着自身的信息(例如确信程度等等)。
4.我们假定使用该专家系统的用户知道Pokemon的基本外貌信息。
5.推理引擎根据候选的Pokemon和用户提供的事实,对已有的Pokemon的确信因子进行更新,并根据相应的规则,筛出部分不合格的Pokemon,然后根据相关算法(见下文)设计出合适的问题以做出进一步的筛选,或者给出它目前认为的答案。
6.数据库编辑器(知识库编辑脚本)给专家提供了更新知识的工具。
基于框架的专家系统适合用面向对象的方法来处理,Python支持面向对象的编程。
我定义了一个Pokemon class来作为它们的基本框架,并为它们的数据载入、初始化、确信因子的变化以及出局等操作提供了相关的方法。
Pokemon class支持继承,这可以用来分类和丰富Pokemon的特性。
教材第3章提供了一种对基于规则的专家系统的不确定性管理方案:确信因子。我在这里把它用在了基于框架的专家系统中。
-
用确信因子表达知识
以皮卡丘知识为例,当我们在知识库中加入:
"整体颜色" : {"黄色": "prob"}
表示当用户给出了"整体颜色为黄色"这个事实后,皮卡丘的候选程度会与确信因子"prob"结合,得到一个新的候选程度,至于"prob"是多少由专家决定,我这里使用的是0.6。
-
用确信因子表达用户对事实的相信程度
在与用户的交流中,我给每个问题提供了三个选项:"是的","有一点","不是",确信因子分别为1.0, 0.6和-1.0,这样做的好处可以容忍用户对事实的一定程度上的不确定,提高专家系统的鲁棒性。
-
确信因子的结合
我采用教材第三章提供的公式来计算结合后的确信因子:
Pokemon的判断与筛选非常适合决策树这个模型。
这里我采用决策树的ID3算法来对问题进行选择。ID3算法的核心在于最大化信息增益,即最小的期望信息熵。
随机变量$S$的信息熵$E(S)=\sum -p_i\log p_i$
我们要做的是最小化$\sum_{v}^{}\frac{|S_v|}{|S|}E(S_v)$
这里有一个问题:如何估计$p_i$,即每个还在候选池里的Pokemon是答案的概率?
我采用的方法是设计一个函数,这个函数能完成确信因子到概率的转换,最终设计的函数长这样:
base是人为设定的一个参数,我给它设置成了100。
完成转换后,我们就能计算出相应的信息熵,从而对问题进行选择。
关于Pokemon知识的来源可以见宝可梦百科
由于该知识库规模较小,故用json作为数据存储就能满足专家系统对知识库操作的基本需求,并且比一般数据库操作更方便。
对于每个Pokemon,我们用一个字符型文件来描述。文件内容的最外层为一对"{"、"}",表示对象的定义。
对象的每个属性用"<属性名>" : { "<值1>" : "<确信因子1>", "值2" : "<确信因子2>" }...来表示。
- 属性可以为空。例如: "尾巴颜色" : {} 意味着这个对象没有尾巴。
- 属性的值可以冲突。以身体颜色为例,可能存在某个Pokemon,它的身体颜色介于红色和黄色之间,那么我们可以这样来描述: "整体颜色": {"红色": "mayb", "黄色": "mayb" },其中mayb代表着正面的但是程度较低的确信因子。
- 属性可以继承。语法为: "继承": { "<被继承对象1>": "<归属度1>", "<被继承对象2>": "<归属度2>"...}。在载入知识库的时候,专家系统会自动处理继承属性,并与原属性结合。
- "?" 语法。当属性值中存在"?"时,这意味着就算回答了与对象该属性相悖的答案,也不会降低这个Pokemon的确信程度,多用于难以判断/模糊的知识,这样做可以增强专家系统的鲁棒性。
-
用户可以直接修改data/*.json文件。
-
用户可以调用DB_proc.py脚本,对知识库进行修改。
命令行格式为:python DB_proc.py <name> <attr> <val> <CertaintyFactor>
该操作会将name对象的attr属性添加一个CartaintyFactor的val值,如果已有val值则在原基础上修改。
我给Pokemon类提供了is_out()函数,用来判定这个Pokemon是否出局,目前的判定方法是如果它的确信因子小于0.5超过两轮则认为出局。
这个条件并不是一成不变的,可以根据后续的需要来进行修改。
见DEMO-*.mp4文件,DEMO中测试了四只宝可梦:皮卡丘、妙蛙种子、阿柏怪和喵喵,均在8个问题内给出答案。
- 尝试加入更多的Pokemon的知识与框架。
- 尝试利用NLP(自然语言处理)的方法来从Pokemon百科上提取结构化的知识,减少人工专家录入知识库的负担。
- 尝试搭建该系统的web服务,免去python依赖。
此项目的代码仓库:Github
宝可梦百科:Wiki