动物识别系统实验报告.docx
《动物识别系统实验报告.docx》由会员分享,可在线阅读,更多相关《动物识别系统实验报告.docx(12页珍藏版)》请在冰豆网上搜索。
动物识别系统实验报告
人工智能实验报告二
班级:
XXXX姓名:
XXXX学号:
XXXXX
一.实验目的
1.理解并掌握基于规则系统的表示与推理。
2.学会编写小型的产生式系统,理解正向推理和反向推理的过3。
程以及两者的差别.
4。
学会设计简单的人机交互界面.
二.实验预习内容及实验环境:
1.了解动物识别系统问题;
2.产生式系统的组成部分,正向推理,逆向推理的算法和产生式系统的类型。
三、实验内容和步骤:
1.内容:
结合第五章内容,以动物识别系统(或货物袋装系统)为例,实现基于规则的系统构造实验。
2.要求:
1>结果显示要求
(1)有若干选择动物特征的选择列表;
(2)表现判断动物时,使用了哪些规则;
(3)表现数据库的变化;(正向推理中表现每使用一次规则后,产生新事实。
反向推理中表现新的目标事实有哪些需要证明,哪些已经得到证实);
(4)显示规则的调用次序;
(5)显示最后的结果,包含动物能识别出来和动物不能识别出来两种情况.
(6)至少检查两个例子实现正向推理和反向推理的区别;
老虎的例子如上所示,以下为金钱豹的例子:
正向推理:
反向推理:
2>指出综合数据库和规则库分别使用哪些函数实现的?
综合数据库和规则库均采用数组完成。
其中的综合数据库为一个string类型的数组,内容为事实的名称字符串,下标则为该事实的序号。
数组长度为33,表示共有33个事实(最后7个为动物名称)。
代码如下:
stringfacts[factnum]={”有爪”,"有犬齿”,"有奶”,"有毛发”,”目视前方”,"吃肉",”有蹄",”有羽毛”,”会飞","生蛋",”不会飞”,”黑白色","会游泳”,”善飞","不怕风浪”,”长腿","长脖子",”有暗斑点”,”白色”,”黑色斑点”,”黑色条纹”,”黄褐色”,"食肉动物","鸟",”哺乳动物",”有蹄动物”,"老虎",”金钱豹",”长颈鹿",”斑马",”鸵鸟",”企鹅","海燕"};
规则库为一个结构体数组。
该结构体由一个int型数组(表示前提条件的序号集合)和一个int数据(表示由此前提推出的结果)表示。
该数组长度为14,表明有14条规则.该规则库在建立时有一个内在要求:
前提的规则(子规则)的序号必须在父规则之前。
(便于正向推理的实现)代码如下:
structrule
{
intcon[10];
intres;
};
Rulerules[rulenum]={{{2,—1},24},{{3,—1},24},{{7,—1},23},{{8,9,-1},23},{{24,0,1,4,—1},22},{{24,5,-1},22},{{24,6,—1},25},{{22,21,20,—1},26},{{22,21,19,—1},27},{{25,15,16,21,17,—1},28},{{25,18,20,—1},29},{{23,10,15,16,11,—1},30},{{23,10,12,11,-1},31},{{23,13,14,-1},32}};
3>规则库的匹配算法是什么?
如何选用可用规则集中的规则?
分别使用哪些函数实现的?
用一个bool型的与数据库数组等长的enfact数组表示各事实是否已经满足(或已经推出)。
要匹配一个规则的时候,只需查看其前提序号集数组中每个元素对应的enfact数组中的值是否为true,如果所有都为true则可由该规则推出相应结果。
代码如下:
boolenfact[factnum];
memset(enfact,false,factnum);//每次输入时需对该数组进行初始化
//以下为输入过程
while(ti!
=-1)
{
if(ti>=0&&ti<=notanimal)
enfact[ti]=true;
else
{
cout〈<”输入错误!
请输入0~25之间的数字!
”〈cin。
clear();//清除流错误错误标记
cin。
sync();////////////清空输入缓冲区
break;
}
cin>〉ti;
};
以上,则完成了输入,并对enfact数组进行了初始化.现在对正向推理和反向推理的匹配和具体推理过程进行简要说明。
Ø正向推理:
从下向上的推理。
由于建立规则库时的内在要求,即子规则必在父规则前,故进行正向推理的时候只要将规则库从前到后扫一遍看是否能由规则推出相应结果即可。
如果能匹配推出结果则看该结果是否为动物,如果已经推出动物则推理成功。
否则更新数据库,匹配下一个规则.代码如下:
intobverse()
{
outputf
(1);
intti;
inti,j;
inttres;
cout<for(i=0;i〈rulenum;i++)
{
j=0;
ti=rules[i].con[j];
while(ti!
=—1)//假设前提数不超过9个,必存在-1作为结束
{
if(enfact[ti]==false)
break;
j++;
ti=rules[i]。
con[j];
}
if(ti==—1)
{
tres=rules[i].res;
enfact[tres]=true;
cout〈<"运用了规则”〈
”;
j=0;
while(rules[i].con[j]!
=—1)
{
cout〈〈facts[rules[i]。
con[j]]〈<”";
j++;
}
cout<〈"====〉"〈if(isAnimal(tres))
{
if(noOtiose(tres))
returntres;
else
return-1;
}
}
}
return—1;
}
Ø反向推理:
从上向下的推理。
反向推理比正向推理要复杂一些。
采用的算法是从事实数据库的动物开始从前往后进行匹配,看是否能成功推出,如果都推不出能识别失败,若能推出其中一个则中止搜索,识别成功。
推某一个事实时,仍然是从该事实的前提出发,逐个匹配,若所有的前提满足,则该事实满足.不同的是,这里以该事实为结果的前提可能有很多,我采用了牺牲空间换时间的方法,即不是将规则库从头到尾搜一遍来查找以待查事实为结果的规则,而是事先将所有的先保存在一个结构体数组中,结构体中只有一个数组,表示能推出某个结果的规则序号。
代码如下:
structfactrule
{
intrulen[5];
};
factrulefactrules[factnum]={{-1},{—1},{-1},{—1},{-1},{—1},{—1},{-1},{—1},{-1},{-1},{-1},{-1},{-1},{—1},{-1},{-1},{-1},{—1},{—1},{-1},{—1},
{4,5,-1},{2,3,-1},{0,1,-1},{6,-1},{7,—1},{8,-1},{9,-1},{10,—1},{11,—1},{12,-1}};
故要看某个事实是否能推出的时候,只要将它对应的标号的factrules中的数组中的规则进行匹配即可,只要有一条规则满足,即可中止搜索,推出该事实可满足.
另外,由于某个规则的前提可能是另一个规则的结果,这相当于规则的嵌套.所以在进行反向推理时必须用到递归技术,并且是一种回溯.整个反向推理的代码如下:
boolachieve(intri)//
{
intj;
intti;
cout〈〈endl〈<"当前验证目标为:
"<cout<〈"尝试规则”<〈ri〈〈":
";
j=0;
while(rules[ri]。
con[j]!
=-1)
{
cout<〈facts[rules[ri]。
con[j]]〈〈””;
j++;
}
cout〈〈"==?
==>”〈〈facts[rules[ri].res]<〈endl;
j=0;
ti=rules[ri].con[j];
while(ti!
=—1)
{
if(enfact[ti]==false)
{
intki=0;
if(factrules[ti].rulen[ki]==—1)
{
cout〈〈"条件"<"<cout<〈"使用规则”<”;
j=0;
while(rules[ri]。
con[j]!
=-1)
{
cout<j++;
}
cout〈<”=====〉"<〈facts[rules[ri].res]<〈"失败!
"<〈endl;
returnfalse;
}
else
{
cout<<"条件”〈boolok=false;
for(ki=0;factrules[ti]。
rulen[ki]!
=—1&&!
ok;ki++)
{
ok=achieve(factrules[ti].rulen[ki]);
}
if(ok)
{
cout<〈”条件"<”〈〈endl;
j++;
ti=rules[ri].con[j];
}
else
{
cout<〈"条件"〈"<〈endl;
cout<〈”使用规则"<";
j=0;
while(rules[ri]。
con[j]!
=-1)
{
cout<〈facts[rules[ri]。
con[j]]<〈"”;
j++;
}
cout<<”=====〉"〈〈facts[rules[ri].res]<〈"失败!
”<〈endl<returnfalse;
}
}
}
else
{
cout〈<”条件"<”<j++;
ti=rules[ri].con[j];
}
}
{
cout〈<”使用规则"<";
j=0;
while(rules[ri].con[j]!
=-1)
{
cout〈con[j]]<<"”;
j++;
}
cout〈<"=====〉”<〈facts[rules[ri]。
res]<〈"成功!
"〈〈endl;
returntrue;
}
}
intreverse()
{
outputf
(2);
inti;
for(i=animalfrom;i〈rulenum;i++)
{
cout〈”〈〈facts[rules[i].res]〈<”进行反向推理—-—-—————------—---——--——”〈if(achieve(i))
{
if(noOtiose(i+notanimal-animalfrom+1))
returni+notanimal-animalfrom+1;
else
return—1;
}
}
return-1;
}
注:
本系统对输入事实冗余的情况做出了处理。
采用的算法是:
仍然是空间换时间的思想,事先将所有动物可能有的特征做成一个与特征数等长的bool数组typefact,这样只需在推出某种动物后再将该动物对应的数组typefact与enfact数组进行匹配,看enfact中是否存在多余的错误特征即可.代码如下:
booltypefact[factnum-notanimal-1][factnum]={
{1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,1,0,0,0,0,0,0},
{1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,0,0,1,0,0,0,0,0},
{0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,1,1,0,0,0,1,0,0,1,1,0,0,1,0,0,0,0},
{0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,1,1,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,1}};
boolnoOtiose(intfi)
{
intki=fi—notanimal-1;
boolok=true;
for(inti=1;i〈factnum;i++)
{
if(enfact[i]==true&&typefact[ki][i]==false)
{
ok=false;
}
}
if(!
ok)
{
cout〈推理失败!
”〈";
for(inti=1;i{
if(enfact[i]==true&&typefact[ki][i]==false)
{
cout〈ok=false;
}
}
cout〈cout〈<"故该动物不是"〈〈facts[fi]<<"!
"〈returnfalse;
}
else
returntrue;
}
四、实验总结及体会:
1.通过本次实践,对正向推理反向推理的过程可以说已经了如指掌了,对代码的控制能力也有了一定的提高,递归函数的设计,流程的控制都得到了一定的强化。
我觉得本次实验的算法还是比较简单的,关键是数据结构的设计,我在设计的过程中用了很多空间换时间的方法,使得整个算法的过程十分简洁,运行效率也很高,数据的冗余度也得到一定控制.
2.这次输入输出的封装做得非常不好,而输出的重复性高,又比较麻烦,直接导致代码存在较大的冗余.
3.由于前期数据库设计存在一定失误,没有将动物从中分离出来,给之后的匹配过程带来了一定的不便。
但总的来说,整个算法的过程还是比较清晰的.另外,由于时间关系,该系统的规则,数据库的可扩展性显得不足,这是在后续版本中需逐步修正的。