【实验名称】
动物识别专家系统
【实验目的】
理解和掌握产生式知识表示方法,能够用选定的编程语言实现产生式系统的规则库。
能够设计并编码实现简单的产生式(推理)系统。
【实验内容】
本实验仿照书中例题,用自己擅长的编程语言实现一个简单的动物识别专家系统——识别虎、金钱豹、斑马、长颈鹿、鸵鸟、企鹅、信天翁等七种动物。
在本系统当中,知识库中的知识用产生式规则来表示,共有如下15条规则:
r1: IF 该动物 THEN 该动物是哺乳动物
r2: IF 该动物有奶 THEN 该动物是哺乳动物
r3: IF 该动物有羽毛 THEN 该动物是鸟
r4: IF 该动物会飞 AND 会下蛋 THEN 该动物是鸟
r5: IF 该动物吃肉 THEN 该动物是食肉动物
r6: IF 该动物有犬齿 AND 有爪 AND 眼盯前方 THEN 该动物是食肉动物
r7: IF 该动物是哺乳动物 AND 有蹄 THEN 该动物是有蹄类动物
r8: IF 该动物是哺乳动物 AND 是反刍动物 THEN 该动物是有蹄类动物
r9: IF 该动物是哺乳动物 AND 是食肉动物 AND 是黄褐色
AND 身上有暗斑点 THEN 该动物是金钱豹
r10:IF 该动物是哺乳动物 AND 是食肉动物 AND 是黄褐色
AND 身上有黑色条纹 THEN 该动物是虎
r11: IF 该动物是有蹄类动物 AND 有长脖子 AND 有长腿
AND 身上有暗斑点 THEN 该动物是长颈鹿
r 12:IF 该动物有蹄类动物 AND 身上有黑色条纹 THEN 该动物是斑马
r13:IF 该动物是鸟 AND 有长脖子 AND 有长腿 AND 不会飞
AND 有黑白二色 THEN 该动物是鸵鸟
r14: IF 该动物是鸟 AND 会游泳 AND 不会飞 AND 有黑白二色
THEN 该动物是企鹅
r15: IF 该动物是鸟 AND 善飞 THEN 该动物是信天翁
当给定初始事实(如该动物的特征为“暗斑点,长脖子,长腿,奶,蹄” )能够推出,该动物是这7种动物中的哪一种动物。请设计并编码实现该动物识别系统。
【实验原理】
通常,人们把利用产生式知识表示方法所进行的推理称为产生式推理,把由此所形成的系统称为产生式系统。产生式推理是专家系统的核心,其基本结构如下图所示。
(1)规则库:用于描述相应领域内知识的产生式集合,也称为知识库,包含了将问题从初始状态转换成目标状态所需要的所有变换规则。这些规则描述了问题领域的一般性知识。可见,规则库是产生式系统进行推理求解的基础。
(2)综合数据库(也称事实库):一个用于存放问题求解过程中各种当前信息的数据结构。例如,问题的初始状态、输入的事实、推理得到的中间结论及最终结论。在推理过程中,当规则库中某条规则的前提可以和综合数据库中的已知事实相匹配时,该规则被激活,由它推出的结论将被作为新的事实放入综合数据库中,成为后面推理的已知事实。
(3)控制系统(推理机构):由一组程序组成,负责整个产生式系统的运行,决定问题求解过程中的推理路线,实现对问题的求解。控制系统主要做以下几项工作:
- 从规则库中选择与综合数据库中的已知事实进行匹配。
- 匹配成功的规则可能不止一条,进行冲突消解。
- 执行某一规则时,如果其右部是一个或多个结论,则把这些结论加入到综合数据库中;如果其右部是一个或多个操作,则执行这些操作。
- 对于不确定性知识,在执行每一条规则时还要按一定的算法计算结论的不确定性。
- 检查综合数据库中是否包含了最终结论,决定是否停止系统的运行。
动物识别系统是一个简单的、比较完整的产生式系统。设该系统可以识别虎、金钱豹、斑马、长颈鹿、鸵鸟、企鹅、信天翁这七种动物,其规则库中包含的知识如【实验内容】中的15条规则。
若在推理开始前,综合数据库中存放有以下事实:
该动物的特征:暗斑点,长脖子,长腿,奶,蹄
当推理开始后,控制系统(推理机)的工作过程如下:
(1)从规则库中取出r1,检查其前提是否可与综合数据库中的已知事实匹配。r1的前提是“有毛发”,但事实库中没有这一事实,故匹配失败,则r1不能被用于推理。然后取r2,r2的前提可与事实库中的已知事实“有奶”相匹配,则r2被执行,并将其结论“该动物是哺乳动物”作为新的事实加入到综合数据库中。此时,综合数据库的内容变为:
该动物的特征:暗斑点,长脖子,长腿,奶,蹄,哺乳动物
(2)从规则库中分别取r3,r4,r5,r6与综合数据库中的已知事实进行匹配,均不成功。接着取r7,该前提与综合数据库(事实库)中的已知事实“是哺乳动物 AND有蹄”相匹配,r7被执行,并将其结论“该动物是蹄类动物”作为新的事实加入到综合数据库中。此时,综合数据库的内容变为:
该动物的特征:暗斑点,长脖子,长腿,奶,蹄,哺乳动物,有蹄类动物
(3)此后,r7,r8,r9,r10均匹配失败。接着取r11,该前提是“该动物是有蹄类动物 AND 有长脖子 AND 有长腿 AND 身上有暗斑”与事实库中的已知事实相匹配,r11被执行,所以将r11的结论 “该动物是长颈鹿”加入综合数据库。此时,综合数据库的内容变为:
该动物的特征:暗斑点,长脖子,长腿,奶,蹄,哺乳动物,有蹄类动物,长颈鹿
检查综合数据库,由于“长颈鹿”已经是目标集合中的一个结论,即已推出最终结果,故问题的求解过程结束。
上述的求解过程是一个不断地从规则库中选择可用规则与综合数据库中的已知事实进行匹配的过程,规则的每一次成功匹配都使综合数据库中增加了新的内容,并朝着问题的解决方向前进了一步。这一过程称为推理,是专家系统中的核心内容。
【设计思想】
一:将所有特征值存入数组
static String Features[] = {"有毛发","有奶","有羽毛","会飞","会下蛋",
"吃肉","有犬齿","有爪","眼盯前方", "哺乳动物","有蹄","蹄类动物",
"反刍动物","食肉动物","黄褐色","暗斑点", "黑色条纹","鸟","长脖子",
"长腿","不会飞","会游泳", "黑白二色","善飞"};
二:将所有最终结果存入数组中
static String Results[] = {"金钱豹","虎","长颈鹿","斑马","鸵鸟","企鹅","信天翁"};
三:根据题意创建相应的对应法则rules (若为false表示其为中间结果,若为true表示其为最终结果)此处仅列举部分 具体代码见下方
public static animalCollect [] IntRules(){
animalCollect [] rules= new animalCollect [15];
//以下为结果集
//rule0
int fact0[]={1};
rules[0]=new animalCollect (1,fact0,false,10);
//rule1
int fact1[]={2};
rules[1]=new animalCollect (1,fact1,false,10);
//rule2
return rules;
}
图解如下:
注:其中条件由直线划分至一起的表示与条件(只有所划分的条件都存在才可推出结果)
【程序清单】
1 testController.java
import java.util.Scanner;
public class testContoller {
static String Features[] = {"有毛发","有奶","有羽毛","会飞","会下蛋",
"吃肉","有犬齿","有爪","眼盯前方", "哺乳动物","有蹄","蹄类动物",
"反刍动物","食肉动物","黄褐色","暗斑点", "黑色条纹","鸟","长脖子",
"长腿","不会飞","会游泳", "黑白二色","善飞"};
static String Results[] = {"金钱豹","虎","长颈鹿","斑马","鸵鸟","企鹅","信天翁"};
public static void main(String args[]){
System.out.println("特征集如下");
for(int n=0;n<Features.length;n++){
System.out.println("特征"+(n+1)+":"+Features[n]);
}
//Rule[] rules = Rule.IntRules();//规则库初始化
Facts factsDB = new Facts();
animalCollect rules[];
rules=animalCollect.IntRules();
//输入特征选项 5,{14,22,20,19,2}
int[] f = new int[25];
Scanner input=new Scanner(System.in);//输入初始化
System.out.println("输入将要匹配的特征的数量(整数)");
int FactNum=input.nextInt();//输入一个正整数
factsDB.setFactNum(FactNum);
System.out.println("对应上表,输入将要匹配的特征值(整数)");
for(int k=0;k<FactNum;k++){
int feature=input.nextInt();//输入一个正整数
f[k] = feature;
}
for(int k=0;k<FactNum;k++){
System.out.println(f[k]);
}
/*f[0]=14;f[1]=22;f[2]=20;f[3]=19;f[4]=2;*/
factsDB.setFacts(f);
boolean isEnd = false;
boolean findAns = false;
while(!isEnd){
isEnd= true;
for(int i=0;i<rules.length;i++){
if(rules[i].isUsed() || !rules[i].isPossible()) continue;//该规则失效
int res = cmp(rules[i],factsDB); //若所输入条件与rules中的一列相匹配
if(res == 0){
continue;//不匹配
}else if(res == 1){
//匹配,但是为中间值
int[] facts=factsDB.getFacts(); //将所输入的条件存入facts中
int n = factsDB.getFactNum(); //获得该中间条件个数n
facts[n]=rules[i].getResultID(); //获得该中间条件的结果存入facts[n]中
System.out.println(Features[facts[n]-1]); //显示中间结果
System.out.println(factsDB.getFactNum()+"+"+rules[i].getFacts().length);
if(factsDB.getFactNum() ==rules[i].getFacts().length){
System.out.println(Features[facts[n]-1]); //显示中间结果
};
factsDB.setFacts(facts); //将获得的中间条件及其结果存入factDB中
factsDB.setFactNum(++n); //新增条件个数
isEnd= false;
break;
}else if(res == 2){
//匹配,且为最终答案
System.out.println("结果是:"+Results[rules[i].getResultID()-1]);
findAns=true;
break;
}else if(res == 3){
System.out.println("结果是:"+Features[rules[i].getResultID()-1]);
findAns=true;
break;
}
}
}
if(!findAns){
System.out.println("无匹配答案");
}
}
public static int cmp(animalCollect r,Facts f){
int F_Rule[] = r.getFacts(); //获得rule[i]对应的fact
int F_Fact[]= f.getFacts(); //获得所输入的条件值集合
int factNum =f.getFactNum(); //获得所输入的匹配特征的数量
for(int i=r.getNextFactPos();i<r.getFactNum();i++){ //从所记录的下一次需要验证的位置开始,逐次遍历每个位置
boolean isMatch=false;
for(int j=0;j<factNum;j++){
if(F_Rule[i] == F_Fact[j]){ //匹配成功
if(i+1 == r.getFactNum()){ //全部规则特征已匹配完毕
r.setUsed(true); //标志r的条件已全部使用
if(r.isEndResult()){
System.out.println("为最终答案");
//是最终答案
return 2;
}else{
//中间特征
System.out.println("为中间答案");
System.out.println(i-r.getNextFactPos()+1+"=="+f.getFactNum());
if(i-r.getNextFactPos()+1==f.getFactNum()){
System.out.println(r.getResultID()); //显示中间结果
return 3;
}
return 1;
}
}
//部分特征匹配成功,开始匹配下一个特征
isMatch=true;
break;
}else{
//不匹配,继续循环
continue;
}
}
if(isMatch){
//匹配成功
continue;
}else{
//匹配失败
r.setNextFactPos(i); //标记下一次开始的位置为i
return 0;
}
}
//无匹配项
return 0;
}
}
2 animalCollect.java
//规则实体
public class animalCollect{
private int factNum;
private int facts[];
private boolean used;//是否使用
private boolean possible;//是否可能
private boolean endResult;
private int resultID;
private int nextFactPos;//记录下一次需验证的特征位置
public animalCollect(int factNum,int[] facts,boolean endResult,int resultID){
this.used=false;
this.possible=true;
this.factNum = factNum;
this.facts=facts;
this.endResult=endResult;
this.resultID=resultID;
this.nextFactPos=0;
}
public int getNextFactPos() {
return nextFactPos;
}
public void setNextFactPos(int nextFactPos) {
this.nextFactPos = nextFactPos;
}
public int getResultID() {
return resultID;
}
public void setResultID(int resultID) {
this.resultID = resultID;
}
public int getFactNum() {
return factNum;
}
public void setFactNum(int factNum) {
this.factNum = factNum;
}
public int[] getFacts() {
return facts;
}
public void setFacts(int[] facts) {
this.facts = facts;
}
public boolean isUsed() {
return used;
}
public void setUsed(boolean used) {
this.used = used;
}
public boolean isPossible() {
return possible;
}
public void setPossible(boolean possible) {
this.possible = possible;
}
public boolean isEndResult() {
return endResult;
}
public void setEndResult(boolean endResult) {
this.endResult = endResult;
}
//初始化规则库
public static animalCollect [] IntRules(){
animalCollect [] rules= new animalCollect [15];
//以下为结果集
//rule0
int fact0[]={1};
rules[0]=new animalCollect (1,fact0,false,10);
//rule1
int fact1[]={2};
rules[1]=new animalCollect (1,fact1,false,10);
//rule2
int fact2[]={3};
rules[2]=new animalCollect (1,fact2,false,18);
//rule3
int fact3[]={4,5};
rules[3]=new animalCollect (2,fact3,false,18);
//rule4
int fact4[]={6};
rules[4]=new animalCollect (1,fact4,false,14);
//rule5
int fact5[]={7,8,9};
rules[5]=new animalCollect (3,fact5,false,14);
//rule6
int fact6[]={10,11};
rules[6]=new animalCollect (2,fact6,false,12);
//以下为非结果集
int fact7[]={10,13};
rules[7]=new animalCollect (2,fact7,false,12);
int fact8[]={10,14,15,16};
rules[8]=new animalCollect (4,fact8,true,1);
int fact9[]={10,14,15,17};
rules[9]=new animalCollect (4,fact9,true,2);
int fact10[]={12,16,19,20};
rules[10]=new animalCollect (4,fact10,true,3);
int fact11[]={12,17};
rules[11]=new animalCollect (2,fact11,true,4);
int fact12[]={18,19,20,21,23};
rules[12]=new animalCollect (5,fact12,true,5);
int fact13[]={18,21,22,23};
rules[13]=new animalCollect (4,fact13,true,6);
int fact14[]={18,24};
rules[14]=new animalCollect (2,fact14,true,7);
return rules;
}
}Facts.java
//事实实体
public class Facts {
private int factNum;
private int facts[];
//初始化
Facts(){
factNum=0;
facts=new int[0];
}
public int getFactNum() {
return factNum;
}
public void setFactNum(int factNum) {
this.factNum = factNum;
}
public int[] getFacts() {
return facts;
}
public void setFacts(int[] facts) {
this.facts = facts;
}
}
【结果截图】
运行效果如下
1 验证中间结果截图 (r1: IF 该动物 THEN 该动物是哺乳动物)(1,1)
2验证最终结果截图(例验证斑马)
由于
int fact11[]={12,17};
rules[11]=new animalCollect (2,fact11,true,4);int fact6[]={10,11};
rules[6]=new animalCollect (2,fact6,false,12);int fact0[]={1};
rules[0]=new animalCollect (1,fact0,false,10);