中 国 矿 业 大 学 徐 海 学 院
《人工智能》实验报告
完成时间: 20##年 4月 20日
目 录
一、课程设计的目的与要求. 3
二、课程设计的内容. 3
三、课程设计的原理. 3
四、系统整体分析. 6
五、课程设计心得体会. 9
六、附录. 10
七、参考文献. 19
一、课程设计的目的与要求
1. 掌握人工智能的知识表示技术,能用产生式表示法表示知识,并实现一个用于识 别的专家系统。
2. 推理策略采用正向推理和反向推理两种。
二、课程设计的内容
1、学习人工智能的知识表示技术,关键掌握产生式知识表示的具体应用方法。
2、实现的动物(植物)识别系统的主要功能如下:
2.1系统能通过正向、反向推理得到正确的动物(植物)识别结果。
2.2系统能动态地添加规则、能显示推理过程。
三、课程设计的原理
1. 植物识别系统是基于产生式系统的一种推理方式,其推理过程需要借助于推理机进行(实际上就是一个有很多规则的规则类库),在我们的系统中,主要的推理规则如下(和数据库中的规则是一致的):
推理规则:
R01:IF 种子的胚有两个子叶
THEN 双子叶植物
R02:IF 叶脉为网状
THEN 双子叶植物
R03:IF 种子的胚有一个子叶
THEN 单子叶植物
R04:IF 叶脉平行
THEN 单子叶植物
R05:IF 双子叶植物+花托呈杯状
THEN 蔷薇科植物
R06:IF 双子叶植物+花为两性+花瓣为5枚
THEN 蔷薇科植物
R07:IF 蔷薇科植物+果实为核果
THEN 李亚科植物
R08:IF 蔷薇科植物+果实为梨果
THEN 苹果亚科植物
R09:IF 李亚科植物+果皮上有毛
THEN 桃
R10:IF 李亚科植物+果皮光滑
THEN 李
R11:IF 果实为圆扁形+果实外有纵沟
THEN 桃
R12:IF 苹果亚科植物+果实里无石细胞
THEN 苹果
R13:IF 苹果亚科植物+果实里有石细胞
THEN 梨
R14:IF 果肉为乳黄色+果肉质脆
THEN 苹果
R15:IF 果核有皱纹+果实为坚果
THEN 核桃
说明:所有关于或的规则均通过拆分改为了与得形式。
总体结构树见附录。
2、正向推理:
算法描述:把规则和事实都放在一个表里面,一个表的记录包括规则1(R1)、规则2、规则3和结果以及状态,推理规则的规则都放在数据库里面,数据库的表如下所示:
把所有得到的事实集放在一个数组里面,设事实集数组为Str[],每次从事实集数组里面找一个元素,把他放在第一个rule1搜索,搜索rule2,rule3,找到rule2和rule3后,在拿rule2和rule3和数组里面的比较,看rule2和rule3是否在数里面,如果在,看flag是否为yes,如果为yes,则找到结果。Flag为no则把result放在数组里面。如果rule或者rule3不再数组里面,则不执行任何操作,操作数组的下一个元素,但数组里面的元素全部取出来了,则有从数组的第0个元素开始,取出来从数据库的rule2搜索,(即执行”select * from plant where rule2=’Str[0]’”),接下来执行str[1]……取完后在从数据库的rule3进行搜索(即执行”select * from plant where rule2=’Str[0]’”),接下来执行str[1]……直到找到结果。找不到结果则表示所选的事实集合不能得出结果。
Str[ ]数组如下(就是我们选择的事实集合,从复选框和手动输入得到的文本内容);
如上图:复选框选择的是“果肉为乳黄色”、“果实里无石细胞”、“果肉质脆”、“花托为杯形”。可知Str[]数组里面的值分别为:str[0]=”果肉为乳黄色”、str[1]=”果实里无石细胞”、str[2]=”果肉质脆”、str[3]=”花托为杯形”;如下图:
数据库基本表如下图所示:
ID:每个规则的ID号,
Ruel1:规则的第一个条件
Ruel2:规则的第二个条件
Rule3:规则的第三个条件
Result:规则的第一、二、三个条件所产生的结果
Flag:最终结果标志,如果为yes则表示为最终结果,如桃、苹果、梨等;如果flag
no则表示得到的结果为中间结果,
Picture:图片的名字,把要显示图片的名字存入数据库里面,当推出最终结果是显示该图片,(只有最终结果才能显示)。
3、反向推理:
反向推理的过程与正向推理相反,其基本原理是:首先假设一个结论为正确的推理结果,然后反相的进行推理看是否可以得到已经选择的条件。若可以,则结论正确;不可以,则假设的结论错误。
优点是搜索的目的性强,推理的效率高。
缺点是选择具有盲目性,可能会求解许多为假的目标,当结论的数目较多,即解的空间较大时,搜索的效率会下降。
如上图为反向推理数据库基本表截图:
Result:推理的结果
Rule1—5:规则1——5,pictuer:图片名称;content:该条规则的备注说明,方便在推理过程中显示。
四、系统整体分析
4.1正向推理
如图所示:当选择了如复选框所示的结果后,单击按钮时,推理过程和推理结果显示如图。当单击 按钮时,界面显示推理结果所对应的图片。
4.2反向推理:
4.3添加新规则:
数据库的基本表变成:
4.4优缺点分析:
4.4.1:优点
1、系统实现了正向推理、反向推理以及添加新规则等功能,实现了图片的动态显示;
2、系统界面清晰、直观,易操作;
3、系统提供了手动输入事实集,配合复选框和添加新规则的功能,有利于用户进一步完善知识库,使其功能逐渐强大;
4、编码实现的算法较简单。
4.4.2:缺点
1、由于时间及人员分配的原因,推理过程显示不是很友好(非专业人士根本看不懂),只能显示了推理数组变化后的结果;
2、图片显示的问题:当推不出任何结果的时候单击“查看该植物”按钮时,仍然会显示图片。出现这种情况的原因是因为当我们找到第一种植物时就把图片名称放在数组里面,当推理出两种以上的植物时,由于给的图片路径的CString变量是全局变量(要在多个函数中调用它),所以最后没有被改变,显示了推理出来的第一种植物的图片;
3、系统启动一次只能执行一次推理操作,出现这种情况的主要原因也是由于在程序设计的时候用到的变量是全局变量,而在单击按钮时触发的只是一个按钮对应的函数。
4.4.3:解决办法
1、要显示出友好的推理过程,可以在推理过程中把推理的规则以及推理结果按照“由规则1+规则2+规则3可以推出结果XX”,最后在界面显示的时候有列表框显示。例如“由果肉质脆+果肉为乳黄色可以推出苹果”;也可以在数据库的基本表中添加一个新的字段,其数据类型为备注,在推理过程中用到该规则的时候直接把该备注赋值给一个字符串在最后推理过程中显示出来。
2、解决图片显示的问题。当推理不出正确结果的时候,我们可以显示一张特定的图片,用来表示没有正确的推理结果,例如:我们可以在保存图片路径数组的第一个元素保存一张特定的图片,当推理不出正确结果时就显示这张退片,当能推理出正确结果时就显示对应植物的图片:如下图就是修正后的界面
3、为了解决程序启动一次,可以推理多次。可以在每个按钮对应的函数下将数组清零,这样就可以轻松实现多次推理,如在每个按钮函数下设count=0。
五、课程设计心得体会
此次课程设计中,黄国锋和王志杰负责主要的编码和数据库设计,负责相关算法的优化与设计。我们讨论的程序中算法的正向推理和反向推理都是大家两个星期不断努力的成果,因此,可以此次课程设计达到了目的。完成了课程设计的要求。
软件的编程语言为visual c++,数据库设计所用access,数据库VC与数据库连接的接口为ADO,在程序设计中,连接数据库我们专门写了一个类,也就是程序中的CAdomodule类,该类继承了ADO类,用起来很方便。这也是我们一次写类进行数据库的操作,优化了程序结构。
通过这次课程设计,我们学会了用vc“皮肤”来美化VC程序的界面,学会了在vc界面上动态显示图片的方法和产生式系统植物识别的相关理论,加深了对所学课程的理解,提高了我们程序设计的技能,更学会了团队合作。
这次课程设计也出现了一些问题:由于同组成员错掌握的编程语言不同,导致在编码设计的过程中对相应算法的理解和沟通上存在局限,从而导致了最终的界面设计很粗糙,存在上面提出的一些缺陷,这都归结于我对程序设计和团队合作经验的欠缺。在以后的学习和实践中一定注意培养这方面精神,逐步提高自己的综合能力。
此次课程设计中,得益于孟老师所教课程的灵活性和全面性,后又在与同学相互交流并提出的意见和建议的情况下,才使得我们最终的程序得以优化.
六、附录
附录A、总体结构树以及程序流程图:
流程图说明:
以上的流程图为从数据库基本表的ruel1搜索(即执行sql语句”select * from plant where ruel1=’str[Tcount]’ ”)的流程图,由于从数据库基本表的rule2和rule3的搜索和从rule1的流程图是相同的。
附录B、参考代码
以下一段代码为从复选框里面读取所选择的值存入数组str[]中;
CString str[30];
int count=0;
void CPlant2Dlg::GetFact()
{
str[0]="N";
count++;
if (m_check1.GetCheck())
{
m_check1.GetWindowText(str[count]);
count++;
}
if (m_check2.GetCheck())
{
m_check2.GetWindowText(str[count]);
count++;
}
if(m_check3.GetCheck())
{
m_check3.GetWindowText(str[count]);
count++;
}
if (m_check4.GetCheck())
{
m_check4.GetWindowText(str[count]);
count++;
}
if (m_check5.GetCheck())
{
m_check5.GetWindowText(str[count]);
count++;
}
if (m_check6.GetCheck())
{
m_check6.GetWindowText(str[count]);
count++;
}
if (m_check7.GetCheck())
{
m_check7.GetWindowText(str[count]);
count++;
}
if (m_check8.GetCheck())
{
m_check8.GetWindowText(str[count]);
count++;
}
}
//这里m_check1到m_check8位复选框的控件关联变量的值
//以下一段代码为手动从文本框输入规则,添加到数组里面
void CPlant2Dlg::OnAddRule()
{
// TODO: Add your control notification handler code here
str[0]="N";//由于在数据库里面空规则用’N’表示,所以给数组的第一个元素赋值为“N“
count++;
CString rule;
m_factin.GetWindowText(rule);
if (rule=="")
{
MessageBox("不能加入空的事实");
return;
}
else
{
str[count]=rule;
count++;
}
m_factin.SetWindowText("");
m_factin.GetFocus();
}
//以下一段代码为正向推理算法
void CPlant2Dlg::OnView()
{
// TODO: Add your control notification handler code here
GetFact();//通过复选框得到的事实集数组
//显示事实集
CString strfact="选择的事实集为:";
for (int i=1;i<count;i++)
{
strfact+=" "+str[i];
}
m_fact.SetWindowText(strfact);
//显示事实集完毕
/*开始产生式系统的算法*/
CAdoModule adop;
CString strSql;
adop.OnInitADOConn("plant.mdb");//连接数据库
for (int j=1;j<count;j++)//从数组里面取出一个值给strtemp然后进行数据库的相关操作
{
CString strtemp,strR2,strR3,strResult,strFlag,strProcess;
_variant_t rule1[10],rule2[10],rule3[10],resoult[10],flag[10],show[10];
int recordcount,recordcount1,recordcount2;
strtemp=str[j];
strSql.Format("select * from plant where rule1='%s'",strtemp);
adop.OpenRecordset(strSql);
//判断规则2有没有内容
//m_result.SetWindowText((_bstr_t)adop.m_pRecordset->GetCollect("rule2"));
recordcount=adop.m_pRecordset->GetRecordCount();
for (int mm=0;mm<recordcount;mm++)
{
rule2[mm]=adop.m_pRecordset->GetCollect("rule2");//得到的记录不止一个
rule3[mm]=adop.m_pRecordset->GetCollect("rule3");
resoult[mm]=adop.m_pRecordset->GetCollect("result");
flag[mm]=adop.m_pRecordset->GetCollect("flag");
show[mm]=adop.m_pRecordset->GetCollect("picture");
//show[mm]=adop.m_pRecordset->GetCollect("picture");
//查找rule2和rule3是否在数组里面,如果rule2和rule3都在数组里面,则把result放进数组里面
for (int i=0;i<count;i++)
{
if (str[i]==(LPCTSTR)(_bstr_t)rule2[mm])//判断rule2是否在数组里面
{
for (int k=0;k<count;k++)
{
if (str[k]==(LPCTSTR)(_bstr_t)rule3[mm])//判断rule3是否在数组里面
{
if ((LPCSTR)(_bstr_t)flag[mm]==(CString)"yes")////
{ //如果得到最后的结果了,输出结果并退出循环
strResult=(LPCTSTR)(_bstr_t)resoult[mm];
// m_result.SetWindowText(strResult);
resultShow=(LPCSTR)(_bstr_t)show[mm];
//if(ResultNum==0)
//{
ResultC[ResultNum]=strResult;
ResultNum++;
}
else//如果flag="no",就把result放进数组里面
{
str[count]=(LPCTSTR)(_bstr_t)resoult[mm];//把结果放进数组里面去
count++;
//strProcess+=(_bstr_t)rule1[j]+(_bstr_t)rule2[mm]+(_bstr_t)rule3[mm]+"-->"+(_bstr_t)resoult[mm];
}
}
else
{
/*rule3不再数组里面的情况*/
}
}//for循环
}
else
{
/*rule2不在数组里面的情况*/
}
}
adop.m_pRecordset->MoveNext();
}
strProcess="经过推理,过程数组里面的值边为:";
for (int p=1;p<count;p++)
{
strProcess+=" "+str[p];
}
m_process.SetWindowText(strProcess);
}
//从ruel2开始搜
for (int jj=1;jj<count;jj++)//从数组里面取出一个值给strtemp然后进行数据库的相关操作
{
CString strtemp,strR2,strR3,strResult,strFlag,strProcess;
_variant_t rule1[10],rule2[10],rule3[10],resoult[10],flag[10],show[10];
int recordcount,recordcount1,recordcount2;
strtemp=str[jj];
strSql.Format("select * from plant where rule2='%s'",strtemp);
adop.OpenRecordset(strSql);
//判断规则2有没有内容
//m_result.SetWindowText((_bstr_t)adop.m_pRecordset->GetCollect("rule2"));
recordcount=adop.m_pRecordset->GetRecordCount();
for (int mm=0;mm<recordcount;mm++)
{
rule1[mm]=adop.m_pRecordset->GetCollect("rule1");//得到的记录不止一个
rule3[mm]=adop.m_pRecordset->GetCollect("rule3");
resoult[mm]=adop.m_pRecordset->GetCollect("result");
flag[mm]=adop.m_pRecordset->GetCollect("flag");
show[mm]=adop.m_pRecordset->GetCollect("picture");
//查找rule1和rule3是否在数组里面,如果rule2和rule3都在数组里面,则把result放进数组里面
for (int i=0;i<count;i++)
{
if (str[i]==(LPCTSTR)(_bstr_t)rule1[mm])//判断rule2是否在数组里面
{
for (int k=0;k<count;k++)
{
if (str[k]==(LPCTSTR)(_bstr_t)rule3[mm])//判断rule3是否在数组里面
{
if ((LPCSTR)(_bstr_t)flag[mm]==(CString)"yes")////
{ //如果得到最后的结果了,输出结果并退出循环
strResult=(LPCTSTR)(_bstr_t)resoult[mm];
//m_result.SetWindowText(strResult);
resultShow=(LPCSTR)(_bstr_t)show[mm];
ResultC[ResultNum]=strResult;
ResultNum++;
//return;//退出for循环
}
else//如果flag="no",就把result放进数组里面
{
str[count]=(LPCTSTR)(_bstr_t)resoult[mm];//把结果放进数组里面去
count++;
//strProcess+=(_bstr_t)rule1[j]+(_bstr_t)rule2[mm]+(_bstr_t)rule3[mm]+"-->"+(_bstr_t)resoult[mm];
}
}
else
{
/*rule3不再数组里面的情况*/
}
}//for循环
}
else
{
/*rule2不在数组里面的情况*/
}
}
adop.m_pRecordset->MoveNext();
}
for (int p=1;p<count;p++)
{
strProcess+=" "+str[p];
}
m_process.SetWindowText(strProcess);
} CString strtemp,strR2,strR3,strResult,strFlag,strProcess;
//从rule3开始搜
for (int jjj=1;jjj<count;jjj++)//从数组里面取出一个值给strtemp然后进行数据库的相关操作
{
_variant_t rule1[10],rule2[10],rule3[10],resoult[10],flag[10],show[10];
int recordcount,recordcount1,recordcount2;
strtemp=str[jjj];
strSql.Format("select * from plant where rule3='%s'",strtemp);
adop.OpenRecordset(strSql);
//判断规则2有没有内容
//m_result.SetWindowText((_bstr_t)adop.m_pRecordset->GetCollect("rule2"));
recordcount=adop.m_pRecordset->GetRecordCount();
for (int mm=0;mm<recordcount;mm++)
{
rule1[mm]=adop.m_pRecordset->GetCollect("rule1");//得到的记录不止一个
rule2[mm]=adop.m_pRecordset->GetCollect("rule2");
resoult[mm]=adop.m_pRecordset->GetCollect("result");
flag[mm]=adop.m_pRecordset->GetCollect("flag");
show[mm]=adop.m_pRecordset->GetCollect("picture");
//查找rule2和rule3是否在数组里面,如果rule2和rule3都在数组里面,则把result放进数组里面
for (int i=0;i<count;i++)
{
if (str[i]==(LPCTSTR)(_bstr_t)rule1[mm])//判断rule2是否在数组里面
{
for (int k=0;k<count;k++)
{
if (str[k]==(LPCTSTR)(_bstr_t)rule2[mm])//判断rule3是否在数组里面
{
if ((LPCSTR)(_bstr_t)flag[mm]==(CString)"yes")////
{ //如果得到最后的结果了,输出结果并退出循环
strResult=(LPCTSTR)(_bstr_t)resoult[mm];
//m_result.SetWindowText(strResult);
resultShow=(LPCTSTR)(_bstr_t)show[mm];
ResultC[ResultNum]=strResult;
ResultNum++;
//return;//退出for循环
}
else//如果flag="no",就把result放进数组里面
{
str[count]=(LPCTSTR)(_bstr_t)resoult[mm];//把结果放进数组里面去
count++;
}
}
else
{
/*rule2不再数组里面的情况*/
}
}//for循环
}
else
{
/*rule1不在数组里面的情况*/
}
}
adop.m_pRecordset->MoveNext();
}
//输出
for (int p=1;p<count;p++)
{
strProcess+=" "+str[p];
}
m_process.SetWindowText(strProcess);
CString ShowResult;
if (ResultNum==0)
{
ShowResult="您选择的条件没有匹配的结果";
}
for (int r=0;r<ResultNum;r++)
{
if (ResultC[r]!=ResultC[0])
{
ShowResult="至少能推出两种的植物, ";
ShowResult+=ResultC[0]+" 和 "+ResultC[r];
ShowResult+=" 所以选择的条件组合是错误的!";
}
else
{
ShowResult=ResultC[r];
}
}
m_result.SetWindowText(ShowResult);
}
count=0;
//m_picture.GetBitmap();
}
//反向推理和添加新规则的相关代码略
七、参考文献
[1] 谭浩强编著,C++程序设计,清华大学出版社,2004
[2] 常明编著,计算机图形学(第三版),华中科技大学出版社,2009
[3] 王士同编著,人工智能教程(第2版),电子工业出版社,2006
人工智能九宫格重移搜索成员赵春杰20xx210665羊森20xx210653黄鑫20xx210周成兵20xx210664王素娟20…
人工智能课程实验指导书实验内容实验一产生式系统实验实验二移动机器人的路径规划与行为决策实验实验三梵塔问题实验实验四A算法实验实验五…
华北电力大学实验报告实验名称课程名称人工智能及应用专业班级学生姓名号成绩指导教师李继荣实验日期20xx5学华北电力大学实验报告华北…
人工智能第二次实验报告一实验题目遗传算法的设计与实现二实验目的通过人工智能课程的学习熟悉遗传算法的简单应用三实验内容用遗传算法求解…
人工智能技术实验报告实验名称人工智能实验1姓名班级指导教师完成时间20xx04301读程序指出运行结果domainsssymbol…
人工智能导论上机实验指导书基于人工智能的状态空间搜索策略研究八数码问题求解一实验软件TC20或VC60编程语言或其它编程语言二实验…
人工智能九宫格重移搜索成员赵春杰20xx210665羊森20xx210653黄鑫20xx210周成兵20xx210664王素娟20…
人工智能课程实验指导书实验内容实验一产生式系统实验实验二移动机器人的路径规划与行为决策实验实验三梵塔问题实验实验四A算法实验实验五…
华北电力大学实验报告实验名称课程名称人工智能及应用专业班级学生姓名号成绩指导教师李继荣实验日期20xx5学华北电力大学实验报告华北…
人工智能第二次实验报告一实验题目遗传算法的设计与实现二实验目的通过人工智能课程的学习熟悉遗传算法的简单应用三实验内容用遗传算法求解…