Символическое представление
Понятие символ настолько распространено в современной теории и практике искусственного интеллекта, что важность его трудно переоценить. Именно на этом понятии базируются главные связи между проблематикой искусственного интеллекта и формальными системами математики и логики. Если воспользоваться самой понятной терминологией, то символ — это нечто, замещающее другое нечто. В этом определении "другое нечто" обычно называется значением (designation) символа. Это то, на что ссылается и что представляет символ. Значением может быть физический объект или понятие (концепт), но сам символ является физическим объектом. Так, цифра "7" является символом, представляющим число 7, которое является понятием.
Идея, которая скрывается за термином "символические вычисления", состоит в том, что мы можем понимать под символом, с которым выполняются какие-то действия, все, что угодно. Языки программирования, основанные на этой парадигме, поддерживают множество простейших структур данных, связывающих одни символы с другими, а также примитивные операции манипулирования символами и структурами символов. Таким образом, программист должен специфицировать
такие синтаксические правила формирования символических структур из символов, которые придают сформированным структурам смысл, зависящий от смысла компонентов;
правила трансформации, регламентирующие преобразование одних символических структур в другие.
Как правило, программы манипуляций с символами принимают в качестве исходной информации одну или более символических структур, представляющих исходное состояние решаемой проблемы, и возвращают символическую структуру, представляющую конечное состояние проблемы или ее решение, причем и вход, и выход должны иметь форму, удовлетворяющую оговоренные синтаксические правила, а преобразование входных символических структур в выходную должно выполняться только с использованием дозволенных правил трансформации. Программа на таком языке сама по себе также является символической структурой. Поэтому нет никаких формальных препятствий к тому, чтобы некоторая программа не могла рассматриваться в качестве исходных данных для другой, а отсюда следует вывод, что такое единообразие в представлении программ и данных очень полезно в контексте проблематики искусственного интеллекта. Но еще более важной является идея, что можно сделать нечто большее, чем просто сформулировать правила манипулирования символами, — мы можем воплотить эти символы вместе с правилами манипулирования ими в виде какого-то физического устройства. Отсюда следует очень, казалось бы, простая и в то же время очень продуктивная идея — идея физической символической системы.
Физическая символическая система
Ньюэлл [Newell, 1981] описывает физическую символическую систему как помещенную в некоторую среду машину, состоящую из следующих компонентов:
памяти, включающей символические структуры, число и содержание которых может изменяться во времени;
набора операторов для манипулирования символическими структурами, например чтения, записи, копирования;
средств управления, предназначенного для непрерывной интерпретации текущей активной символической структуры или структуры, к которой выполняется обращение;
средств ввода из окружающей среды посредством рецепторов и вывода в окружающую среду посредством эффекторов.
Программа в физической символической системе — это также символическая структура, которая интерпретируется или обрабатывается каким-то способом, зависящим от символов, составляющих ее (программу), и от символов, полученных от средств ввода. Простейшие программы соответствуют операторам манипулирования символами, а более сложные описывают процедуры, скомпонованные из этих операторов. Средства управления способны отличать данные от программы, хотя и те и другие являются символическими структурами. Физическая символическая система весьма схожа с компьютером общего назначения, оснащенным программами обработки символов. Известно, что компьютер с хранимой программой является универсальной машиной (грубо говоря, он может моделировать операции любой другой машины), а следовательно, обладает способностью воспроизводить все обобщенные рекурсивные функции (т.е. все функции, которые могут быть реализованы любой машиной). Именно наличие такого средства, которое потенциально подходит для реализации абстрактной физической символической системы, и вдохновило исследователей на смелое предположение, что машина может обладать интеллектом.
В следующем разделе мы рассмотрим реализацию и использование физической символической системы в искусственном интеллекте.
4.1.
Главная гипотеза
"Физическая символическая система имеет необходимые и достаточные средства для того, чтобы производить осмысленные действия".
Другими словами, без символических вычислений невозможно выполнять осмысленные действия, а способность выполнять символические вычисления вполне достаточна для того, чтобы быть способным выполнять осмысленные действия. Таким образом, если мы полагаем, что животное, или человек, или машина действуют осмысленно, то значит, они каким-то образом выполняют символические вычисления. (Ваш кот в действительности умнее, чем вы думаете.)
Независимо от того, справедлива ли эта гипотеза, символические вычисления стали реальностью, и полезность этой парадигмы для программирования трудно отрицать.
Реализация символических структур на языке LISP
Как только мы беремся за задачу реализации символических структур и выполнения операций над такими структурами, немедленно встает вопрос, о каких именно структурах идет речь. Символы в логике и математике обычно организованы в виде множеств или последовательностей. Посмотрим, как эти формальные структуры, достаточно понятные на абстрактном уровне, можно использовать в качестве базиса для структуры, объединяющей физические символы.
Множество — это неупорядоченный набор элементов, в то время как физические символы в структуре должны занимать определенное положение (это положение может быть скрытым от программиста, и он может рассматривать структуру как неупорядоченное множество, но это уже относится в особенностям реализации). Последовательность, как структура, позволяет говорить о месте символа в этой последовательности, но абстрактная последовательность может быть бесконечной.
Хорошим кандидатом на место базисной структуры в физической символической системе является список— элементы в списке занимают совершенно определенное место и его можно однозначно связать с каждым отдельным элементом. С помощью списка можно представить и множество, и последовательность, хотя размерность последней и ограничена физическими характеристиками среды реализации.
Структуры данных в языке LISP
Одним из первых языков обработки списков был LISP2 [McCarthy, 1960]. За четыре десятилетия, которые прошли после появления его первой версии, язык неоднократно, модифицировался и расширялся, но в основе своей изменился мало. Разработчики языка утверждали, что LISP отличается от прочих языков программирования следующими свойствами [McCarthy et al, I960]:
основной структурой данных в нем является список;
программы на этом языке также имеют списочную структуру;
его базовыми операциями являются операции над списками.
В 1960 году выбор списков в качестве базовой структуры языка программирования рассматривался как революционный шаг. Сейчас большинство языков программирования общего назначения тем или иным образом поддерживает операции над списочными структурами, хотя от программистов обычно требуется запрашивать выделение памяти для формирования списка, а затем после его использования — возвращать память системе. В LISP еще на ранних стадиях развития в исполняющую систему был встроен механизм "уборки мусора", и программисту не требовалось следить за распределением памяти.
Базовым блоком в структуре данных языка LISP является символическое выражение. Простое символическое выражение использует атомарные символы, или атомы — строки буквенно-цифровых символов, которые начинаются с буквы, например WOMBAT. (Допустимая длина строки варьируется в зависимости от версии исполняющей системы.) Во внутренней структуре данных атом представлен ячейкой памяти. Отдельным атомом является символ Т, которым представляется константа "True" — истина. Другой специальный атом, NIL, представляет, с одной стороны, константу "False"—ложь, а с другой — пустой список.
Составные выражения объединяются в древовидной структуре, при этом используется очевидное соответствие между символическими выражениями и представлением конечных деревьев. Читатели, склонные к математическим формулировкам, найдут более строгое изложение этого соответствия во врезке 4.2.
Списки представляют собой довольно гибкие структуры данных, поскольку могут объединять элементы разных типов и иметь произвольную длину и размерность (вложенность). Например, в LISP возможен такой список:
("а" (9) () N (? (WOMBAT)) (A . В) NIL 0.9)
Этот список содержит элементы разных типов — строки, числа с фиксированной и плавающей точкой, атомы, булевы значения, точечные пары и другие списки.
Но списки имеют и определенные недостатки, из-за которых в LISP были включены и другие структуры данных. Списки в LISP представляют собой стеки, т.е. доступ к ним возможен только с одного конца списка. Манипулируя только таким списком, невозможно обратиться к элементу списка по его позиции, как это делается с элементом массива. Поэтому для представления больших совокупностей относительно постоянных или редко меняющихся данных в LISP были включены другие типы структур. В современных версиях LISP поддерживаются массивы, хэш-таблицы и структуры, подобный записям, которые позволяют эффективнее использовать пространство памяти и повысить скорость доступа.
4.2.
Списки и точечные пары
Любой атом является символическим выражением.
Если А1 и А2 суть символические выражения, то (А1 A2)— это также символические выражения.
Если S = (А,. (А2 . (.... (Ап-1. AJ ....))) — суть символическое выражение для некоторого п>0, то S — список тогда и только тогда, когда Аn =NIL.
В соответствии с этим определением, если п=0, то S представляет собой пустой список, NIL. Такое определение допускает существование списка списков, а также списка атомов. Если S1 S2, ..., Sn— символические выражения, то мы будем представлять список
S1.(S2.(.... (Sn. NIL)....))) в виде
(S1 S2.... Sn)
Таким образом, (А . (В . NIL)) является списком. Он представляет список (А В), но (А . (В. С)) списком не является, поскольку (С=NIL)).
Символическое выражение, которое не является ни атомом, ни списком, называется точечной парой. Если (А . В) — точечная пара, то А — это голова пары, а B — ее хвост. Точечные пары могут иметь произвольную сложность. Так, ((А . В). С) — тоже точечная пара, так же, как и ((А . В) . (С. D)). Благодаря наличию соответствия между точечными парами и списками, понятия головы и хвоста определены и для списков. Поскольку список (А В) — это (А . (В . NIL)), то очевидно, что А — голова в списке (А В), а хвост — это (В), но не В. Хвостом списка (В) является NIL
Структура LISP-программы
Как использовать список в качестве базовой структуры данных, понятно. Сложнее представить себе, как можно организовать программу или выражение программы в виде списка. Например, список
(+ X Y) представляет математическое выражение в форме
(<функция> <1-й аргумент> <2-й аргумент>),
которое обозначает сложение двух чисел. Такой метод обозначений (нотация) отличается от привычного нам обозначения функции п переменных в виде f(x1, ... xn). Но возникает вопрос, как компилятор или интерпретатор отличает данные от программы, если и то и другое представлено списками.
Необходимо иметь возможность определить значение такого выражения— например, значение выражения ( + X Y). Для этого нужно отыскать определение функции (в данном случае — функции +). В этом определении должна содержаться та последовательность элементарных операций, которую нужно применить к аргументам, чтобы определить значение функции.
Нужно иметь средства сформировать определение функции и применить это определение к аргументам, т.е. к действительным, а не формальным параметрам. Механизм определения функции и приложения функции базируется на логической системе, названной лямбда-исчислением (см. [Church, 1941]). Лямбда-исчисление является скорее функциональным, чем исчислением отношений, и в этом состоит различие между ним и исчислением предикатов первого порядка. В функциональном исчислении первичным понятием является отношение "многие-к-одному", а не "многие-ко-многим". Так, отец — это функциональное отношение, а брат — более общее отношение, поскольку каждый человек может иметь только одного отца, а братьев может быть несколько. Ниже в этой главе мы более подробно остановимся на связях между языком LISP и лямбда-исчислением.
Нужно располагать средствами доступа к текущим значениям переменных (или формальных параметров), таких как X и Y. Вычисление каждого символического выражения выполняется в контексте формирования переменных. Нужно располагать средствами сохранения и восстановления этого контекста при вычислении значений сложных символических выражений, т.е. вычисления и комбинирования значений содержащихся в них подвыражений.
При вычислении сложных символических выражений, когда необходимо вычислять значения его компонентов, которые являются сложными выражениями, нужно располагать средствами сохранять текущее выражение и промежуточные результаты. Необходимо также обладать средствами копирования символических выражений.
Нужно уметь подавлять вычислительную обработку списков, которые не являются операторами программы, а структурами данных. Например, не нужно пытаться вычислять выражение, подобное следующему:
((1 2 3)(4 5 6)(7 8 9))
и пытаться отыскать определение функции (1 2 3).
Для этого существует специальная форма выражения (QUOTE X) для любых X, которая возвращает X. Точно такое же действие выполняется и выражением (quote X).
Современные версии LISP не чувствительны к регистру символов, хотя и возможно так сконфигурировать исполнительную систему, что она станет по-разному воспринимать символы верхнего и нижнего регистров.
4.3.
Функции, их вычисление и проблема цитирования в CLIPS
Например, в CLIPS можно следующим образом определить функцию:
(deffunction between(?lb ?value ?ub) (or (> lib ?value) (> ?value ?ub))))
Эта функция определяет, попало ли заданное целочисленное значение в диапазон между указанными нижним и верхним пределами. Знак вопроса, предшествующий именам, говорит интерпретатору CLIPS, что выражения ?lb, ?value и ?ub являются переменными и их не нужно оценивать.
Общепринятым методом реализации функциональных языков типа LISP является использование четырехстековой машины, за которой закрепилось наименование SECD-машины. В четырех стеках машины отслеживаются промежуточные результаты, значения переменных, текущее выражение и копии текущего состояния процесса вычислений сложного выражения, которые нужны, чтобы восстановить состояние после завершения вычисления вложенного выражения (подвыражения). Не вдаваясь в подробности, отметим, что процесс оценивания символического выражения в такой машине — это не что иное, как реализация базовой операции приложения функции, как это определено в лямбда-исчислении (см., например, [Henderson, 1980], [Glaser et al., 1984]).
Приложение функции и лямбда-исчисление
Для того чтобы разобраться в связи между лямбда-исчислением и языком LISP, нужно постоянно держать в уме сформулированное Черчем отличие между денотацией (означиванием) и абстракцией. Так, выражение (X X) означивает конкретное число, которое зависит от значения X. Но то же число можно получить и при помощи функции square(X), которая является абстракцией, поскольку ее можно приложить к разным значениям X. Для того чтобы отличить означивание от абстракции, первое представляется в лямбда-исчислении в таком виде:
(лх)(X х)
Говорят, что лямбда-оператор, X, связан с переменной X, как квантор связывает отдельные переменные в исчислении предикатов. Тогда (ЛХ)(X X) может служить определением функции возведения в квадрат:
square(X) = (лX)(X X)
Теперь для применения функции возведения в квадрат к конкретному числу, скажем 3, мы должны каким-то образом подставить 3 вместо переменной X и оценить (X ,Х), в результате чего получим 9. Когда определение функции применяется к аргументу 3, используется правило влияния, получившее наименование лямбда-преобразования. Доложим, что (лХ)М определяет любую лямбда-абстракцию, и пусть S(a, X, М) — результат подстановки X в М.
Лямбда-преобразование. Заменим любую часть (lХ)М в формуле на s(a, x, М), причем ограниченные переменные в м отличны как от х, так и от свободных переменных а.
Если мы полагаем, что ((ЛХ)М) а обозначает применение определения функции (ЛХ)М к аргументу а, то
((ЛХ)(Х Х))(3) = (3 3) = 9 .
Какое же все это имеет отношение к языку LISP? А вот какое. Определение функции возведения в квадрат в LISP выглядит примерно так:
(defun SQUARE (X) (LAMBDA (X) ( X X))) .
В различных диалектах языка допустимы вариации, но смысл остается тем же. В частности, в диалекте COMMON LISP используется сокращенная форма
(defun SQUARE (X) ( X X)) .
В любом случае имя функции, в данном случае SQUARE, ассоциируется в определении с лямбда-выражением. Выражение (LAMBDA (X) ( X X)) — это просто синтаксический вариант ((ЛХ) (X X)). Фактически, если определено SQUARE, в любом диалекте LISP имеем
(SQUARE 3) = ((LAMBDA (X) ( X X)) 3) = ((АХ)(Х Х))(3) = 9 .
Обратите внимание на то, что LAMBDA не является функцией. Это специальный оператор в лямбда-исчислении.
Синтаксическая форма вызова функции в языке LISP имеет вид
(<функция> <аргумент> ... <аргумент>).
Это не самая сложная синтаксическая форма, а вместе с QUOTE, LAMBDA и условными выражениями этим фактически исчерпывается все, что необходимо знать о синтаксисе языка LISP. Тем, кто по каким-то иррациональным причинам испытывает тягу к запятым, двоеточиям, точкам с запятой, палиндромам вроде (if... ft, case ... esac) и тому подобному, будет поначалу трудно свыкнуться с мыслью, что в LISP единственным ограничителем являются круглые скобки. Программа на языке LISP — это просто структура данных, и другая LISP-программа ее может читать, записывать и обрабатывать точно так же, как любой другой набор данных.
Обработка списков
Языку LISP можно дать очень лаконичное формальное определение. Большинство LISP-программ можно специфицировать, используя только пять простейших операторов над символическими выражениями (см. врезку 4.4) и одну специальную форму (условное выражение). Эта элегантность и красота языка LISP часто не заметна неопытному взгляду, поскольку большинство LISP-приложений включает множество дополнительных операторов, собственно к LISP не относящихся. Современные диалекты LISP в буквальном смысле задыхаются от программных конструкций, заимствованных из языка FORTRAN, некоторые из которых оказались полезными, а другие просто вызывают отвращение (справедливости ради, нужно отметить, что некоторые обладают и тем и другим).
Как оказалось, структурой, наиболее подходящей для нечисловых вычислений, являются списки. Именно такие вычисления необходимо выполнять в процессе поиска решения в пространстве альтернатив, как это было показано в главе 2. В списке можно держать в поле зрения те альтернативные варианты, которые уже были рассмотрены ранее, не которые еще предстоит рассмотреть, и т.д. Поскольку между списками и древовидными ориентированными графами существует изоморфизм, естественно представлять развернутое пространство состояний в виде одного или более списков.
4.4.
Примитивы в LISP
Пусть s — множество символических выражений. Можно, например, записать:
Е(Х , Y): S x S -> {Т, NIL}
Это означает, что Е является функцией двух аргументов, причем оба аргумента — символические выражения из множества S, которые могут принимать значение либо Т, либо NIL.
(1)Е(Х , Y): S x S -> {Т, NIL} проверяет, равны ли два атома.
(2)А(Х): S -> {Т, NIL} проверяет, является ли символическое выражение атомом.
(З)Н(Х): S -> S извлекает голову символического выражения, которое не является атомом; если х — атом, то результат функции не определен.
(4) Т(Х): S —> S извлекает хвост символического выражения, которое не является атомом; если х — атом, то результат функции не определен.
(5)С(Х , Y): S х S —> S формирует символическое выражение; если А и в являются символическими выражениями , то можно сформировать новое символическое выражение (А . В).
Совокупности операции композиции функций и условного оператора описанных оераций вполне достаточно для того, чтобы вычислить любую обобщенную рекурсивную функцию. Композиция функций — это способность сделать значение одной срункции аргументом другой, т.е. организовать гнездование функций, например С(Н(Х), У).
Фактически система, состоящая из трех компонентов
(1) единственного атома NIL;
(2) условного выражения, проверяющего равенство, в форме
if E(X, NIL) then ... else ... 3) функций Н(Х), Т(Х), С(ХД)
к которым добавлена операция композиции функций, вполне позволяет реализовать машину Тьюринга (см. [Minsky, 1972, Chapter 10]).
Можно использовать списки и для представления ассоциативной связи одних символов с другими. Например, список
((Alabama Montgomery) (Alaska Juneau) (Arizona Phoenix) ... )
позволяет представить столицы пятидесяти штатов. Представленная ниже LISP-программа сможет затем извлечь название столицы заданного штата из этого ассоциативного списка.
(defun assoc (key alist)
(cond ((null alist) NIL)
((eq (first (first a list)) key) (first alist))
(T (assoc key (rest alist)))) )
Если обратиться к этой функции с помощью, например, выражения (assoc 'Alaska '((Alabama Montgomery) (Alaska Juneau) (Arizona Phoenix) ... ), то функция возвратит список
(Alaska Juneau) .
NULL — это предикат, который проверяет, не пуст ли список, EQ — предикат, который проверяет равенство двух атомов, FIRST — функция, которая возвращает головной элемент списка, a REST — функция, которая возвращает хвост списка (см. врезку 4.4).
Основным условным выражением в LISP является COND. В приведенном выше фрагменте программного LISP-кода это условное выражение может быть расшифровано следующим образом:
если alist это null, то вернуть NIL, иначе
{
если головной элемент головного элемента alist равен key, то вернуть головной элемент alist,
иначе вернуть результат применения функции assoc к хвосту alist. }
Условное выражение COND можно представить в терминах примитива if-then-else, описанного во врезке 4.4. Выражение COND может включать сколько угодно вложенных конструкций if-then-else.
Конечно, ассоциативные списки — это не самое лучшее средство хранения данных, но наш пример с таким списком помог вам представить, как в LISP организуется рекурсивная обработка списков.
Сопоставление с образцом
Одним из ключевых компонентов в большинстве программ искусственного интеллекта является анализатор соответствия (pattern matcher) — компонент, который некоторым образом сравнивает поступающие на его вход списки (или другие структуры данных) с имеющимися символическими образцами и таким образом выполняет распознавание входных данных.
В главе 3 мы обращали ваше внимание на то, что факты, относящиеся к состоянию окружающего мира, представляются в форме "предикат— аргумент". Тот факт, что робот находится в комнате, был представлен в модели мира формулой
at(robot, room). На языке LISP этот факт будет представлен символическим выражением
(at robot room). Положим, что ? — символ универсальной подстановки и что выражение
(at robot ?) представляет собой образец, которому соответствует и выражение (at robot room), и другое выражение в форме
(at robot blah),
где blah — любой символ. На языке LISP несложно разработать простой анализатор соответствия, который будет сравнивать два ординарных списка (т.е. списка, на имеющего подсписков в качестве элементов) и возвращать значение TRUE, если один из них, sample (пример), можно представить как реализацию другого — pattern (образец). Текст такой программы приведен ниже. Предполагается, что образец может иметь любую конечную длину и содержать любое количество символов универсальной подстановки.
(defun match (sample pattern)
(cond ((and (null sample) (null pattern)) T) ((or (null sample) (null pattern)) NIL) ((eq (first pattern '?))
(match (rest sample) (rest pattern))) ((eq (first sample) (first pattern)) (match (rest sample) (rest pattern))) (T NIL)) )
Обращение к этой функции в выражении
(match '(at robot room) '(at robot ?)) даст результат Т, а обращение (match '(at box room) '(at robot ?))
даст результат NIL.
Можно усовершенствовать приведенный анализатор соответствия. Например, сделать так, чтобы он различал другой символ универсальной подстановки в качестве переменной, которой может быть присвоено значение символа, соответствие с которым анализируется. Например, образцу
(at ?X ?Y)
должен соответствовать пример
(at robot room),
который образуется при подстановке {?X/robot, ?Y/room}, как об этом говорилось в главе 3. Можно также потребовать, чтобы присвоение значений переменным было совместимым, т.е. чтобы пример
(at robot room)
не соответствовал образцу
(at ?Х ? X).
Но, как мы видели в главе 3, главное назначение анализатора соответствия — показать, что имеющаяся в программе модель мира удовлетворяет условиям некоторого правила, которое в таком случае программа сможет затем применить. Пусть в программе имеется простое правило, утверждающее, что все объекты, находящиеся в комнате, нужно покрасить:
if (at ?X room) then (paint ?X)
Нужно проверить, соответствует ли условию if (at ?X room) этого правила модель мира, представленная списком
(at box room).
Полученную подстановку {?X/box} применим затем к констатирующей части правила и получим в результате
(paint box).
Анализ соответствия — это довольно "расточительная" операция в смысле расхода вычислительных ресурсов, если только не пользоваться ею с умом. В главе 13 мы увидим, что существуют довольно эффективные алгоритмы, которые позволяют решить, в каких именно из имеющихся в наборе правилах (или отдельном правиле) сформулированы условия, соответствующие анализируемым данным. В настоящее время язык LISP не используется для реализации систем, базирующихся на правилах, в основном из-за недостаточной его эффективности, но по-прежнему используется тот принцип обработки списков при анализе соответствия, который был впервые реализован на LISP.
Почему. LISP не является языком представления знаний
Невольно напрашивается вопрос, почему с помощью LISP нельзя удовлетворить все наши потребности в области представления знаний. Ведь, как было показано, этот язык позволяет хранить и обрабатывать символические структуры и управлять процессом их оценивания. С его помощью можно реализовать анализ соответствия, эвристический поиск и устанавливать наличие ассоциативной связи между символами. Разве всего этого недостаточно для того, чтобы на базе LISP реализовать физическую символическую систему для разумных действий?
h1>
Символический уровень и уровень знаний
Совершенно
ясно, что среда символических вычислений весьма подходит для реализации
структур, необходимых для представления знаний, но символический уровень анализа
ничего не говорит нам о том, чем должны быть такие структуры. Нужен еще один
уровень анализа, расположенный выше символического, который будет ограничивать
набор возможных представлений при решении некоторой проблемы. Ньюэлл в работе
[Newell, 1982] назвал его уровнем знаний и предположил, что знания
должны быть охарактеризованы функционально, т.е. в терминах действия,
а не в терминах структурной организации.
Из предложения
Ньюэлла следует, что нельзя адекватно представлять знания, не располагая сведениями
о том, как они могут быть использованы. Возможно, это одна из причин, которая
побуждает нас разделить факты и знания. То, что норманны в 1066
году захватили Англию, — это только факт. Но мое знание этого факта может быть
использовано совершенно по-разному. В частности, это в равной степени позволит
мне успешно сдать экзамен по истории или спровоцировать драку между англичанами
и французами. Если цель состоит именно в том, чтобы получить более высокую оценку
на экзамене, то представление знаний лучше связать с другими фактами, например
сведениями о короле Гарольде или короле Вильяме. Если же цель — разжечь ненависть
между англичанами и французами, то лучше воспользоваться такими фактами, как
вандализм английский футбольных фанатов или эксцентричная манера вести себя
на дорогах, присущая французским мотоциклистам. Таким образом, не существует
"правильного" способа представить какой-то факт, но существует более
или менее полезное представление знания некоторыми фактами.
В синтаксисе
и семантике языка LISP нет ничего такого, что подсказало бы вам, как организовать
знания. Список — это удобная, но иногда неэффективная в работе структура данных,
но он не имеет никаких преимуществ с точки зрения представления знаний по сравнению
с массивом в языке FORTRAN и менее удобен, чем класс в языке C++. Утверждение
о широких возможностях LISP имеет скорее отношение к тому, что для опытного
программиста довольно легко создать на его основе производный язык по своему
выбору. Но такой специализированный интерпретатор, функционирующий в среде LISP,
оказывается очень непроизводительным, и в этом его основной недостаток.
h1>
LISP и разработка программ
Многие программисты
склонны к тому, чтобы создавать программный код, напоминающий спагетти, и их
буквально приводит в состояние шока знакомство с широкими возможностями, которые
сулит в создании такого кода язык LISP. Но при всем этом сообщество приверженцев
LISP на удивление мало привнесло в методологию программирования (см. [Abelson
et al, 1996]).
Многие программисты склонны к тому, чтобы создавать программный код, напоминающий спагетти, и их буквально приводит в состояние шока знакомство с широкими возможностями, которые сулит в создании такого кода язык LISP. Но при всем этом сообщество приверженцев LISP на удивление мало привнесло в методологию программирования (см. [Abelson et al, 1996]).
4.5.
Гипотеза Смита
"Каждая интеллектуальная физическая символическая система включает символические структуры, которые мы, как внешние наблюдатели, можем расценивать как предложение, .основанное на знаниях, которыми располагает система".
Очевидно, что эта гипотеза ничего не говорит о том, как эти знания могут быть в действительности представлены. Единственное, что нам, как внешним наблюдателям, доступно— это выводы, которые система делает на основании своих знаний. Но эти выводы сами по себе не дают возможности однозначно выяснить, на основании какой схемы представления знаний они cделаны.
Многие идеи, касающиеся представления знаний, зародились в процессе развития методики объектно-ориентированного анализа и проектирования. Объектно-ориентированные языки программирования, такие как C++, SmallTalk и Eiffel, стали в последнее время привлекать все большее внимание конструкторов экспертных систем. Появилось довольно много библиотек классов, которые можно использовать при построении такого рода приложений. В этом же направлении стал развиваться и LISP. В частности, на его основе разработан язык CLOS — Common Lisp Object System, в котором механизм множественного наследования работает значительно эффективнее, чем в C++ (подробнее об этом — в главе 7). Но даже самые верные приверженцы LISP находят маловероятным, что его новейшие диалекты скоро найдут широкое применение в создании коммерческих программных продуктов.
Языки представления знаний
И представление знаний, и объектно-ориентированный подход к программированию основываются на одной и той же идее, что конкретная предметная область приложения имеет такое же значение для модели, как и для проблем, которые нужно разрешить. Если вы работаете в определенной предметной области — технической, издательском деле или сфере управления, — то вид проблемы, которую потребуется решить, будет изменяться не только от проекта к проекту, но и на разных стадиях работы над проектом, по мере того как будут уточняться концепции проекта и его цели. Относительно постоянными остаются только "обитатели" предметной области — машины, процессы, неживые объекты или люди. Представление этих сущностей, которое может быть воспринято машиной и обработано программой, формируется таким образом, чтобы его можно было использовать в самых разнообразных проектах.
В самом общем виде разница между представлением знаний и объектно-ориентированным подходом состоит в том, что в первом случае стремятся представить не только сущности в определенной предметной области, но и знания об этих сущностях, которыми обладают эксперты в данной области. Например, экспертам известны различные способы классификации, упорядочивания, обозрения и манипулирования такими сущностями, которые позволяют эффективно решать разнообразные задачи.
Если рассматривать в этом свете проблематику представления знаний, то ее можно сформулировать следующим образом: "Существует ли способ, пользуясь которым можно закодировать знания о предметной области таким образом, чтобы поддерживать приложение этих знаний к решению различных проблем в разных проектах?" Ответом на этот вопрос является проектирование независящих от приложения "банков знаний", о котором речь пойдет в главе 10. Вы в дальнейшем встретитесь и с такими примерами, когда доступные программе знания по-разному используются в пределах одного и того же приложения (главы 13 и 16).
В последующих трех главах вы познакомитесь как с подходом, базирующимся на правилах, так и с объектно-ориентированным подходом к программированию, причем на примере языка CLIPS будет показано, как можно комбинировать оба этих подхода. Причина, по которой в индустрии производства программных продуктов продолжают оставаться популярными такие инструментальные средства, состоит в том, что как бы ни тяжело было кодировать человеческие знания, такие эпистемологические трудности ничто в сравнении с тем, что может наделать с указателями C++ неопытный программист. Построение системы, базирующейся на правилах на таком языке, — это нетривиальная задача, которую лучше всего поручить специалистам.
Другая причина состоит в том, что при создании с нуля системы, базирующейся на знаниях, аналитики и программисты попадают в такое обширное пространство альтернативных решений, что запутаться в нем гораздо легче, чем отыскать правильный путь. Языки представления знаний предлагают разработчику как программные средства высокого уровня, так и множество конструкций низкого уровня, которые можно использовать для организации и применения знаний, синтаксические и семантические примитивы, уже не раз доказавшие свою полезность на практике.
В главе 5 в качестве основного инструмента для иллюстрации идей построения систем, основанных на знаниях, используется язык CLIPS. Сделано это по следующим причинам:
этот язык относительно дешев;
без зазрения совести разработчики включили в него множество опробованных на практике конструкций из других инструментальных средств;
язык имеет довольно четко сформулированный синтаксис, позаимствованный у LISP;
язык (точнее, его исполнительная система) обладает вполне приемлемой производительностью, так что предлагаемые в качестве примеров программы выполняются достаточно быстро;
язык допускает вызов внешних функций, написанных на других языках программирования;
язык включает средства (правда, ограниченные), позволяющие комбинировать правила и объекты.
В главе 6 будет проанализировано использование структурированных объектов, таких как семантические сети и фреймы, а в главе 7 мы перейдем к более тщательному анализу объектно-ориентированного подхода. Описание методики логического программирования, в частности с использованием языка PROLOG, завершит в главе 8 тему изучения языков представления знаний. В главе 17 вы найдете обзор множества доступных на сегодняшний день программных пакетов, предназначенных для построения экспертных систем, а в главах 18 и 19 анализируются более специализированные инструментальные средства.
Рекомендуемая литература
В качестве наиболее доступного руководства по языку LISP я бы рекомендовал книгу Уинстона и Хорна [Winston and Horn, 1988], а в книгах Чарняка [Charniak et al., 1987] и Грехема [Graham, 1994] можно уточнить многие детали применения LISP для решения задач искусственного интеллекта.
В прекрасной книге Норвига [Norvig, 1992] подробно описан базовый диалект Common LISP, а в книге Рассела и Норвига [Russel and Norvig, 1995] основное внимание уделено программированию задач искусственного интеллекта.
В книге Кратко [Braico, 1990] читатель найдет обширный материал по использованию языка PROLOG для решения задач искусственного интеллекта. Кроме того, желающим изучить язык PROLOG я также рекомендую прочесть книгу Стерлинга и Шапиро [Sterling and Shapiro, 1994].