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

Точно Не проект 2 / Не books / Источник_1

.pdf
Скачиваний:
10
Добавлен:
01.02.2024
Размер:
20.67 Mб
Скачать

390

Глава 7

 

 

 

Х имеет прямо посаженные глаза

то Х хищник)) (setq правило7

(если Х млекопитающее и

Х имеет копыта то Х жвачное))

(setq правило8

(если Х млекопитающее и Х жует жвачку

то Х жвачное)) (setq правило9

(если Х хищник и

Хжелто-коричневое и

Химеет темные пятна

то Х гепард))

(setq правило10

(если Х хищник и

Хжелто-коричневое и

Хполосатое и

Химеет черные полосы то Х тигр))

(setq правило11

(если Х жвачное и

Хдлинношеее и

Хдлинноногое и

Хжелто-коричневое и

Химеет темные пятна то Х жираф))

(setq правило12

(если Х жвачное и

Хполосатое и

Химеет черные полосы

то Х зебра))

(setq правило13

(если Х птица и

Хне летает и

Хдлинношеее и

Хдлинноногое и

Хчерно-белое то Х страус))

(setq правило14

(если Х птица и

X не летает и

Хплавает и

Хчерно-белое

то Х пингвин))

(setq правило15

(если Х птица и

Х хорошо летает

Экспертные системы

391

 

 

то Х альбатрос))

С целью упрощения программы преобразуем базу знаний из исходной формы в список, элементами которого являются структуры:

(defstruct rule

;имя структуры

имя

; имя правила

условия

; список предпосылок правила

выводы)

; список заключений правила

Напомним, что создать экземпляр структуры в языке Лисп можно с помощью функции-конструктора, имя которой образуется из слова MAKE и имени структуры. Например:

(make-rule

: имя правило15

:условия ( (Х птица) (Х хорошо летает))

:выводы ((Х альбатрос)) )

Вэтом случае условия и заключения правила задаются подсписками, каждый из которых выступает в роли образца, сопоставляемого с фактами рабочей памяти. Преобразование базы знаний в список, элементами которого

являются указанные структуры, выполним

с помощью функции

ПРЕОБРАЗОВАТЬ:

 

(defvar *правила*)

(setq *правила* (преобразовать *база-знаний*))

Здесь значением глобальной переменной *ПРАВИЛА* будет список из структур. Функция ПРЕОБРАЗОВАТЬ определяется следующим образом

[49]:

(defun преобразовать (база-знаний)

(mapcar #преобразовать-правило база-знаний ))

;;преобразует правило из формы в виде списка в структуру

(defun преобразовать-правило (имя) (let (( правило (eval имя)))

(make-rule

:имя имя

:условия (prem правило)

:выводы (concl правило))))

;;возвращает условия правила в виде списка, ;;элементами которого являются образцы

(defun prem (правило)

392

Глава 7

 

 

(список-образцов (cdr правило) nil nil ))

;;возвращает заключения правила в виде списка,

;;элементами которого являются образцы

(defun concl (правило)

(список-образцов (cdr (member то правило)) nil nil ))

;;формирует список образцов, разделяемых в

;;правиле символом и

(defun список-образцов (правило часть результат)

( cond

 

((null правило)

; выход из рекурсии

(concat результат часть))

; объединить результат и часть

((eq (car правило) то)

; окончание анализа условий

(concat результат часть))

 

((eq (car правило) и)

; начало нового образца

(список-образцов (cdr правило) nil

(concat результат часть)))

(t (список-образцов (cdr правило)

(concat часть (car правило))

результат))))

(defun concat (x y) (append x (list y)))

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

Машина вывода применяет правила к фактам, содержащимся в рабочей памяти, пока не будет получен искомый результат. Обратный вывод начинается с внесения целевого утверждения рабочую память. Представим возможные целевые утверждения (гипотезы) с помощью глобальной пере-

менной *ГИПОТЕЗЫ*:

(defvar *гипотезы*) (setq *гипотезы*

((Х гепард) (Х тигр)

(Х жираф) (Х зебра)

(Х страус) (Х пингвин)

(Х альбатрос)))

Экспертные системы

393

 

 

Переменная *ГИПОТЕЗА* представляет список целевых утверждений, которые система будет пытаться последовательно доказывать. Следовательно, в основной программе должна вызываться рекурсивная функция SOLVE, последовательно обрабатывающая элементы списка *ГИПОТЕЗЫ*

[49]:

;;; главная программа

 

 

(defvar

*факты*)

;

список фактов

(defvar

*вопросы*)

;

список фактов, о которых

(setq *факты* nil)

;

задавались вопросы пользователю

 

 

(setq *вопросы* nil)

 

 

;; экспертная система классификации животных

(defun

expert ()

 

 

(solve *гипотезы*))

;; функция последовательно проверяет гипотезы

(defun solve (гипотезы)

(cond

((null гипотезы) “Не могу доказать ни одну гипотезу”)

((back-prove

; вызов функции доказательства

(car гипотезы))

 

 

(car гипотезы))

;

результат

(t (solve (cdr гипотезы)))))

;

следующая попытка

Рассмотрим функцию BACK-PROVE, осуществляющую обратный вывод. Функция базируется на следующих правилах:

1)если гипотеза уже имеется в списке фактов (рабочей памяти), то она доказана;

2)если гипотезы нет в списке фактов, то все правила, которые могут подтвердить гипотезу, помещаются в список КАНДИДАТЫ, и если какоелибо из правил списка КАНДИДАТЫ непосредственно выполняется на множестве известных фактов, то гипотеза доказана, иначе предпринимается попытка доказать все условия одного из правил списка

КАНДИДАТЫ, применив рекурсивно рассматриваемую процедуру обратного вывода;

3)если условие, которое требуется доказать, не выводится с помощью правил, то необходимо запросить его значение у пользователя; если условие выполняется, то добавить его к списку фактов.

Выполнимость правила на множестве фактов будем проверять с по-

мощью функции TEST-RULE:

(defun test-rule (правило)

(match1 (rule-условия правило) *факты*))

394

Глава 7

 

 

;;Функция сопоставления образцов правила ;;с множеством фактов

(defun match1 (образцы факты)

(equal образцы (intersection образцы факты)))

Здесь функция MATCH1 выполняет сопоставление условий правила (образцов) с фактами. При этом сопоставление реализуется простой проверкой на равенство списка образцов и списка общих элементов

(INTERSECTION) двух списков ОБРАЗЦЫ и ФАКТЫ.

Добавление заключений, полученных с помощью выполняемых правил, к списку фактов реализуется функцией ADD-CONCL:

(defun add-concl (правило)

(do ((выводы (rule-выводы правило) (cdr выводы)))

((null выводы) *факты*)

(if (member (car выводы) *факты* : test # ’equal) nil

(prog1

(format t “~S утверждает, что” (rule-имя правило)) (print-el (car выводы)) (terpri)

(push (car выводы) *факты*)))))

;; печать элементов списка

(print-el (список)

(mapc # ‘(lambda (элемент) (princ элемент) (princ “ ”))

список ) t )

Описанные выше правила обратного вывода соответствуют трем ветвям COND формы, вызываемой в функции BACK-PROVE:

(defun back-prove(гипотеза)

 

 

(let ((кандидаты))

; список правил-кандидатов,

(cond

; способных подтвердить гипотезу

((member гипотеза *факты* : test #equal) t)

; ветвь 1

((setq кандидаты (поиск-кандидатов гипотеза))

; ветвь 2

(if (непосредственно гипотеза кандидаты)

 

t

 

 

(рекурсивно гипотеза кандидаты)))

 

(t (cond

 

; ветвь 3

((member гипотеза *вопросы* : test #equal) nil) ((and

(princ “Верно, что:” )

(print-el гипотеза) (progn (princ “?”) (terpri) t) (eq (read) да))

;если ответ ’да’, то добавляем гипотезу к списку фактов

Экспертные системы

395

 

 

(setq *факты* (union (list гипотеза) *факты*))

;добавляем гипотезу к списку вопросов независимо от ответа (push гипотеза *вопросы*) t)

(t (push гипотеза *вопросы*) nil))))))

;;формирование списка правил-кандидатов, способных

;;подтвердить гипотезу

(defun поиск-кандидатов (гипотеза) (mapcan #’(lambda (x)

(if (member гипотеза (rule-выводы x) : test #’equal) (list x)))

*правила*))

;;проверяет, можно ли непосредственно доказать

;;гипотезу с помощью какого-либо правила-кандидата (defun непосредственно(гипотеза кандидаты)

(cond

((null кандидаты) nil)

((null *факты*) nil)

;если правило применимо, то добавляем его заключение к фактам

((test-rule (car кандидаты)) (add-concl (car кандидаты)))

(t (непосредственно гипотеза (cdr кандидаты)))))

;;проверяет, можно ли рекурсивно доказать гипотезу с

;;помощью какого-либо правила-кандидата

(defun рекурсивно(гипотеза кандидаты) (cond

((null кандидаты) nil)

; проверить возможность доказательства условий правила ((test-условия (rule-условия (car кандидаты))) ; новая подцель

(add-concl (car кандидаты)))

(t (рекурсивно гипотеза (cdr кандидаты)))))

;;проверяет возможность доказательства всех условий

;;правила с помощью функции обратного вывода (defun test-условия (условия)

(every # ’back-prove условия))

Процесс классификации животных по внешним признакам начинается с вызова основной функции EXPERT. Ниже приведен возможный диалог с системой:

(expert)

 

Верно, что: Х имеет шерсть ?

; вопрос

Нет

; ответ

Верно, что: Х кормит детенышей молоком ?

 

Нет

 

Верно, что: Х имеет перья ?

 

396

Глава 7

 

 

Да

 

 

Правило3 утверждает, что

Х

птица

Верно, что: Х не летает ?

 

 

Нет

 

 

Верно, что: Х хорошо летает ?

 

Да

 

 

Правило15 утверждает, что

Х

альбатрос

( Х альбатрос )

 

; результат

Рассмотренная реализация ЭС характеризуется простейшими возможностями. Расширить возможности системы можно, реализовав функции объяснения и применив более совершенный вариант функции сопоставления с образцом.

7.6. Реализация экспертной системы на языке Пролог

Рассмотрим два варианта реализации ЭС, один из которых основан на продукционной модели представления знаний, а другой – на фреймовой модели1).

7.6.1.Представление знаний правилами

Вкачестве примера выберем задачу диагностики. Пусть требуется установить причину неисправности электрической плиты, оборудованной нагревательным элементом, контрольной лампочкой и выключателем. Плита подключена к источнику напряжения с помощью электрического кабеля, который обеспечивает передачу тока. Набор правил “неисправ- ность-причина” представим в виде графа (рисунок 7.4).

Простейший способ построения ЭС на Прологе – использование механизма поиска решений, заложенного в Пролог-системе. Для этого представим правила в виде предикатов языка Пролог:

/* структура правила: причина: неисправность */

 

 

нагреватель(неисправен): лампа(светится),

/* r1 */

плита(холодная).

 

 

выключатель(не_включен): тока(нет).

/* r2

*/

напряжения(нет): тока(нет).

/* r3

*/

тока(нет): плита(холодная),

/* r4

*/

лампа(не_светится).

 

 

лампа(неисправна): лампа(не_светится),

/* r5

*/

плита(горячая).

 

 

1) ) Примеры и программы данного параграфа разработал Дж. Криз (J. Kriz)

Экспертные системы

397

 

 

Рисунок 7.4 – Сеть вывода

Определить причину поломки электрической плиты можно, указав обнаруженные неисправности и обеспечив проверку выполнимости каждой из возможных гипотез. Например:

/* вариант 1 */ лампа(не_светится). /*факты*/ плита(холодная).

причина(нагреватель(неисправен)). /*гипотезы*/ причина(выключатель(не_включен)). причина(напряжения(нет)). причина(лампа(неисправна)).

найти(Х): причина(Х), cаll(X). /*проверка гипотез */

Для того чтобы узнать, какая из гипотез неисправности выполняется, необходимо задать вопрос:

? – найти(Х).

Получив такой вопрос, Пролог-система выполнит необходимый обратный поиск с помощью встроенного механизма вывода и вернет ответ:

Х= выключатель(не_включен);

Х= напряжения(нет).

398

Глава 7

 

 

Недостатком рассмотренного варианта программы является необходимость внесения обнаруженных неисправностей непосредственно в текст программы в виде фактов. Устранить указанный недостаток можно, запросив значения соответствующих неисправностей у пользователя в процессе выполнения программы:

/* вариант 2 */ лампа(не_светится): спроси(‘Лампа не светится ?’).

плита(холодная): спроси(‘Плита холодная ?’). лампа(светится): спроси(‘Лампа светится ?’). плита(горячая): спроси(‘Плита горячая ?’).

спроси(Вопрос): write(Вопрос), read(‘да’).

В этом случае система будет задавать вопросы пользователю относительно неизвестных фактов. Так как ответы пользователя не запоминаются, то возможно повторение вопросов. Для запоминания ответов можно воспользоваться встроенным предикатом assert.

Дальнейшие улучшение программы связано с упрощением формы записи правил. Запись правил в форме предикатов языка Пролог неудобна для пользователя. Представим правила в более естественной форме. Например:

правило1: если лампа(светится) и плита(холодная)

то нагреватель(неисправен).

Для представления правил в такой форме определим операторы:

определить_операторы: op(920, xfy, и), op(950, xfx, то), op(960, fx, если), op(970, xfx, ‘:’).

?– определить_операторы.

Спомощью введенных операторов правило 1 представляется структурой, изображенной на рисунке 7.5.

Остальные правила рассматриваемого примера запишутся в виде:

правило 2: если тока(нет)

то выключатель(не_включен).

правило 3: если тока(нет)

то напряжения(нет).

Экспертные системы

399

 

 

правило 4: если плита(холодная) и лампа(не_светится)

то тока(нет).

правило 5: если лампа(не_светится) и плита(горячая)

то лампа(не_исправна).

:

правило1 если

то

инагреватель(неисправен)

лампа(светится) плита(холодная)

Рисунок 7.5 – Структура, соответствующая правилу

В ходе вывода метаинтерпретатор должен установить причину неисправности, проверяя различные гипотезы. Представим возможные гипотезы в форме:

Имя_факта : Факт.

Тогда в соответствии с введенными правилами база данных возможных причин неисправности (гипотез) запишется в виде:

h1 : причина(нагреватель(неисправен)). h2 : причина(выключатель(не_включен)). h3 : причина(напряжения(нет)).

h4 : причина(лампа(неисправна)).

Кроме этого, перечислим наблюдаемые признаки, на основе которых метаинтерпретатор должен установить причину неисправности.

h1 : признак(лампа(светится)).

h2 : признак(плита(холодная)). h3 : признак(лампа(не_светится)). h4 : признак(плита(горячая)).

Соседние файлы в папке Не books