《用Prolog建專家系統》學習筆記(4)
來源:互聯網
上載者:User
《用Prolog建專家系統》學習筆記(4)四、說明解釋據說,專家系統的重要功能之一,是對其行為的自我解釋。這意味著,使用者在諮詢過程中,可以隨時向專家系統提問,為什麼它做出某個回答;系統則根據一定規則作出解釋。1、解釋功能對使用者的意義使用者不見得太需要解釋功能,往往他只需要答案。即使使用者要求解釋,這一解釋又非總是有用。2、解釋功能對開發人員的意義對於系統開發人員來說,解釋功能很重要,因為,專家可以通過解釋的內容,發現出錯的規則。3、解釋功能的類型專家系統通常有4種方式的解釋功能:● 在諮詢過程中,跟蹤應用的規則;● 解釋系統是如何得出某個結論的;● 解釋系統為何提出某個問題;● 解釋為何結論不確定。(一)Clam中的解釋功能
1、跟蹤為Clam增加的第一個解釋功能,是跟蹤規則的執行。它的表現,就像Prolog的跟蹤模組,告知使用者某個規則的“調用”、“退出”、“失敗”。它用到特殊的謂詞,bugdisp,向使用者傳達跟蹤資訊。bugdisp的參數,是“項”(term)的列表。bugdisp(L) :- ruletrace, %% 該原子值為true時,系統進入跟蹤狀態 write_line(L), !.bugdisp(_).write_line([]) :- nl.write_line([H|T]) :- write(H), tab(1), write_line(T).Clam系統級命令謂詞,go,調用下列新謂詞do,發出命令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(_).把跟蹤調用加入謂詞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).跟蹤調用的謂詞失敗時的捕捉處理:prove(N, IfList, Tally) :- prov(IfList, 100, Tally), !.prove(N, _, _) :- bugdisp(['fail rule', N]), fail.2、解釋系統是“如何(怎樣)”得出結論的系統得出結論,使用者提問系統後,會看到推理用的證據。證據的收集,一是來自對結論的專門跟蹤,二是來自黑板(工作資料庫)。Clam用的是第二種辦法,收集的證據,來自工作資料庫中的事實(fact)。各項事實產生於多項規則,這些規則終結於相同的“屬性-值”和複合信賴度。因此,事實的第3個參數,是儲存規則編號的列表。它不是完整的證明樹,但也直接得出了事實結論。fact(AV, CF, RuleList)更新事實用謂詞update。它的第4個參數是要更新的規則的編號。它的第1子句更新事實,將新的規則編號加入事實中。update(Goal, NewCF, CF, RuleN) :- fact(Goal, OldCF, _), combine(NewCF, OldCF, CF), retract( fact(Goal, OldCF, OldRules) ), asserta( fact(Goal, CF, [RuleN | OldRules]) ), !.update(Goal, CF, CF, RuleN) :- asserta( fact(Goal, CF, [RuleN]) ).從謂詞fg調用update,改用一新參數,規則編號:fg(Goal, CurCF) :- rule(N, lhs(IfList), rhs(Goal, CF)), ... update(Goal, NewCF, CurCF, N), ...既然相關規則來自工作資料庫中的事實,我們就可以回答使用者的問題:事實從何而來。要做的事極其簡單,即寫出用過的規則列表。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(_).詢問否定性目標是如何得出的謂詞,與否定性事實類似,也是用負值的CF。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將av與列表互相轉換。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命令,向使用者顯示與事實直接相關的規則集。這些規則似乎基於其他事實。顯示這些資訊的方式有2種:● 由使用者進一步提問各種規則LHS部分的目標是如何證明的,以深入證據樹內部;● 在相關謂詞中調用how命令,顯示完整的證據樹。至今,我們用的是第一種方法。為了實現第二種方法,需要寫謂詞how_lhs。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).3、對系統向使用者發出的提問,使用者反問它為什麼提問這個問題
使用者向系統提問how,是在推理完成之後。提問why,發生在規則鏈的底端,規則都已用完,於是系統向使用者提問。這時,使用者要求知道系統為什麼提出這個問題。為了使系統能回答這類why問題,必須跟蹤推理鏈。以下2個謂詞中的參數Hist,就是由跟蹤到的規則構成的列表。它的初始值是個空表。findgoal(Goal, CurCF, Hist) :- fg(Goal, CurCF, Hist).fg(Goal, CurCF, Hist) :- ... prove(N, IfList, Tally, Hist), ...prove(N, IfList, Tally, Hist) :- prov(IfList, 100, Tally, [N|Hist]), !.prove(N, _, _) :- bugdisp(['fail rule', N]), fail.prov([], Tally, Tally, Hist). prov([H|T], CurTal, Tally, Hist) :- findgoal(H, CF, Hist), min(CurTal, CF, Tal), Tal >= 20, prov(T, Tal, Tally, Hist).get_user(X, Hist) :- repeat, write(': '), read_line(X), process_ans(X, Hist).process_ans([why], Hist) :- nl, write_hist(Hist), !, fail.process_ans([trace, X], _) :- set_trace(X), !, fail.process_ans([help], _) :- help, !, fail.process_ans(X, _). % just return user's 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).