




已阅读5页,还剩10页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Prolog开发专家系统-解释专家系统的一个重要的功能就是要能够解释它自己的行为。用Prolog开放具体解释能力的专家系统。解释专家系统的一个重要的功能就是要能够解释它自己的行为。这意味着用户可以在任何时候询问系统为什么得出某个结论,或者为什么提出某个问题。这对于用户来说是一项重要的功能,有时候用户只要求知道答案,可是有时候用户需要知道解释,而通常的专家系统无法对它的行为做出有说服力的解释,而只能够告诉用户它使用了哪些规则得出的结论,至于为什么这些规则能够得出这样的结论,系统是无法解释的。例如下面这个例子:汽车能够启动么? 不行引擎发动了么? 是的你问到汽油味道了么?是的建议:等待5秒钟,然后再试。为什么?因为我使用了这样的规则:如果不能够启动而且引擎发动了而且问到汽油味,那么就推荐的等待5秒再试。 很显然这个专家系统无法解释其选择某个规则的原因,而只能告诉用户它使用了某种规则。如果用户硬要刨根问底的话这个系统就无能为力了。为了让系统具有真正的解释功能,我们需要比规则更多的知识。对每个规则进行注释是一个比较好的方法,这种方法将在以后的章节介绍。还有一种方法就是把更多的知识进行编码,推理引擎和解释引擎都同时使用这个知识库。还有些专家系统的知识库是属于经验知识,在这种情况下系统的解释可以直接使用规则。像识别鸟类的分类系统就属于这种情况。鸟类识别系统就能够使用它的规则直接进行解释,例如为什么某种鸟是野鸭,就是因为它具有野鸭的一些特性,而这些特性就是规则所定义的。识别鸟类并不存在什么高深的理论,而只是根据某些特点进行分类的。也许对于用户来说某些解释是多余的,不过对于开发人员来说这是十分重要的。这和通常的语言中的跟踪调试有些类似。当系统没有按照预期的效果执行的时候,开发人员可以根据解释研究错误的产生原因。知识工程师也可以根据解释从而设计出更加贴近用户的知识库。解释的种类在一般的专家系统中常用的有4种解释。1. 报告当前的会话进程。2. 解释系统是如何得出某个结论的。3. 解释为怎么系统向用户询问某个问题。4. 解释为什么某个结论不成立。在我们上一章介绍的Clam外壳程序中,推理引擎是自己编写的,所以这些解释特性并不难加入系统当中。在第一章的原始外壳中没有推理引擎,而是使用prolog的内部引擎,这样就无法加入新的解释特性,为了达到这个目的,我们需要编写自己的推理引擎,而这个引擎的运作方式和prolog相同,也就是说需要使用prolog编写一个prolog,好在这项工作并不难完成。在Clam中使用解释首先让我们看看在Clam中加入了解释的一个例子,这里沿用了上一章汽车诊断系统。 首先用户打开对话跟踪功能,跟踪的信息使用粗体字表示,跟踪信息显示了系统是如何调用规则的。注意系统正确的表示出了规则的嵌套调用。报告当前的会话进程的解释:consult, restart, load, list, trace, how, exit:trace onconsult, restart, load, list, trace, how, exit:consultcall rule 1 Does the engine turn over?: nocall rule 2 Are the lights weak?: yesexit rule 2 call rule 3 Is the radio weak?: yesexit rule 3 exit rule 1 call rule 4 fail rule 4call rule 5 fail rule 5call rule 6 fail rule 6problem-battery-cf-75done with problem下面来看看如何解释为什么要向系统提问。用户可以在任何时候向推理引擎询问why,请看这个例子:.Is the radio weak?: whyrule 3 If radio_weak Then battery_bad 50 rule 1 If not turn_over battery_bad Then problem is battery 100 goal problem .这里可以看出来当用户向系统询问为什么问is the radio weak这个问题的时候,系统把有关这个问题的几个规则列出来了。再来看看how提问,当系统给出了某个结论的时候,用户可能想知道是如何得到这个结论的,这个时候向系统询问blem-battery-cf-75done with problemconsult, restart, load, list, trace, how, exit:howGoal? problem is batteryproblem is battery was derived from rules: 1 rule 1 If not turn_over battery_bad Then problem is battery 100 在这里列出了能够直接得到结论的规则。如果用户需要继续知道为什么battery_bad的话,就进行下面的询问:consult, restart, load, list, trace, how, exit:howGoal? battery_badbattery_bad was derived from rules: 3 2 rule 3 If radio_weak Then battery_bad 50 rule 2 If lights_weak Then battery_bad 50 在这里有两个规则可以得到battery_bad的结论,系统把它们都列举出来了。看完了示范,该是研究程序的时候了。 跟踪首先我们来看看如何制作跟踪功能。这个跟踪功能可以向用户报告某个规则的调用、退出以及失败几个事件。 这里使用谓词bugdisp来向用户显示跟踪信息,它的参数是一个要显示出来的列表。为了让用户可以选择是否打开跟踪功能,bugdisp首先检查ruletrace是否为真。因此在我们的外壳程序中就又多了一个打开或者关闭跟踪功能的命令。然后我们就可以把bugdisp放在任何想要显示跟踪信息的地方了。bugdisp(L) :- ruletrace,write_line(L),!.bugdisp(_).write_line() :- nl.write_line(H|T) :-write(H),tab(1),write_line(T).然后我们在外壳程序中加入trace(on)和trace(off)两个命令。do( trace(X) ) :- set_trace(X), !.set_trace(off) :- ruletrace,retract( ruletrace ).set_trace(on) :-not ruletrace,asserta( ruletrace ). set_trace(_). 现在我们已经编写好了可以显示跟踪信息的谓词了。下面我们就需要把bugdisp放入到适当地方,让它显示出跟踪信息。在上一章介绍的谓词fg中很容易找到规则被调用和规则成功的地方。下面就是增加了跟踪功能的fg谓词。fg(Goal, CurCF) :- rule(N, lhs(IfList), rhs(Goal, CF),bugdisp(call rule, N),prove(N, IfList, Tally),bugdisp(exit rule, N),adjust(CF, Tally, NewCF),update(Goal, NewCF, CurCF, N),CurCF = 100, !. fg(Goal, CF) :- fact(Goal, CF). 当某个规则的目标满足fg的目标的时候,这个规则就被调用,所以在rule后面加入call rule。当这个规则的前提都得到证实的时候这个规则就成功了,因此在prove后面加入exit rule。那么规则在什么时候失败呢?在prove失败的时候,规则就失败了,因此我们加入一个处理prove失败的子句:prove(N, IfList, Tally) :- prov(IfList, 100, Tally), !. prove(N, _, _) :-bugdisp(fail rule, N),fail.注意上面的第二个子句就是新加入的,当第一个子句失败的时候,就调用这个子句,它首先显示失败信息,然后再失败。回答how问题当用户想知道系统是如何得出某个结论的时候,可以向系统提问how。实现这种方法有两个途径,一种当用户询问how的时候重新跟踪系统的调用过程,另外一种则是把推理过程直接保存在工作空间中。我们使用后面这种方法。在我们的工作空间中原来的储存信息格式如下:fact(AV,CF).它只保存了属性信息和确信度信息,由于现在我们要回答用户是如何得到某个结论的,因此我们要加入第三个参数RuleList,改进后的fact格式如下:fact(AV,CF,RuleList).RuleList用来保存推理出这个fact所使用的规则列表。这里的RuleList不是整个求解树,而是是直接推导出这个fact的规则。fact是使用update谓词更新的,因此需要改写update。我们给update新加入一个参数用来告诉update是哪个规则引起的update,也就是说是哪个规则支持的需要更新的这个fact。update(Goal, NewCF, CF, RuleN) :- fact(Goal, OldCF, _), %如果已经存在这个fact,combine(NewCF, OldCF, CF),retract( fact(Goal, OldCF, OldRules) ), %就取得原来的rulelist,asserta( fact(Goal, CF, RuleN | OldRules) ), !. %并且添加新的Rule进去。update(Goal, CF, CF, RuleN) :- %否则就是系统中不存在这个fact,asserta( fact(Goal, CF, RuleN) ). %就直接添加入工作空间。调用update的谓词fg也要相应的有所改动:fg(Goal, CurCF) :- rule(N, lhs(IfList), rhs(Goal, CF),. update(Goal, NewCF, CurCF, N), %把规则名传递给update。. 下面再来编写处理用户的how命令,最简单的办法就是把用户询问的Goal对应的rulelist给列出来,不过如果我们能够把规则的内容也显示出来的话,这样就更加方便了。how(Goal) :-fact(Goal, CF, Rules),CF 20,pretty(Goal, PG),write_line(PG, was, derived, from, rules: |Rules),nl, list_rules(Rules),fail.how(_). how(not Goal) :- fact(Goal, CF, Rules),CF -20,pretty(not Goal, PG), write_line(PG, was, derived, from, rules: |Rules),nl, list_rules(Rules), fail. pretty谓词用来把属性值结构转化为更容易的阅读的列表。pretty(av(A, yes), A) :- !. pretty(not av(A, yes), not, A) :- !.pretty(av(A, no), not, A) :- !.pretty(not av(A, V), not, A, is, V). pretty(av(A, V), A, is, V). list_rules用来显示出规则的内容。list_rules(). list_rules(R|X) :-list_rule(R), list_rules(X).list_rule(N) :-rule(N, lhs(Iflist), rhs(Goal, CF), write_line(rule , N),write_line(If),write_ifs(Iflist),write_line(Then),pretty(Goal, PG),write_line( , PG, CF), nl.write_ifs().write_ifs(H|T) :- pretty(H, HP), tab(5), write_line(HP),write_ifs(T). 我们还可以反过来使用pretty,也就是说把用户输入的列表,转换为属性值的结构,这样的话,用户就不需要知道系统内部是如何表达知识的了。how :-write(Goal? ),read_line(X), nl,pretty(Goal, X),how(Goal).最后把how命令加入外壳程序的命令列表:do(how) :- how, !. 上面就是完整的how的编写过程了。不过它只显示出直接推导出某个规则,而这些规则又是基于其他的规则或者事实的,如何进一步的推理信息呢?有两种方法:让用户使用how继续询问,让how命令自动的显示完整的证明树。第一项功能已经实现,如何实现第二个功能呢?我们使用how_lhs把rulelist中的每个规则作为目标,递归的调用how。当某个目标的rulelist为空的时候,表示这个目标不是由规则得出的,而是用户输入的已知事实,这就是说完成了整个证明树的搜索过程。list_rules().list_rules(R|X) :-list_rule(R),how_lhs(R), list_rules(X).how_lhs(N) :-rule(N, lhs(Iflist), _),!, how_ifs(Iflist). how_ifs().how_ifs(Goal|X) :- how(Goal), how_ifs(X). 在这里我们回答how提问有3种选择:只显示规则名,显示规则的内容,显示完整的搜索树。回答why提问当系统得出某个结论之后,用户可以使用how向它询问是如何得出这个结论的。而在系统的用户的对话过程中,系统为了收集资料会向用户询问,这个时候如果用户感到困惑的时候,可以询问系统为什么问这个问题。为了回答why问题,我们需要跟踪推理过程,也就是说要记录下以前推理一些信息。在推理谓词中增加一个保存这种信息的参数,就可以很好的解决这个问题。在findgoal和prove中我们增加了一个Hist参数。findgoal(Goal, CurCF, Hist) :- fg(Goal, CurCF, Hist). fg(Goal, CurCF, Hist) :-.prove(N, IfList, Tally, Hist),.在prove谓词中,在递归调用findgoal去寻找进一步的解的前面,先把现在所使用的规则记录下来。prove(N, IfList, Tally, Hist) :- prov(IfList, 100, Tally, N|Hist), !.prove(N, _, _) :-bugdisp(fail rule, N), v(, Tally, Tally, Hist).prov(H|T, CurTal, Tally, Hist) :-findgoal(H, CF, Hist),min(CurTal, CF, Tal), Tal = 20, prov(T, Tal, Tally, Hist). 我们举个例子说明一下,当系统需要证明a的时候,它发现要使用b,c。当它开始着手证明b之前,先把a记录下来,这样当用户询问系统你为什么要证明b的时候,它就可以告诉用户:我是要证明a才来证明b的。假如证明b又需要d和e,在在证明d和e之前,先把b记录下来,而d和e是需要向用户收集的信息,用户可能会询问为什么要问我d和e这样的问题,这时系统中的历史列表应该是b,a,系统查询这个列表就会告诉用户,我需要证明b,因此询问d和e,而用户问为什么要证明b呢,系统就会回答是要证明a。显然有了这个历史列表,系统就知道自己为什么要获得某个属性的值了。 由于why命令是用户在和系统的对话的中间询问的,和原来how命令有些不同。原来的外壳命令help、exit、how等等,都不能在对话中间使用,下面我们就修改原来的程序,让用户可以打断和系统的对话,来做其他的事情。用下面的get_user谓词替代以前的向用户询问的谓词,这个get_user谓词允许在对话中间运行why,trace,help命令,当然加入其他的命令也是不困难的。 get_user(X, Hist) :-repeat, write(: ),read_line(X),process_ans(X, Hist).process_ans(why, Hist) :- nl, write_hist(Hist), !, cess_ans(trace, X, _) :- set_trace(X), !, cess_ans(help, _) :- help, !, cess_ans(X, _). % just return users answerwrite_hist() :- nl.write_hist(goal(X)|T) :- write_line(goal, X), !, write_hist(T).write_hist(N|T) :- list_rule(N), !,write_hist(T). 在回答why的时候,不仅仅显示规则名,还可以显示规则的内容。在原始的推理引擎中加入解释还记得第一章介绍的那个识别鸟类的专家系统么,由于这个专家系统使用的是prolog的推理引擎,所以无法加入解释功能,为了加入解释,我们必须使用prolog编写一个prolog解释器,这项工作很容易完成。当编写完成了自己的推理引擎之后,就可以很方便的处理解释了。推理引擎首先要能够读取规则,在prolog中,子句本身就是prolog的项。内部谓词clause可以让我们存取规则。它的两个参数分别与子句的头和内容匹配。事实的内容就只有目标ture一个。在prolog的语法中,使用“,”隔开规则的每个子目标,其实在prolog中,规则的储存方法和我们看上去的有很大的不同。下面我们举个例子。对于规则a:-b,c,d.在prolog中的实际结构是:-(a,&(b,&(c,d).可以看出,这和我们自己定义的数据结构信息是相同的。例如我们可能会定义:father(a,b).在这里father就是谓词,a和b就是参数。而在规则中,:-是谓词,&也是谓词。这一点在前面的prolog语言介绍中过。有了上面的知识,我们就很容易编写出递归处理每个子目标的程序了。recurse(FirstGoal & RemainingGoals) :- process(FirstGoal),recurse(RemainingGoals).recurse(SingleGoal) :-process(SingleGoal). 这里使用&是为了读者不把prolog中的两种逗号搞混淆,一种逗号是用来把谓词的参数分开的例如:father(a,b). 另外一种则是用来连接规则中的两个子目标的, 表示并且的意思,这种逗号实际上是谓词。有了可以存取prolog的事实和规则的方法以后,我们就可以很容易的编写出处理这些事实和规则的谓词了。prove(true) :- !. prove(Goal, Rest) :-clause(Goal, Body), %找到和目标匹配的子句prove(Body), %证明Body部分。prove(Rest). %证明上一层目标的剩余部分。prove(Goal) :-clause(Goal, Body), %找到和目标匹配的子句。prove(Body). %证明这个子句的Body部分。注意,prove谓词正好模拟了prolog的解题过程。首先他找到头部和第一个目标匹配某个子句。然后试图证明这个子句的目标列表。上面的这个解释器只能够处理纯prolog子句。对于prolog的内部谓词无能为力。因此最后我们加上一条:prove(X):-call(X).用来调用内部谓词。在我们的这个外壳程序中并不打算使用prolog的内部谓词,不过因为需要调用ask和menuask这样的谓词来和用户对话,这些谓词对于上面的解释器就可以被认为是内部谓词了。和前面的Clam一样,我们加入参数Hist来回答用户的why提问。prove(true, _) :- !.prove(Goal, Rest), Hist) :-prov(Goal, (Goal, Rest),prove(Rest, Hist).prov(true, _) :- !.prov(menuask(X, Y, Z), Hist) :- menuask(X, Y, Z, Hist), !.prov(ask(X, Y), Hist) :- ask(X, Y, Hist), !. prov(Goal, Hist) :- clause(Goal, List), prove(List, Goal|Hist). 注意这里的历史记录保存的是目标列表,而不是规则名。下面来修改顶层的谓词。solve :-abolish(known, 3), define(known, 3),prove(top_goal(X), ),write(The answer is ), write(X), nl.solve :- write(No answer found), nl.处理why提问的程序和clam类似。 get_user(X, Hist) :-repeat,read(X),process_ans(X, Hist), !.p
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 离婚协议条款细节及范文解析
- 2025江苏盐城市妇女儿童活动中心选调2人考试模拟试题及答案解析
- 2025中铁众德(衡水)教育咨询服务有限公司招聘4人考试模拟试题及答案解析
- 农民合作社农田水利设施维护合同
- 2025年大庆市第二医院招聘工作人员6人备考模拟试题及答案解析
- 2025济南市历城区兴元小学补充顶岗教师备考练习试题及答案解析
- 2025贵州遵义市播州区自然资源局招聘城镇公益性岗位人员3人考试模拟试题及答案解析
- 2025新疆乌鲁木齐市高新区(新市区)银龄教师招募8人考试参考题库及答案解析
- 2025-2030餐饮直播带货转化率分析及内容创意与供应链响应速度评估
- 2025黑龙江齐齐哈尔市泰来县城镇建设服务中心招聘市政园林养护人员5人考试模拟试题及答案解析
- 小升初重点专题立体图形计算题(专项训练)-小学数学六年级下册苏教版
- 数字媒体技术就业
- 2025年食品流通单位食品安全管理人员抽查考核试题(附答案)
- 2025年高考化学四川卷试题答案解读及备考指导(精校打印)
- 2025年上海见证取样考试题库
- 农产品检验员试题及答案
- 急诊质控工作汇报
- 2025年危险运输三级教育试题及答案
- 新疆维稳管理办法
- 2025企业级AI Agent(智能体)价值及应用报告
- 云南省高中学业水平考试数学考题分类汇编以及知识点穿插(2025年7月-2026年1月)
评论
0/150
提交评论