ДонНТУ Магистры Главная Автореферат Библиотека Ссылки Отчет о поиске
Фото Материал подготовила
Капустина Екатерина Александровна
infinity2004@list.ru

Тема магистерской работы: «Разработка автоматизированной системы оценки состояния печени на основании результатов морфологических исследований»

Донецкий национальный технический университет
Факультет компьютерных информационных технологий и автоматики

Научный руководитель: доцент, к.т.н. Привалов М.В.

Применение средств языка CLIPS для организации и представления знаний в экспертных системах


ВВЕДЕНИЕ

Система, претендующая называться экспертной, должна обладать знаниями. Эти знания, естественно, должны быть ориентированы на конкретную предметную область, и из этих знаний должно непосредственно вытекать решение проблемы. Именно поэтому знания в экспертных системах предполагают определенную организацию и интеграцию (отдельные факты, сведения должны каким–либо образом соотноситься друг с другом и образовывать между собой определенные связи). То есть знания должны быть соответствующе представлены.

В [1] показано, что в области экспертных систем представление знаний означает не что иное, как систематизированную методику описания на машинном уровне того, что знает человек–эксперт, специализирующийся в конкретной предметной области. Представление знаний должно позволять извлекать их в нужной ситуации с помощью относительно несложного и более–менее естественного механизма. Следует понимать, что недостаточно простого перевода знаний в форму, пригодную для хранения на машинных носителях. Чтобы достаточно быстро извлекать те элементы знаний, которые наиболее пригодны в конкретной ситуации, база знаний (о разнице между понятиями "база данных" и "база знаний" см., например, работу [2]) должна обладать достаточно развитыми средствами контекстной адресации и индексирования. Тогда программа, использующая знания, сможет управлять последовательностью применения отдельных "элементов" знания, даже не обладая точной информацией о том, как они хранятся [1].

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

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

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

Ниже рассмотрены предлагаемые CLIPS форматы представления данных и способы организации знаний.

Наверх

Формат представления данных в CLIPS

В CLIPS предусмотрены 3 основных формата представления информации: факты, объекты и глобальные переменные.

Факты. Факты являются одной из основных форм высокого уровня для представления информации в системе CLIPS. Факт (fact) — это список элементарных значений, на которые ссылаются либо позиционно (упорядоченные (ordered) факты), либо по имени (неупорядоченные (non–ordered) или шаблонные (template) факты). Обращение к фактам осуществляется по индексу или адресу.

Каждый факт представляет часть информации и помещается в текущий список фактов (fact-list). Факты — фундаментальная единица данных, используемая правилами.

Факты могут быть добавлены в список фактов (используя команду assert), удалены из него (используя команду retract), изменены (используя команду modify) или скопированы (используя команду duplicate) в результате явного воздействия пользователя или при исполнении программы CLIPS. Число фактов в списке фактов и количество информации, которая может быть запомнена в факте, ограничено только объемом памяти компьютера. Если в список фактов заявлен факт, который точно соответствует уже имеющемуся там факту, эта вставка будет проигнорирована (впрочем, такое поведение может быть принудительно изменено).

Некоторые команды, такие как retract, modify и duplicate требуют наличия факта (что вполне логично: довольно затруднительно удалить, изменить или скопировать несуществующий факт!). Факт может быть указан или индексом (fact-index), или адресом (fact-address). Каждый раз при добавлении (или изменении) факта он получает уникальный целочисленный индекс, называемый fact-index. Индексы начинаются с нуля и увеличиваются на единицу для каждого нового или измененного факта. Каждый раз при выполнении команд reset (обновление рабочей памяти) или clear (очистка рабочей памяти) индексы фактов сбрасываются в ноль. Факт может быть указан и с использованием адреса факта (fact-address). Адрес факта может быть получен, перехватив возвращаемое значение команд, которые возвращают адреса (например, assert, modify, duplicate), или присвоением переменной адреса факта, который соответствует образцу в левой части правила (LHS — Left–Hand Side, т.е. список условий), например, так:

            ...
;;присвоение переменной адреса факта (somefact exists)
?somefact <- (somefact exists)
            ...

Идентификатор факта (fact identifier) представляет собой краткую нотацию для отображения факта. Формат идентификатора факта — f–<fact-index>, например, запись f–10 относится к факту с индексом 10.

Как отмечалось выше, факты хранятся в одном из двух форматов: упорядоченном (ordered) или неупорядоченном (non-ordered).

Упорядоченные факты состоят из символьного обозначения с последовательностью нуля или более полей, разделенных пробелами, ограниченного начальной круглой скобкой с левой стороны и завершающей круглой скобкой справа. Первое поле упорядоченного факта обозначает "отношение", которое следует применять к следующим полям в факте. Например, факт (father-of Jack Bill) означает, что Билл — отец Джека (Bill is father of Jack).

Ниже приведены примеры упорядоченных фактов:

                    ...
     ;; скорость - 80 км/ч  
     (speed is 80 km/h)
     ;; список бакалеи - хлеб молоко масло
     (grocery-list bread milk butter)
     ;; Робот находится в комнате
     (at room robot) 
                    ...

Следующие символьные обозначения зарезервированы и не могут быть использованы в качестве первого поля в любых фактах: test, and, or, not, declare, logical, object, exists и проч. Эти слова зарезервированы и не могут быть использованы в качестве имен шаблонов в конструкциях deftemplate, но могут быть использованы в качестве имен слотов (slot) (см. в библиотеке), хотя это и не желательно.

Неупорядоченные факты. Упорядоченные факты кодируют информацию позиционно. Чтобы обратиться к этой информации, пользователь должен знать не только то, какие данные хранятся в факте, но и какое именно поле содержит те или иные конкретные сведения. Неупорядоченные (или deftemplate) факты обеспечивают пользователю способность абстрагироваться от структуры факта, назначая имя каждому полю факта. Конструкция deftemplate (пример см. в библиотеке) используется для создания шаблона, который затем может быть применен для получения доступа к полям шаблонных фактов по их имени. Конструкция deftemplate является аналогом определения записей или структур в таких языках программирования, как Pascal и C.

     (deftemplate имя_шаблона
       (slot атрибут1 (тип)[( default значение_по_умолчанию)])
       (slot атрибут2 (тип)[( default значение_по_умолчанию)])
                         ………
       (slot атрибутN (тип)[(default значение_по_умолчанию)])
     )

Эта конструкция позволяет определять шаблон с нулем или более поименованных полей (named fields) или заполнителей–слотов (slots). В отличие от упорядоченных фактов, для слотов шаблонного факта обязательно указание типа и значения. Кроме того, для слотов могут быть заданы значения по умолчанию. В неупорядоченных фактах отсутсвует ограничение на порядок следования полей, главное, чтобы было указано имя поля. Следует отметить, что слоты не могут быть использованы в упорядоченных фактах.

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

Ниже приведены примеры шаблонных фактов:

     (patient (name "Ivanov Ivan") (age 46))
     (class (teacher "Alexey Alexeev") (N_pupils 28) (Room "37A"))
     (car (model "BMW-Z3") (color "silver") (price 50000))         

Заметьте, что порядок слотов в шаблонном факте не важен. Например, следующие факты идентичны:

     (patient (age 46) (name "Ivanov Ivan"))
     (patient (name "Ivanov Ivan") (age 46))
                         ………
     (car (color "silver") price 50000 (model "BMW-Z3"))         
     (car (price 50000) (model "BMW-Z3") (color "silver"))         

В отличие от приведенных фактов, следующие упорядоченные факты не идентичны:

     (class "Alexey Alexeev" 28 "37A")
     (class 28 "37A" "Alexey Alexeev")
     (class "37A" "Alexey Alexeev" 28)

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

Факты инициализации (Initial Facts). Конструкция deffacts позволяет задать набор априорных или инициализирующих знаний. Когда командой reset обновляется рабочая память CLIPS, каждый факт, задаваемый конструкцией deffacts в базе знаний CLIPS, добавляется в список фактов.

Команда reset создает в базе знаний специальный инициализирующий факт — initial-fact. Используя его, можно создать правило, которое бы выполняло некоторые действия при запуске кода CLIPS. Поскольку условием, активизирующим стартовое правило, будет наличие в базе знаний факта инициализации (а он там всегда присутствует самым первым), это правило будет активизировано в первую очередь. Например, напишем следующее правило и сохраним в файле example.clp:

     (defrule start
       (initial-fact)
        =>
       (printout t "Hello, world!" crlf)
     )

Тогда в диалоговом окне CLIPS при загрузке этого кода будем наблюдать:

      CLIPS> (load example.clp)
      *
      TRUE
      CLIPS> (reset)
      ==> f-0     (initial-fact)
      ==> Activation 0      start: f-0
      CLIPS> (run)
      FIRE    1 start: f-0
      Hello, world!
      CLIPS>

Глобальные переменные. Конструкция defglobal позволяет описывать переменные, которые являются глобальными в контексте окружения CLIPS. То есть глобальная переменная доступна в любом месте окружения CLIPS и сохраняет свое значение независимо от других конструкций. Напротив, некоторые конструкции (как, например, defrule или deffunction) могут иметь собственные локальные переменные, которые задаются в пределах описания конструкции. Обращение к таким переменным возможно только изнутри конструкции, где они описаны; за ее пределами они не имеют значения. Глобальные переменные CLIPS подобны глобальным в процедурных языках программирования, таких как LISP или C. Однако, в отличие от того же С, глобальные переменные CLIPS слабо типизированы (отсутствуют ограничения на хранения значений различных простых типов данных).

Глобальные переменные могут быть доступны как часть процесса сопоставления образцов (pattern-match), однако их изменение не вызывает процесс сопоставления образцов. Функция bind используется, чтобы задать значения глобальным переменным. Значения глобальных переменных сбрасываются к начальным установочным значениям при выполнении команды окружения reset или если для глобальной переменной вызвана функция bind без соответствующего значения. Такой порядок может быть изменен, если использовать функцию set-reset-globals. Глобальные переменные могут быть удалены командами clear или undefglobal. Если элемент глобальных переменных отслеживается при трассировке, то соответствующее информационное сообщение будет отображаться каждый раз, когда значение глобальной переменной изменяется.

Синтаксис описания:

     (defglobal [<defmodule-name>] ?*<имя_глобальной_переменной>* = <выражение>)

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

В целом возможны и множественные конструкции defglobal. Любое число глобальных переменных может быть задано в описании defglobal. Необязательный <defmodule-name> указывает модуль, в котором конструкция defglobals будет определена. Если ничего не указано, глобальные переменные будут размещены в текущем модуле. Если переменная была определена в предыдущей конструкции defglobal, её значение будет заменено значением, заданном в новой конструкции defglobal. Если при описании конструкции defglobal допущена ошибка, любые описания глобальной переменной, заданные до того, как ошибка обнаружена, будут оставаться в силе.

Команды, которые оперируют с глобальными переменными, как например ppdefglobal и undefglobal, требуют наличия символического имени глобальной переменной без "звездочек" ( этом случае, например, используйте обозначение max, когда хотите сослаться на глобальную переменную ?*max*).

Глобальные переменные могут быть использованы в любом месте программы также, как и локальные переменные (за исключением 2–х случаев):

  1. глобальные переменные не могут быть использованы в качестве параметрических переменных в конструкциях deffuncion, defmethod или message-handler;
  2. глобальные переменные не могут быть использованы так же, как локальные переменные используются в списках условий правил для связывания их со значениями.
Таким образом, следующее правило неверное:

     (defrule example
       (fact ?*x*)
        =>
     )

А следующее правило корректно:

     (defrule example
       (fact ?y&:(> ?y ?*x*))
        => 
     )

Отметьте, что это правило не обязательно будет активизировано, когда значение ?*x* изменится. Например, если ?*x* составляет 4 и факт (fact 3) добавлен в рабочую память, условия правила не будут удовлетворены. Если же значение ?*x* изменится на 2, правило все равно не будет активизировано.

Пример объявления и использования глобальных переменных приведен здесь (←щёлкни по ссылке).

Наверх

Объекты. Объекты в CLIPS могут быть описаны как символьные, строковые, целые или вещественные числа, значения с множеством полей, внешние адреса или объекты определенного пользователем класса. При описании объектов выделяют 2 основные части: свойства и их поведение. Класс — это шаблон общих свойств и поведения объектов данного класса.

Для создания пользовательского класса используется конструкция defclass. Объект пользовательского класса создается посредством функции make-instance, и к созданному таким образом объекту можно обращаться по уникальному адресу. В пределах модульного контекста к объекту можно уникально обращаться и по имени. Ниже приведен пример создания простого класса и его экземпляра:

    ;; описание класса car (машина)
    (defclass car
      (is-a user) ;; пользовательский класс
      (name)      ;; наименование модели
      (made-by)   ;; производитель
    )
    
    ;; описание экземпляра класса car
    (make-instance corvette of car
      (name lacetti)
      (made-by chevrolet)
    )

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

К объектам простых типов обращаются, подставляя их значения, и они создаются и удаляются исключительно окружением CLIPS по мере необходимости. Объекты простых типов не имеют имен и слотов-заполнителей, и их классы предопределены CLIPS. Поведение объектов простых типов такое же, как и у объектов пользовательских классов. Пользователь может создавать собственные обработчики сообщений (которыми и оперируют классы в CLIPS — обычно над слотами объекта выполняются какие–то действия при получении объектом тех или иных сообщений) и присоединить их к объектам простых типов. Тем не менее, предполагается, что простые типы не будут часто использоваться в контексте объектно–ориентированного программирования в CLIPS.

Ниже представлены несколько примеров объектов и их классов:

Объект (печатное представление) Класс
Rolls–RoyceSYMBOL
"Rolls–Royce"STRING
8.0FLOAT
8INTEGER
(8.0 Rolls-Royce 8 [Rolls-Royce])MULTIFIELD
<Pointer- 00CF61AB>EXTERNAL-ADDRESS
[Rolls-Royce]CAR (пользовательский класс)

На объект пользовательского класса ссылаются по имени или адресу, и они создаются и удаляются явно через сообщения и специальные функции. Свойства объекта пользовательского класса определяются набором слотов, которые объект получает от его класса. Как ранее упоминалось, слоты — это поименованные одиночные поля или мультиполя (multifields). Например, объект Rolls–Royce — это объект класса CAR. Одним из слотов в классе CAR может быть "цена" (price), и значение этого слота для объекта Rolls–Royce составило бы, например, $75 000. Поведение объекта указано в терминах процедурного кода называемого обработчики сообщений (message-handlers), которые присоединены к классу объекта. Все объекты пользовательского класса имеют одинаковый набор слотов, но каждый объект может иметь различные значения для этих слотов. Однако два объекта, которые имеют одинаковый набор слотов, не обязательно принадлежат к одному и тому же классу, так как два различных класса могут иметь идентичные наборы слотов.

Базовая разница между слотами объекта и шаблона (неупорядоченного факта) состоит в наследовании. Наследование позволяет описывать свойства и поведение класса описывать в терминах других классов. COOL (CLIPS Object–Oriented Language — Объектно–ориентированный язык CLIPS) поддерживает множественное наследование: класс может напрямую наследовать слоты и обработчики сообщений от более чем одного класса. Так как наследование полезно только для слотов и обработчиков сообщений, часто не значимо наследование от одного из классов простых типов, как, например, MULTIFIELD или NUMBER. Это связано с тем, что эти классы не могут иметь слотов и обычно не имеют обработчиков сообщений.

Пользовательские классы могут быть конкретными и абстрактными. Абстрактные классы играют ту же роль, что и виртуальные классы в С++, то есть они используются только для порождения производных классов. Например, если имеем абстрактный класс PERSON, то используя механизм наследования можно создать производные классы WOMAN и MAN.

Использование объектно–ориентированных средств в CLIPS позволяет значительно упростить программирование правил, поскольку для обновления данных можно применять механизм передачи и обработки сообщений методами классов. Ниже представлен пример описания класса и его экземпляра из [1]:

;; определяем класс pistol, в котором будут перечислены свойства,
;; необходимые для моделирования работы полуавтоматического пистолета
(defclass pistol
  ;; системные слоты
  (is-a USER)             ;; pistol - это пользовательский класс	
  (role concrete)         ;; это конкретный класс, т.е. возможно создание экземпляров этого класса
  (pattern-match reactive);; экземпляры класса pistol могут быть использованы в качестве объектов данных
                          ;; кот. можно сопоставлять с условиями в правилах и использовать в действиях,
                          ;; определенных правилами
       (slot safety (type SYMBOL)   (create-accessor  read-write)) ;; предохраитель (on/off)
       (slot slide (type SYMBOL)    (create-accessor read-write))  ;; затвор (forward/back)
       (slot hammer (type SYMBOL)   (create-accessor read-write))  ;; курок (back/down)                            
       (slot chamber (type INTEGER) (create-accessor read-write))  ;; патронник (1/0)	
       (slot magazine (type SYMBOL) (create-accessor read-write))  ;; обойма (in/out)                                     
       (slot rounds (type INTEGER)  (create-accessor read-write))  ;; патроны (текущее количество в обойме)
       ;; фацет create-accessor разрешает записывать в слот новое значение и считывать текущее
)
;; формируем экземпляр класса pistol
(definstances pistols 
  (РРК of pistol  
   (safety on)    ;; PPK установлен на предохранитель
   (slide forward);; затвор в переднем положении  
   (hammer down)  ;; курок опущен
   (chamber 0)    ;; патронник пуст
   (magazine out) ;; обойма вынута
   (rounds 6)     ;; в обойме 6 патронов
   )
)
;; создаем новый класс pistol2 на базе класса pistol
(deffclass pistol2
   (is-a pistol)
   (role concrete)
       ...
) 

Загрузить полностью листинг примера, в котором средствами CLIPS моделируются правила обращения с полуавтоматическим пистолетом, можно, щелкнув по этой ссылке.

Объекты инициализации. Конструкция definstances позволяет задать априорные (инициализирующие) знания в виде набора экземпляров пользовательских классов.

При перезагрузке рабочей памяти CLIPS (командой reset) каждый экземпляр, определенный внутри конструкции definstances, добавляется в список экземпляров базы знаний CLIPS.
Наверх

Механизмы представления знаний

CLIPS предоставляет три механизма представления знаний: эвристический, процедурный и объектно ориентированный. Рассмотрим эти механизмы подробнее.

Эвристический подход.

Одним из основных подходов к представлению знаний в CLIPS является использование правил. Правила используются для представления эвристик ("правил влияния"), определяющих набор действий, которые необходимо выполнить в данной ситуации. Разработчик экспертной системы (инженер по знаниям) задает набор правил, которые совместно работают над разрешением проблемы. Правило (rule) состоит из антецедента (antecedent) и консеквента (consequent). Антецедент правила есть не что иное как часть "ЕСЛИ..." (то есть список условий) и называется LHS (см. выше). Консеквент — это часть "ТО..." (то есть список действий) и называется RHS (Right Hand Side — правая часть правила).

Антецедент определяет набор условий, которые должны быть удовлетворены для активации правила. В CLIPS удовлетворение условий базируется на существовании или несуществовании определенных фактов в списке фактов (см. выше) или определенных экземпляров пользовательских классов в списке экземпляров базы знаний. Один вид условия, которое может быть задано, — это образец pattern. Образцы состоят из набора ограничений, которые используются, чтобы определить, какие факты или объекты удовлетворяют условию, заданное образцом. Процесс сопоставления фактов и объектов с образцами называется pattern-matching. CLIPS обладает механизмом машины логического вывода, который автоматически ставит в соответствие образцам текущее состояние списка фактов и списка экземпляров и определяет, какие правила активизировать.

Консеквент правила — это набор действий, которые выполняются в случае активизации правила. Действия соответствующих правил выполняются, когда машина логического вывода CLIPS проинструктирована начать выполнение соответствующих правил. Если возникла ситуация, что может быть активизировано более чем одно правило, машина логического вывода задействует стратегию разрешения конфликтов чтобы выбрать, какое именно правило должно быть активизировано. Действия выбранного правила выполняются (что может повлиять на список правил, которые могут быть активизированы в дальнейшем), затем машина логического вывода другое правило и его действия выполняются. Этот процесс выполняется до тех пор, пока существуют правила, которые могут быть активизированы либо пока программа не будет остановлена принудительно (прекращение выполнения программы может быть одним из действий консеквента правила).

Правила в CLIPS реализованы в привычной форме:

     ЕСЛИ условие_1 и ... и условие_M удовлетворяются,
     ТО
     ВЫПОЛНИТЬ действие_1 и ... и действие_N.

Следует заметить, что количество условных предпосылок M и число действий N, подлежащих выполнению в случае удовлетворения условий, в общем случае не равны. Если КАЖДОЕ условие в LHS находит себя среди фактов, то происходит активизация правила и выполнение ВСЕХ действий, записанных в его RHS. В противном случае правило не активизируется.

Для создания правила используется конструкция defrule, которая имеет следующий синтаксис:

     (defrule имя_правила
		 [необязательный комментарий]
		 [необязательное объявление]
		 (условие_1)
		 (условие_2)
		 . . . . . .
		 (условие_M)
		 =>
		 (действие_1)
		 (действие_2)
		 . . . . . .
		 (действие_N))

Обратите внимание, что LHS отделяется от RHS комбинацией символов "=>". Чтобы было более понятно, рассмотрим простенький пример с несколькими правилами.

;; описание шаблонов

;; fact - субъект с некоторым свойством
   (deftemplate fact
      (field subject  (type SYMBOL)) ;; субъект
      (field property (type SYMBOL)) ;; его свойство
   )

;; action - действие, которое нужно выполнить
   (deftemplate action
      (field to_do (type STRING))   ;; что делать
   )

;; факты, задающие исходное состояние проблемы
   (deffacts basic_state
      (fact (subject day)(property day_off))  ;; выходной день
      (fact (subject weather)(property sunny));; погода солнечная
   )

;; правила
;; если день будний - нужно идти на работу
   (defrule work
      (fact (subject day)(property weekday))
       =>
      (assert (action (to_do "go to work")))
      (printout t "Нужно идти на работу!" crlf)
   )
;; если день выходной - можно остаться дома
   (defrule rest
      (fact (subject day)(property day_off))
       =>
      (assert (action (to_do "stay at home")))
      (printout t "Можно остаться дома!" crlf)
   )  
;; если день выходной и к тому же нет дождя - можно пойти на прогулку
   (defrule good_rest
      (fact (subject day)(property day_off))
      (fact (subject weather)(property ?P&~rainy))
       =>
      (assert (action (to_do "go for a walk")))  
      (printout t "Можно пойти на прогулку!" crlf)
   )  
;; если нужно куда-то идти и погода дождливая - взять зонт
   (defrule take_umbrella
      (action (to_do ?A))
      (fact (subject weather)(property rainy))
       =>
      (if (eq (sub-string  1 2 ?A) "go") then (printout t "Нужно взять зонт!" crlf))
   )

;; если нужно куда-то идти и погода солнечная - взять солнцезащитные очки
   (defrule take_sunglasses
      (action (to_do ?A))
      (fact (subject weather)(property sunny))
       =>
      (if (eq (sub-string 1 2 ?A) "go") then (printout t "Нужно взять солнцезащитные очки!" crlf)) 
   )  

Программа состоит из трех частей: базы данных basic_state, описываемой конструкцией deffacts; шаблонов фактов fact и action; пяти правил work, rest, good_rest, take_umbrella, take_sunglasses.

В базе данных содержаться сведения о предметной области: какой сегодня день (будний или выходной) и какая погода (в данном примере не учитываются все разновидности погоды, а только знания о том, солнечная погода или дождливая). Сведения представлены в виде набора шаблонных фактов. Поле subject первого факта указывает на то, что речь идет о дне (day); поле property содержит значение свойства дня — выходной (day_off). Поле subject второго факта указывает на то, что речь идет о погоде (weather); поле property содержит значение свойства погоды — солнечная (sunny). Правило work определяет, нужно ли идти на работу. Правило активизируется, если будет выполнено условие "день будний", то есть если в базе данных будет факт (fact (subject day)(property weekday)). В этом случае в базу данных будет добавлен факт (action (to_do "go to work")), а также на экран будет выведено соответствующее сообщение командой printout.

Правила rest и good_rest определяют альтернативные действия в случае, если день — выходной. Первое правило активизируется единственным условием в списке LHS (достаточно, чтобы в базе данных был факт (fact (subject day)(property day_off))), в то время как второе правило активизируется, если будут удовлетворены два условия в списке LHS, то есть если в базе данных будут факты, свидетельствующие, во–первых, что день выходной, а во–вторых — что погода не дождливая. В нашем примере очевидно будут активизированы оба эти правила, налицо конфликт — чему отдать предпочтение: отдыху дома или прогулке? Выбор приоритетного правила будет зависеть от стратегии разрешения конфликтов. Можно воспользоваться предусмотренным CLIPS свойством выпуклости и добавить, например, в правило rest после имени правила строку (declare (salience 10)), придав ему выпуклость 10, что сделает это правило приоритетным по сравнению с good_rest со значением выпуклости, равным 0 по умолчанию. Однако было бы предпочтительнее усовершенствовать программу, разделив контексты, в которых используются эти правила. Например, в контексте_1 (для человека ленивого) приоритетным действительно было бы остаться дома (активизация правила rest), а в контексте_2 (для человека активного) приоритетной была бы прогулка (активизация правила good_rest). Каждый контекст можно было бы обрабатывать отдельно.

Наверх

Процедурный подход. CLIPS также поддерживает процедурный механизм, как большинство традиционных языков программирования, таких как Pascal или C. Функции, заданные конструкцией deffunction и родовые функции позволяют пользователю создавать новые исполнимые элементы, выполняющие полезные второстепенные действия или возвращают некоторое полезное значение. Созданные функции вызываются так же, как и встроенные функции CLIPS. Обработчики сообщений позволяют задать соответствующее поведение объекта в ответ на получаемые им сообщения. Функции, родовые функции и обработчики сообщений — это все процедурные части пользовательского кода, выполняемого CLIPS в соответствующих ситуациях.

В CLIPS процедурный и эвристический механизмы представления знаний могут тесно взаимодействовать путем вызова пользовательских функций как из LHS, так и из RHS. для создания пользовательских функций используется конструктор deffunction, который имеет следующий синтаксис:

   (deffunction имя_функции
	     [необязательный комментарий]
	     (список формальных параметров)
	     (действие_1)
	     (действие_2)
	     . . . . . .
	     (действие_N))

Например, определим функцию om(x,y), которая возвращает целую часть частного от деления переменной y на переменную x:

   (deffunction om
	     (?x ?y)
	     (div ?y ?x))

Обратите внимание, что в CLIPS имя переменной начинается с символа "?", что для вызова функции, в данном случае встроенной функции деления нацело div, используется префиксная нотация и что вся конструкция представляет собой список, состоящий из четырех полей. Так что у CLIPS ноги растут не только из С (CLIPS — C Language Integrated Production System), но и из LISP.

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

Наверх

Объектно-ориентированный подход. В CLIPS для организации объектно-ориентированного подхода к представлению знаний предусмотрен собственный объектно-ориентированный язык (функциональные возможности которого, тем не менее, уже, чем у того же C++) COOL — CLIPS Object Oriented Language. Язык COOL, включенный в состав CLIPS, имеет 17 системных классов, причем некоторые из них выполняют функции метаклассов. На самом верхнем уровне иерархии расположен класс Object, ниже которого в иерархии два производных класса — Primitive и User. Производными от Primitive являются классы Number, Instance, Address, Multifield и Lexeme. Производный класс от User — Initial-Object.

Все классы, определенные пользователем, являются производными от User, который отчасти выполняет функции метакласса. В нем реализованы практически все базовые обработчики инициализации и удаления объектов. Однако User все–таки не является метаклассом, поскольку классы, определенные пользователем, — это производные от User, а не его экземпляры. Initial-Object является экземпляром по умолчанию, который создается при выполнении функции definstances. Класс Primitive и его подклассы реализуют основные структуры данных — числа, символы строки, адреса и многокомпонентные объекты. Все вышеперечисленные классы, кроме Initial-Object, являются абстрактными и служат для определения родовых операций и структур данных.

Наверх

Обзор ресурсов по данной тематике

Во всемирной "паутине" Internet по данной тематике можно найти не так уж много ресурсов. Причем русскоязычные материалы по CLIPS, как и многие англоязычные, представлены только обсуждениями на специализированных форумах. К сожалению, из подобных обсуждений в большинстве случаев можно почерпнуть очень мало полезной информации. Из моих посещений русскоязычных форумов, где так или иначе затрагивалась тема CLIPS, наиболее удачным оказалось посещение форума ресурса Библиотека программиста. Там мне подсказали ссылки на наиболее содержательные (правда, опять-таки, англоязычные) ресурсы, а также выслали электронной почтой несколько интересных русскоязычных статей, посвященных применению средств CLIPS (часть из них см. в библиотеке).

Говоря об англоязычных сайтах, посвященных CLIPS, чаще всего ссылаются на ресурс http://www.ghg.net/clips/. Здесь размещена вводная информация о том, что представляет собой CLIPS как язык программирования, история его создания и развития, информация о его использовании при создании систем искусственного интеллекта и экспертных систем, приведены ответы на часто задаваемые вопросы по CLIPS, информация о версиях и обновлениях CLIPS. Имеется также перечень серверов, связанных ресурсов, электронных конференций и подписок по CLIPS. Кроме того, отсюда можно бесплатно скачать некоторую документацию по CLIPS, программные коды и исполнимые файлы.

Еще один ресурс http://www.siliconvalleyone.com/ — сайт фирмы "Silicon Valley One". Отдельная страница посвящена информации и новостям CLIPS. Предоставлен перечень ссылок на ресурсы, где можно найти:

  • архив версий CLIPS, всевозможные патчи, ссылки;
  • документацию, программные коды, новейшие обновления, ссылки;
  • документ, посвященный параллелизации программирования на CLIPS и подобных языках, основанных на правилах, с перечнем релевантных ссылок по исследованиям в данной области;
  • документация по включению CORBA-агента в CLIPS-проекты для расширения их возможностей;
  • библиография литературы по CLIPS;
  • а также сопутствующая информация о языке, основанном на правилах — OPS-2000 и параллельных продукционных системах (PPS — Parallel Production Systems), включая документацию и примеры программ.

Еще один интересный и довольно содержательный сайт — CLIPS DLL Homepage, на котором можно найти:

  • страницы, посвященные CLIPS и FuzzyCLIPS;
  • документацию по CLIPS в формате PDF;
  • архив CLIPS и список рассылок, а также ссылку на форум разработчиков систем на CLIPS;
  • готовые проекты на CLIPS, демо-версии программ, примеры (всего более 50);
  • статьи и документацию по CLIPS DLL

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

И напоследок еще пару слов о том, зачем вообще использовать средства специальных языков представления знаний, например, CLIPS. Казалось бы, все выше изложенное позволяет сделать вывод, что то же самое можно было бы при желании реализовать на каком-нибудь процедурном языке программирования, таком как С++, а в случае объектно-ориентированного подхода тем более. Однако не стоит быть поспешными в суждениях.

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

     char able_to_fly (char *s)
     { 
       char answer='д';
       if (strcmp(s,"страус")==0)
       { answer='н';}
       return answer;
     } 

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

        ...
      char c;
      c = able_to_fly("страус");
        ...

А теперь предположим, что вместо этого у нас есть шаблон и два правила на языке CLIPS, которые хранятся в базе знаний:

     ;; шаблон "птица"
     (deftemplate bird
        (field name(type SYMBOL))
     )    
     ;; правило, утверждающее, что всякая птица летает
     (defrule flying
       (bird (name ?X))
        =>
       (assert (yes))
     )
     ;; правило, утвержающее, что страус не летает    
     (defrule not_flying
       (declare (salience 10)) ;; придаем этому правилу выпуклость
       (bird (name strauss))
        =>
       (assert (no))
     )  

Здесь для случайно выбранной птицы утверждается, что она умеет летать, но если известно, что птица — страус, то утверждается, что летать она не способна. Однако поскольку страус — тоже птица (а вы как думали?!), то какой–то другой компонент экспертной системы должен решить, какое из этих правил применять в данной ситуации. Этот компонент называется машиной логического вывода (inference engine). Машина логического вывода обращается к базе знаний, извлекает знания, необходимые для ответа на конкретный вопрос, и передает сформированный ответ пользователю либо как решение проблемы, либо в форме рекомендации или совета.

В этом примере совершенно отчетливо видна модульная природа правил. Код, который в явном виде вызывает то или иное правило, отсутствует. Повторюсь, что в системах, основанных на знаниях, часть программы, которая содержит представление знаний, касающихся конкретной предметной области, — база знаний, как правило, отделена от той части программы, которая занимается формулировкой соображений, — машины логического вывода. Такое разделение позволяет вносить изменения (конечно, в разумных пределах), в одну часть программы, не меняя другой. В частности, можно добавлять в базу знаний новую информацию, расширяя имеющиеся в системе знания, или настраивать механизм логического вывода, повышая его эффективность, и при этом не модифицировать программный код системы, что является значительным преимуществом. Поэтому гораздо удобнее использовать CLIPS, нежели традиционные языки программирования, ввиду того, что одним из компонентов CLIPS является машина логического вывода, которая сопоставляет имеющиеся факты и определенные правила и выясняет, какие из правил можно активизировать. И не нужно «изобретать велосипед»!

Наверх

ЛИТЕРАТУРА

  1. Джексон П. Введение в экспертные системы.: Пер. с англ.: Уч. пос. — М.: Изд. дом «Вильямс», 2001. — 624 с.: ил.
  2. Трофимов В. База данных+CLIPS=База знаний// Компьютеры+программы. — 2003. — N 10. — C. 56–61
  3. Трофимов В.Е. Фрейм как высшая стадия структурирования// Компьютеры+программы. — 2004. — N 7. — C. 45–54
  4. Трофимов В.Е. Автоматизация процесса диагностики РЭА на основе метода эвристической классификации// Технология и конструирование в электронной аппаратуре. — 2004. — N 2. — C. 18–24
  5. Cайт фирмы GHG Corporation, раздел CLIPS
    http://www.ghg.net/clips/;
  6. Cайт фирмы Silicon Valley One, раздел CLIPS
    http://www.siliconvalleyone.com/clips.htm;
  7. Домашняя старница CLIPS DLL, программные разработки и документация по CLIPS
    http://ourworld.compuserve.com/homepages/marktoml/clipstuf.htm.
Наверх
ДонНТУ Магистры Главная Автореферат Библиотека Ссылки Отчет о поиске
© 2005 ДонНТУ
© 2005 Капустина Е.А.