三、后向链非确定性推理
现实世界中,事物往往不是非此即彼的简单逻辑关系,而是与专家的经验直觉相关的非确定性关系。
以下,介绍一个后向链非确定性推理的专家系统,Clam,它有自己独特的规则格式和推理机。
(一)置信度(Certainty Factors)
处理非确定性问题的最常用的办法,是给系统中的每条信息加上置信度。推理机自动更新维护置信度,并将其作为推理产生的结果。
Clam 的一个用例:
置信度(cf)取值-100(全假)至+100(全真)。
以下是Clam中的小型知识库,用于诊断汽车为何不点火启动。它描述了处理非确定性的一些方法。
goal problem.
rule 1
if not turn_over and battery_bad
then problem is battery.
rule 2
if lights_weak
then battery_bad cf 50.
rule 3
if radio_weak
then battery_bad cf 50.
rule 4
if turn_over and smell_gas
then problem is flooded cf 80.
rule 5
if turn_over and gas_gauge is empty
then problem is out_of_gas cf 90.
rule 6
if turn_over and gas_gauge is low
then problem is out_of_gas cf 30.
ask turn_over
menu (yes no)
prompt 'Does the engine turn over?'.
ask lights_weak
menu (yes no)
prompt 'Are the lights weak?'.
ask radio_weak
menu (yes no)
prompt 'Is the radio weak?'.
ask smell_gas
menu (yes no)
prompt 'Do you smell gas?'.
ask gas_gauge
menu (empty low full)
prompt 'What does the gas gauge say?'.
非确定性规则
以下是用户与汽车专家系统的咨询对话:
consult, restart, load, list, trace, how, exit
:consult
Does the engine turn over?
: yes
Do you smell gas?
: yes
What does the gas gauge say?
empty
low
full
: empty
problem-out_of_gas-cf-90
problem-flooded-cf-80
done with problem
注意,这个推理机不像Prolog那样只管找出一个答案,而是找出全部合理答案,并且报告答案的可信程度。可见,置信度不是概率值,而是简单地对各个答案的可信性给出一个权重。
用户定义的置信度,可以置入系统:
:consult
Does the engine turn over?
: yes
Do you smell gas?
: yes cf 50
What does the gas gauge say?
empty
low
full
: empty
problem-out_of_gas-cfproblem-flooded-cf-40
-90
done with problem
复合置信度:
:consult
Does the engine turn over?
: no
Are the lights weak?
: yes
Is the radio weak?
: yes
problem-battery-cf-75
done with problem
该例以2条规则,确定“电池缺电”的置信度是75。
置信度的用途范围:
● 结论不确定的规则;
● 前提不确定的规则;
● 用户输入的不确定数据;
● 前提和结论都不确定的规则;
● 用不确定的信息,更新黑板中的不确定数据;
● 将已知的前提设为不确定。
(二)MYCIN的置信度
(三)规则格式
自行建造推理机,必须自行设计内部规则格式。
自定规则格式,至少要有2个参数:一个是前提(LHS),另一个是结论(RHS)。为了更实用,再加上第3个参数,规则编号或名称。于是,
完整的规则结构是:
rule(Name, LHS, RHS).
结论RHS包括一个目标模式及其相应的置信度CF:
rhs(Goal, CF)
前提LHS可以包括许多子目标,用于肯定或否定RHS:
lhs(GoalList)
表示子目标的最简单方式,是用“属性-值”配对:
av(Attribute, Value)
其中的2个参数是简单的原子。
完整的规则结构是这样的:
rule(Name,
lhs( [av(A1, V1), av(A2, V2), ....] ),
rhs( av(Attr, Val), CF) ).
上述的规则五(rule 5)是这样:
rule(5,
lhs( [av(turns_over, yes), av(gas_gauge, empty)] ),
rhs( av(problem, flooded), 80) ).
(四)推理机
有了固定格式的规则,就需要自造的推理机处理它们。处理行为包括:
● 把置信度综合起来;
● 维护工作数据库(黑板),更新信息作为推理所需的新证据;
● 找出用户所需的全部特定信息,存入黑板。
主要的谓词如图3.1所示:
工作数据库:fact( av(A, V), CF).
找出属性对应的值,例如:
?- findgoal( av(problem, X), CF).
该谓词必须处理3种情况:
● 已知的“属性-值”;
● 有谓词可推断“属性-值”;
● 必须询问用户。
可以定义新谓词:
askable(live, 'Where does it live?').
要问的问题是live,问题的提示是'Where does it live?'
于是,可写出谓词findgoal。
已知属性值:
findgoal( av(Attr, Val), CF) :- fact( av(Attr, Val), CF), !.
向用户询问属性值:
findgoal(av(Attr, Val), CF) :- not fact(av(Attr, _), _),
askable(Attr, Prompt),
query_user(Attr, Prompt),
!,
findgoal(av(Attr, Val), CF).
谓词query_user提示用户,输入属性值和置信度CF,并将其声明(保存)为“事实”。递归调用findgoal会用到这一事实。
query_user(Attr, Prompt) :- write(Prompt),
read(Val),
read(CF),
asserta( fact(av(Attr, Val), CF)).
用规则推论出属性值:
findgoal(Goal, CurCF) :- fg(Goal, CurCF).
fg(Goal, CurCF) :- rule(N, lhs(IfList),
rhs(Goal, CF)),
prove(IfList, Tally),
adjust(CF, Tally, NewCF),
update(Goal, NewCF, CurCF),
CurCF == 100,
!.
fg(Goal, CF) :- fact(Goal, CF).
在fg中有3个新谓词:
● prove – 对LHS前提作出证明,并且给出其置信度CF;
● adjust – 合并LHS的CF和RHS的CF;
● update – 用新的结论,更新现有工作数据库的值。
prove(IfList, Tally) :- prov(IfList, 100, Tally).
prov([], Tally, Tally).
prov([H|T], CurTal, Tally) :- findgoal(H, CF),
min(CurTal, CF, Tal),
Tal >= 20,
prov(T, Tal, Tally).
min(X, Y, X) :- X =< Y,
!.
min(X, Y, Y) :- Y =< X.
After prove succeeds, adjust computes the combined CF based on the RHS CF and the Tally from the LHS.
成功证明后,调整复合CF值。
adjust(CF1, CF2, CF) :- X is CF1 * CF2 / 100,
int_round(X, CF).
int_round(X, I) :- X >= 0,
I is integer(X + 0.5).
int_round(X, I) :- X < 0,
I is integer(X - 0.5).
然后,谓词update 将已知证据与新证据进行整合。第一个参数是推论得出的“属性-值”对,第二个参数是其CF。第三个参数合并CF后,返回的新CF值。
update(Goal, NewCF, CF) :- fact(Goal, OldCF),
combine(NewCF, OldCF, CF),
retract( fact(Goal, OldCF) ),
asserta( fact(Goal, CF) ),
!.
update(Goal, CF, CF) :- asserta( fact(Goal, CF) ).
combine(CF1, CF2, CF) :- CF1 >= 0,
CF2 >= 0,
X is CF1 + CF2*(100 - CF1)/100,
int_round(X, CF).
combine(CF1, CF2, CF) :- CF1 < 0,
CF2 < 0,
X is - ( -CF1 -CF2 * (100 + CF1)/100),
int_round(X, CF).
combine(CF1, CF2, CF) :- (CF1 < 0; CF2 < 0),
(CF1 > 0; CF2 > 0),
abs_minimum(CF1, CF2, MCF),
X is 100 * (CF1 + CF2) / (100 - MCF),
int_round(X, CF).
表示否定的CF负值:
findgoal(not Goal, NCF) :- findgoal(Goal, CF),
NCF is - CF,
!.
(五)实现Clam外壳
super :- repeat,
write('consult, load, exit'), nl,
write(':'),
read_line(X),
doit(X),
X == exit.
doit(consult) :- top_goals,
!.
doit(load) :- load_rules,
!.
doit(exit).
调用top_goals,开始推理:
top_goals :- top_goal(Attr),
top(Attr),
print_goal(Attr),
fail.
top_goals.
top(Attr) :- findgoal(av(Attr, Val), CF),
!.
top(_) :- true.
print_goal(Attr) :- nl,
fact(av(Attr, X), CF),
CF >= 20,
outp(av(Attr, X), CF),
nl,
fail.
print_goal(Attr) :- write('done with '),
write(Attr), nl, nl.
outp(av(A, V), CF) :- output(A, V, PrintList),
write(A-'cf'-CF),
printlist(PrintList),
!.
outp(av(A, V), CF) :- write(A-V-'cf'-CF).
printlist([]).
printlist([H|T]) :- write(H),
printlist(T).
(六)英语化的规则
load_rules(F) :- clear_db, see(F),
lod_ruls,
write('rules loaded'), nl,
seen,
!.
lod_ruls :- repeat,
read_sentence(L),
process(L),
L == eof.
process(eof) :- !.
process(L) :- trans(R, L, []),
assertz(R),
!.
process(L) :- write('translate error on:'), nl,
write(L), nl.
clear_db :- abolish(top_goal, 1),
abolish(askable, 4),
abolish(rule, 3).