Control 版 (精华区)
发信人: dagam (断情), 信区: Control
标 题: [转载]专家系统解释[2]
发信站: 哈工大紫丁香 (2001年07月01日11:49:59 星期天), 站内信件
再来看看how提问,当系统给出了某个结论的时候,用户可能想知道是如何得到这个结
论的,这个时候向系统询问how。
...
problem-battery-cf-75
done with problem
consult, restart, load, list, trace, how, exit
:how
Goal? problem is battery
problem 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
:how
Goal? battery_bad
battery_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是否为真。因此在
我们的外壳程序中就又多了一个打开或者关闭跟踪功能的命令。然后我们就可以把bugd
isp放在任何想要显示跟踪信息的地方了。
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,也就是说是哪个规则支持的需要更新的这个fa
ct。
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对应的rulelis
t给列出来,不过如果我们能够把规则的内容也显示出来的话,这样就更加方便了。
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, !.
--
--
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: heart.hit.edu.cn]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.449毫秒