Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Оболочка IMP

.pdf
Скачиваний:
5
Добавлен:
27.03.2015
Размер:
361.17 Кб
Скачать

11

Здесь связи между узлами отображают шаги цепочки рассуждений. Например, связи узла c1 отображают импликацию

c (¬ e1) e2 , ct(IMP) = 0.9, признак обратимости = r (обратимая). Эту импликацию можно представить в программе на языке Prolog в виде факта

imp(a,r,c1,neg,e1,pos,e2,0.9).

Здесь 1-ый аргумент – тип посылки импликации (a – конъюнкция, o – дизъюнкция, s – простая посылка); 2-ой аргумент – признак обратимости импликации (r – обратима, n – необратима); 3-ий аргумент – заключение импликации (текст); 4-ый аргумент – признак отрицания первого условия посылки (neg – отрицание, pos – нет отрицания); 5-ый аргумент – первое условие посылки (текст); 6-ой аргумент – признак отрицания второго условия посылки; 7-ой аргумент – второе условие посылки (текст); 8-ой аргумент – КО импликации.

Примеры:

imp(s,r,c3,pos,e5,dummy,dummy,0.6). imp(o,r,c6,pos,c1,pos,c2,0.6).

Заключение, поддерживаемое двумя импликациями:

imp(s,r,c8,pos,с6,dummy,dummy,0.8). imp(a,r,c8,pos,e4,pos,c3,0.7).

Узлы высшего уровня отображают проверяемые гипотезы и представляются как факты:

hypothesis_node(c7). hypothesis_node(c7).

Узлы низшего уровня отображают условия внешнего мира и представляются как факты:

terminal_node(e1). terminal_node(e2).

...

Теперь можно записать правила для вычисления КО заключения для импликаций трех базовых типов:

для импликации с простой посылкой:

infer(Node1,Ct):– imp(s,Use,Node1,Sign,Node2,_,_,C1), allinfer(Node2,C2), find_multiplier(Sign,Mult,dummy,0), Ct=Mult*C2*C1.

для импликации с посылкой в форме конъюнкции:

infer(Node1,Ct):–

imp(a,Use,Node1,SignL,Node2,SignR,Node3,C1),

allinfer(Node2,C2),

allinfer(Node3,C3), find_multiplier(SignL,MultL,SignR,MultR), C2S=MultL*C2,

C3S=MultR*C3,

min(C2S,C3S,CE),

Ct=CE*C1.

12

для импликации с посылкой в форме дизъюнкции:

infer(Node1,Ct):–

imp(o,Use,Node1,SignL,Node2,SignR,Node3,C1),

allinfer(Node2,C2),

allinfer(Node3,C3), find_multiplier(SignL,MultL,SignR,MultR), C2S=MultL*C2,

C3S=MultR*C3,

max(C2S,C3S,CE),

Ct=CE*C1.

Определим правило, которое возвращает КО узла, входящего в посылку импликации. Знак КО должен изменяться, если связь от узла содержит отрицание. Для этого КО умножается на коэффициент, возвращаемый правилом find_multiplier:

для импликации с простой посылкой:

find_multiplier(pos,1,dummy,0). find_multiplier(neg,–1,dummy,0).

для импликации с посылкой в форме конъюнкции или дизъюнкции :

find_multiplier(pos,1,pos,1). find_multiplier(pos,1,neg,–1). find_multiplier(neg,–1,pos,1,). find_multiplier(neg,–1,neg,–1).

Будем применять правило infer для определения значений КО не только для промежуточных, но и для терминальных узлов сети вывода. Поскольку программа не может вычислить значение КО терминального узла, она должна попытаться извлечь его из базы данных (факты на основе предиката evidence) и в случае неудачи запросить его у пользователя. Поэтому правило infer для терминальных узлов имеет следующий вид:

infer(Node,Ct):– terminal_node(Node), evidence(Node,Ct),!.

infer(Node,Ct):– terminal_node(Node),

write(”Для данного условия:”),nl, write(Node),nl,

write(”Введите значение КО от –1 до +1: ”), readreal(Ct),

asserta(evidence(Node,Ct),!.

Определим правило allinfer, которое обрабатывает ситуации, когда несколько импликаций поддерживают один узел (например, узел c7 на рисунке). Это правило находит все импликации, поддерживающие узел, и по каждому из них вычисляет частный КО узла. Затем оно собирает все эти частные КО узла в список Ctlist, обрабатывает его и получает общее значение КО узла Ct. Таким образом, это правило имеет следующий вид:

allinfer(Node,Ct):–

findall(C1,infer(Node,C1),Ctlist), combine(Ctlist,Ct).

13

Здесь стандартный предикат findall формирует список Ctlist. Правило combine, предполагая, что список Ctlist содержит не более двух частных КО узла (считая, что не более двух импликаций поддерживают узел), на их основе вычисляет общий КО узла по формуле (1.8). Поэтому оноимеет следующий вид:

combine([Ct],Ct).

/*если в списке только один КО*/

combine([-1,1],0).

/*противоречие двух КО*/

combine([1,-1],0).

/*противоречие двух КО*/

combine([C1,C2],Ct):–

 

C1>=0,C2>=0,

 

Ct=C1+C2-C1*C2.

 

combine([C1,C2],Ct):–

 

C1<0,C2<0,

 

Ct=C1+C2+C1*C2.

 

combine([C1,C2],Ct):–

 

C1<0,C2>=0,

 

abs(C1,Z1),abs(C2,Z2),

min(Z1,Z2,Z3), Ct=(C1+C2)/(1-Z3).

combine([C1,C2],Ct):–

C1>=0,C2<0,

abs(C1,Z1),abs(C2,Z2),

min(Z1,Z2,Z3), Ct=(C1+C2)/(1-Z3).

Для обобщения правила combine на случай, когда узел поддерживается произвольным числом импликаций, подходит метод дополнения. В нем общий КО заключения, поддерживаемого многими правилами, вычисляется в два этапа:

1)создается список частных КО по всем правилам, поддерживающим заключение;

2)элементы списка последовательно попарно объединяются, пока в списке не останется один элемент, который и является искомым общим КО заключения.

Пример:

Пусть 3 импликации дают для заключения положительные частные КО ct1, ct2 и ct3 соответственно.

Исходный список: [ct1, ct2, ct3 ].

Результат объединения первой пары: ct(1) = ct1 + ct2 – ct1 · ct2;

Укороченный список: [ct(1), ct3 ].

Результат объединения второй пары: ct(2) = ct(1) + ct3 – ct(1) · ct3; Укороченный список: [ct(2) ]. Расчет завершен.

Общий КО заключения равен ct(2).

Для реализации метода дополнения надо добавить в программу следующее правило:

supercombine([Ct],Ct):–!.

supercombine([C1,C2],Ct):– combine([C1,C2],Ct).

supercombine([C1,C2|T],Ct):–

combine([C1,C2],C3),

append([C3],T,TL), /* здесь TL=[C3|T] */ supercombine(TL,Ct).

14

Примечание:

Здесь стандартный предикат append([L1,L2,L) создает список L путем присоединения к списку L1 списка L2 в качестве ”хвоста”, т.е. L=[L1|L2].

Таким образом, если заключение поддерживается одним или двумя правилами, то правило supercombine работает как правило combine, а если более, чем двумя правилами, то реализует метод дополнения.

Теперь, чтобы сделать правило allinfer универсальным, достаточно заменить в нем обращение к правилу combine на обращение к правилу supercombine.

До сих пор все импликации в правилах infer предполагались обратимыми. Чтобы правила infer могли обрабатывать как обратимые, так и необратимые импликации, надо добавить в правила infer обращение к правилу qualifier, которое определяется так:

qualifier(Use,C,Qmult):–

Use=r,Qmult=1,!.

qualifier(Use,C,Qmult):–

Use=n,C>=0,Qmult=1,!.

qualifier(Use,C,Qmult):–

Use=n,C<0,Qmult=0,!.

Здесь переменная Use – признак обратимости импликации, C – КО посылки импликации, Qmult – коэффициент, на который умножается КО, вычисляемый правилом infer.

Таким образом, теперь правило infer для импликации с посылкой в форме конъюнкции принимает следующий вид:

infer(Node1,Ct):–

imp(a,Use,Node1,SignL,Node2,SignR,Node3,C1),

allinfer(Node2,C2),allinfer(Node3,C3), find_multiplier(SignL,MultL,SignR,MultR), C2S=MultL*C2,C3S=MultR*C3,min(C2S,C3S,CE),

qualifier(Use,CE,Qmult),

/*добавлен

предикат*/

Ct=CE*C1*Qmult.

/*добавлен

множитель*/

Аналогично надо дополнить также правила infer для импликаций с простой посылкой и посылкой в форме дизъюнкции. Здесь множитель Qmult=0 автоматически исключает из ОЦР необратимую импликацию с отрицательным КО посылки.

Наконец, для получения законченной ЭС надо написать правило, управляющее очередностью рассмотрения гипотез и печати результатов. Такое правило называется драйвером ЭС и в простейшем случае имеет следующий вид:

exsys_driver:– makewindow(), hypothesis_node(X), allinfer(X,Ct),

write(”Для гипотезы: ”),nl, write(X),nl,

write(”КО равен: ”,Ct),nl, fail.

15

2. Механизм объяснения экспертной системы IMP

2.1. Общие сведения о механизме объяснения ЭС

ЭС, способная объяснять свое поведение, внушает конечному пользователю большее доверие и кажется более разумной. Обычно пользователь желает получать объяснения в следующих типичных ситуациях:

Когда ЭС запрашивает у пользователя какие-либо данные, он желает спросить у нее:

ПОЧЕМУ ты запрашиваешь эти данные ?

Когда ЭС делает определенное заключение, пользователь желает спросить у нее:

КАК ты получила это заключение ?

Очевидно, что целесообразно дать пользователю возможность задавать вопросы “почему” тогда, когда ЭС работает с узлами низшего уровня сети вывода, т.е. в моменты, когда ЭС дает пользователю указание на ввод данных. Тогда пользователь, прежде чем ввести данные, может в качестве альтернативы ввести вопрос “почему”. Получив такой вопрос, ЭС сообщает информацию о последней импликации, с которой работала. Если этой информации пользователю недостаточно и он задает новый вопрос “почему”, то ЭС сообщает информацию о предпоследней рассмотренной импликации и т.д. Этот процесс может продолжаться по воле пользователя вплоть до импликации, расположенной на высшем уровне сети вывода, после чего пользователь уже обязан ввести запрашиваемые данные.

Очевидно также, что целесообразно дать пользователю возможность задавать вопросы “как” тогда, когда ЭС завершает работу с узлами высшего уровня сети вывода, представляющими целевые гипотезы, и сообщает пользователю результаты. Тогда пользователь может ввести вопрос “как”, указав имя любого интересующего его узла сети вывода. В ответ на это ЭС сообщает информацию об импликациях, поддерживающих этот узел, и объясняет, как она установила истинность их посылок.

16

2.2. Принцип работы блока ответа на вопросы “почему”

Рассмотрим принцип работы блока ответа на вопросы “почему” в ЭС IMP на примере набора импликаций, представленного сетью вывода на рис. 2.1.

c4

0.8 (r)

 

c1

c2

 

 

0.7

0.9

 

 

(r)

(r)

 

e1

e2

e3

c3

 

 

 

0.8

 

 

 

(r)

 

 

e4

e5

Рис. 2.1. Сеть вывода

Этот набор импликаций реализуется в программе на языке Prolog как группа соответствующих фактов на базе предиката imp во внутренней базе данных (БД).

Предположим, ЭС уже получила от пользователя значения КО для узлов e1, e2 и e3 и теперь запрашивает у него значение КО для узла e4. В этом случае реализуется следующий сценарий диалога:

Введите ’п’ [почему] или КО для узла e4

п

Пытаюсь установить c3 при помощи импликации c3 e4 e5 Введите ’п’ [почему] или КО для узла e4

п

Пытаюсь установить c2 при помощи импликации c2 (¬ e3) c3 Введите ’п’ [почему] или КО для узла e4

п

Пытаюсь установить c4 при помощи импликации c4 c1 c2 Введите ’п’ [почему] или КО для узла e4

0.85

Здесь первый вопрос “почему” относится к узлу e4, второй – к узлу c3 и, наконец, третий – к узлу c2.

17

Таким образом, ЭС шаг за шагом сообщает пользователю все более сложные цели, зависящие от затребованных у него данных. Получив эти данные, ЭС продолжает прерванное рассуждение. Чтобы реализовать такой сценарий диалога, ЭС использует стек. Всякий раз, когда ЭС, рассматривает какую-нибудь импликацию, она помещает ее в стек. После прекращения работы с импликацией, ЭС удаляет ее из стека.

Рассмотрим динамику содержимого стека импликаций при выводе целевого заключения, представленного узлом c4 в сети вывода на рис. 2.1. Итак, вначале рассматривается и, следовательно, помещается в стек импликация для узла c4. Стек импликаций реализуется в программе на языке Prolog как группа фактов на базе предиката dbimp во внутренней БД, причем предикат dbimp полностью идентичен предикату imp, кроме имени предиката. Поэтому начальное состояние стека:

dbimp(o,r,c4,pos,c1,pos,c2,0.8).

Далее рассматривается и помещается в стек импликация для узла c1:

dbimp(a,r,c1,pos,e1,pos,e2,0.7).

dbimp(o,r,c4,pos,c1,pos,c2,0.8).

Пользователь вводит значения КО для узлов e1 и e2. ЭС вычисляет КО для узла c1 и удаляет из стека верхнюю импликацию. Далее рассматривается и помещается в стек импликация для узла c2:

dbimp(o,r,c2,neg,e3,pos,c3,0.9).

dbimp(o,r,c4,pos,c1,pos,c2,0.8).

Пользователь вводит значение КО для узла e3. Далее рассматривается и помещается в стек импликация для узла c3:

dbimp(a,r,c3,pos,e4,pos,e5,0.8).

dbimp(o,r,c2,neg,e3,pos,c3,0.9).

dbimp(o,r,c4,pos,c1,pos,c2,0.8).

Далее ЭС запрашивает значение КО для узла e4. Пусть теперь пользователь вводит вопрос “почему”, т.е. имеет место ситуация, соответствующая началу диалога, сценарий которого приведен выше. Тогда ЭС извлекает из стека верхнюю импликацию и формирует из него ответ:

Пытаюсь установить c3 при помощи импликации c3 e4 e5 Введите ‘п’ [почему] или КО для узла e4

18

Затем ЭС сдвигает указатель вершины стека на одну ячейку вниз. На все последующие вопросы “почему” ЭС реагирует аналогично, постепенно опускаясь на “дно” стека. Когда пользователь вместо очередного вопроса “почему” вводит значение КО для узла e4, указатель вершины стека возвращается на исходную ячейку с импликацией для узла c3, т.е. стек принимает состояние, которое было в момент прерывания ОЦР.

2.3. Принцип работы блока ответа на вопросы “как”

Вопрос “как” требует от ЭС перечисления всех импликаций, лежащих в основе сделанного заключения. Они соответствуют поддереву, начинающемуся в заданном узле сети вывода. При определении объема информации, сообщаемой пользователю в ответ на вопрос “как”, надо решить, какую часть поддерева показать пользователю, в каком порядке показывать импликации и т.п.

Простейший механизм ответа на вопросы “как” позволяет открыть для просмотра сеть вывода в некоторых стратегически важных точках. Тогда пользователь может задать вопрос “как” для конкретного узла и получить информацию только о тех импликациях, которые поддерживают этот узел. Допустим пользователя интересует, как получено целевое заключение, представленное узлом c4 в сети вывода на рис. 2.1. В этом случае реализуется следующий сценарий диалога:

Введите ’к имя_узла’ [как] или ’п’ [продолжить]

к c4

Заключению c4 присвоен КО = 0.68 на основании: c4 c1 c2

Импликация обратима c1 имеет КО = 0.6 c2 имеет КО = 0.85

Импликация имеет КО = 0.8 Импликация, использованная отдельно, дает КО = 0.68

Введите ’к имя_узла’ [как] или ’п’ [продолжить]

Чтобы реализовать такой сценарий диалога, ЭС всякий раз, когда заканчивается вывод для некоторого узла, записывает в БД предложение, содержащее информацию о том, какие импликации применялись при выводе и какое значение было присвоено КО узла. Тогда ЭС, получив вопрос “к имя_узла”, выполняет выборку данных из БД по имени узла и формирует из них ответ.

В общем случае, отвечая на вопрос “как” для узла, поддерживаемого несколькими импликациями, ЭС сообщает для этого узла общее значение КО и все частные значения КО, полученные по отдельным импликациям, например:

Введите ’к имя_узла’ [как] или ’п’ [продолжить]

к c8

Заключению c8 присвоен КО = 0.724 на основании:

c8 c5 c7

Импликация обратима c5 имеет КО = 0.75 c7 имеет КО = 0.5

Импликация имеет КО = 0.8 Импликация, использованная отдельно, дает КО = 0.4

19

c8 (¬ c6)

Импликация необратима

C6 имеет КО = –0.6

Импликация имеет КО = 0.9 Импликация, использованная отдельно, дает КО = 0.54

Введите ’к имя_узла’ [как] или ’п’ [продолжить]

2.4. Программная реализация механизма объяснений в оболочке ЭС IMP

Для реализации механизма объяснений в оболочке ЭС IMP в правила infer добавлены предикаты, обеспечивающие запоминание необходимой информации. Например, правило infer для импликации с посылкой в форме конъюнкции теперь имеет следующий вид:

infer(Node1,Ct):–

imp(a,Use,Node1,SignL,Node2,SignR,Node3,C1),

asserta(dbimp(a,Use,Node1,SignL,Node2,SignR,Node3,C1)), asserta(tdbimp(a,Use,Node1,SignL,Node2,SignR,Node3,C1)),

allinfer(Node2,C2),allinfer(Node3,C3), find_multiplier(SignL,MultL,SignR,MultR), C2S=MultL*C2,C3S=MultR*C3,min(C2S,C3S,CE), qualifier(Use,CE,Qmult),

Ct=CE*C1*Qmult,

assertz(infer_summary( imp(a,Use,Node1,SignL,Node2,SignR,Node3,C1), Ct),

retract(dbimp(a,Use,Node1,SignL,Node2,SignR,Node3,C1)), retract(tdbimp(a,Use,Node1,SignL,Node2,SignR,Node3,C1)).

В начале правила infer посредством стандартного предиката asserta во внутреннюю БД записываются два факта, а именно, один из них в начало группы фактов dbimp, а другой – в начало группы фактов tdbimp. Эти факты являются точными копиями импликации, рассматриваемой в данный момент механизмом вывода ЭС. Группы фактов dbimp и tdbimp во внутренней БД по принципу заполнения представляют собой стеки. Первый стек используется при выводе ответов на вопросы “почему”. Второй стек идентичен первому и используется для восстановления содержимого первого стека, которое теряется выводе ответов на вопросы “почему”. В конце правила infer посредством стандартного предиката assertz во внутреннюю БД записывается составной факт infer_summary, который объединяет текущую импликацию с вычисленным КО заключения этой импликации. Эти факты используются при выводе ответов на вопросы “как”. Наконец, рассмотренная импликация удаляется из стеков во внутренней БД посредством стандартного предиката retract.

1) Блок ответа на вопросы “как”

Драйвер ЭС, правило exsys_driver, вначале вызывает правило getallans, порождающее все возможные заключения ЭС, а затем – правило showresults, отображающее эти заключения:

exsys_driver:- getallans,showresults,!.

20

Каждое заключение, для которого надо вычислить значение КО, определено в программе как узел гипотезы hypothesis_node. Правило answer вычисляет для узла гипотезы значение КО и сохраняет в БД имя узла гипотезы и значение КО для него в виде факта danswer. Поэтому часть драйвера ЭС, порождающая все возможные заключения ЭС, имеет вид:

getallans:- not(prepare_answer).

prepare_answer:-

answer(X,Y), /* X - имя узла, Y - КО узла */ fail.

answer(X,Y):- hypothesis_node(X), allinfer(X,Y),

assert(danswer(X,Y)). /* заполнение БД снизу вверх */

В правиле getallans используется стандартный предикат not, т. к. вычисление предиката prepare_answer заканчивается неудачей. Правило prepare_answer завершается стандартным предикатом fail, вызывающим откат. Благодаря этому предикат answer используется всеми возможными способами.

После выполнения правила getallans выполняется правило showresults:

showresults:- not(displayall).

displayall:- display_one_answer, fail.

display_one_answer:- danswer(X,Y),

clearwindow, /* стандартный предикат очистки экрана */ write(”Для этой гипотезы: ”),nl,

write(X),nl,

write(”КО равен: ”,Y),nl,nl, not(how_describer(X)).

Правило display_one_answer отображает информацию для одного заключения (гипотезы), а затем вызывает блок ответа на вопрос “как”, который имеет следующий вид:

how_describer(Node):- hypothesis_node(Node),

repeat, /* предикат повторения, определяемый пользователем, см. учебник Prolog */

write(”Введите ’к имя_узла’ [как] или ’п’ [продолжить]”),nl,

readln(Reply),nl, how_explain(Reply),!.

При выполнении этого правила появляется запрос:

Введите ’к имя_узла’ [как] или ’п’ [продолжить].