Продолжение
серии постов об онтологиях. Я уже писал о двух разных концепциях, используемых при создании логических моделей: гипотезах открытости и закрытости мира модели. Обе концепции используются как в повседневной жизни, так и в среде IT-специалистов, причем как раз последние, возможно, обнаружат в этом посте парочку сюрпризов.
Начнем с концепции закрытости мира. Она означает, что наша модель описывает все нужные нам истинные факты. То же, что в нашей модели отсутствует, заведомо предполагается ложным. Именно на этой концепции основано классическое объектно-ориентированное моделирование, знакомое (надеюсь) каждому программисту. В этой модели свойства класса наследуются подклассами: если мы определили для "млекопитающего" свойство "имеет два глаза", то подкласс "собака" автоматически будет иметь то же свойство, даже если оно не определено напрямую. С другой стороны, класс "лягушка" может иметь то же свойство, не унаследованное от "млекопитающего". Далее: классы, не находящиеся на одной линии наследования, принципиально дизъюнктны: индивидуум класса "лягушка" никак не будет одновременно и "собакой". В концепции "закрытого мира" определение класса первично: мы заранее определяем свойства классов и только в рамках готовых классов создаём инстанции. Более того, мы можем создавать абстрактные классы, для которых создание инстанций вообще запрещено и которые нужны исключительно для создания иерархий наследования свойств.
Если этот текст сейчас читает программист, то он наверняка удивляется, зачем я описываю настолько очевидные вещи и, возможно, задаётся вопросом: "А как же моделировать иначе?" На самом деле, концепция моделирования в "закрытом мире" настолько широко распространена и настолько настойчиво вдалбливается студентам, изучающим практическое программирование, что люди, постоянно с ней работающие, начинают воспринимать её, как нечто само собой разумеещееся. Они забывают, что эта концепция, будучи полезной для определённых задач, тем не менее совершенно противоестественна. Чтобы развеять возможные сомнения в этом утверждении, я расскажу, почему этот подход получил популярность и в чем его сильные и слабые стороны.
Идея программирования, основанного на объектно-ориентированной модели возникла в середине 60-х и была впервые применена на практике в языке
Simula67, предназначенном для создания моделей и симуляции физических процессов. Для этих целей "закрытый мир" с четко обозначенным набором законов, процессов и эффектов подходил оптимально. В симулированном "мире" изначально должно быть возможно только то, что мы заложили в его описание. Идея удачно совпала с
кризисом программного обеспечения, когда стоимость разработки программ переросла стоимость "железа" и продолжала неуклонно расти дальше. Объектно-ориентированная парадигма (ООП) обещала решить многие тогдашние проблемы за счет улучшенной модулярности и и потенциальной возможности повторного использование результатов. На ее основе возникли новые языки: Smalltalk, C++, Object Pascal и т. д. И это действительно неплохо работало, пока специализированная программа могла работать в том же "закрытом мире", для которого её создали. При серьёзных изменениях чаще всего нужно было ломать иерархию классов и свойств и создавать её заново, но ООП позволяя ускорить этот процесс. А чтобы ломать проходилось чуть реже, было создано множество правил, стандартных техник и т. п., вроде
design patterns.
Серьёзные проблемы начались с развитием веб-сервисов и распределённых программ. Во-первых, это потребовало от программ способность коммуницировать вне зависимости от внутренней модели, платформы и языка. Во-вторых, это способствовало развитию универсальных фреймворков, интегрурующих разные технологии и требовавших выхода за рамки закрытой модели. Для этого часто используются разные техники (например
"annotation" и
"reflection"), "взламывающие" стандартную ООП-модель. Тем не менее, для обработки данных, полученных скажем через
REST-интерфейс, вам нужно указать, в какой из ваших заранее определённых классов, вы хотите эти данные "упаковать". И если соответствующего класса в вашей модели нет, то обработка таких данных в вашей программе проблематична. При этом я упоминал только наиболее распространённые техники, относящиеся к линии
императивных языков программирования. Что касается логического программирования и моделирования, то тут концепция "закрытого мира" ещё сильнее ограничивает ваши возможности.
Остаётся вопрос, почему я назвал моделирование в "закрытом мире" противоестественным. Я сделал это потому, что, хотя такие модели подходят для решения узкоспециализированных практических задач, они противоречат естественному подходу к приобретению знаний, причем как научному, так и повседневному. Этим утром вы не искали на ваших туалетных принадлежностях указание на их принадлежность к какому-то классу, например в виде надписей на них "это - полотенце" или "это - зубная щётка". Вы определяете класс предмета по его свойствам, а не наоборот. И если подложить вам вместо зубной щетки сапожную с надписью "зубная щётка", вы вряд ли станете чистить ею зубы, а тут же обнаружите несоответствие свойств (в данном случае: размер и отсутствуе рукоятки). Наше мышление работало именно так изначально, поскольку мир, с которым взаимодействовали наши предки, никогда не был закрытым и полностью каталогизированным. Когда кроманьонец видел в лесу еще неизвестное ему животное, он не вспоминал о классификации, а смотрел на признаки: большое - много мяса - сильное - не охотиться в одиночку - острые зубы - когти - ОПАСНОСТЬ - ДЕЛАЙ НОГИ!!! И уже потом (если конечно убежать удалось) он пытался рассортировать всех известных зверей по классам. Причем классы почти всегда пересекались, например "вкусная добыча" и "опасное животное" ни коим образом не были взаимоисключающими.
Более того, аналогичным образом построена логика, лежащая в основе точных и естественных наук. Простой пример: во многих школьных задачах по геометрии нельзя просто назвать фигуру "окружностью" или "квадратом", а надо доказать, что это так, продемонстрировав, что свойства фигуры совпадают со свойствами в определении понятия "квадрат", "равнобедренный треугольник" или "окружность". И если определение со временем меняется (что нетипично для геометрии, но часто происходит в естественных науках), то некоторые инстанции автоматически обретают принадлежность к классу (птицы и "динозавры") или теряют ее (Плутон и "планета"). Классификации не существуют сами по себе, это - не более чем вспомогательные конструкции в наших головах, облегчающие ориентирование при нехватке информации или позволяющее ссылаться на общеизвестный контекст, упрощая коммуникацию. Например, из очень короткой надписи "WC (M)" на указателе вы сразу же сможете вывести около десятка предполагаемых свойств той инстанции, которую эта надпись символизирует. Классификации настолько удобны, что люди цепляются за них, даже при явном устаревании определений и, вопреки логике, всячески сопротивляются изменениям. Это хорошо продемонстрировали многочисленные дискуссии, относящиеся к приведенным выше примерам (динозавры и Плутон) и частично продолжающиеся до сих пор. Кстати, тенденция смешивать формальные определения с реальными данными и цепляться при этом за устаревшие или неверные классификации - типичная черта псевдонаучных (например, креационистских) текстов.
Итак, как же работает моделирование в "открытом мире"? В нём вы точно так же, как и в "закрытом мире" можете создать иерархию классов и привязать инстанции к конкретным классам, унаследовав заданные свойства. Различие же заключается в том, что привязка "класс - набор свойств" работает в "открытом мире" в обе стороны. То есть вы можете создать нечто, обладающее набором свойств, но (теоретически) не имеющее прямого отношения ни к одному классу, после чего ваш reasoner автоматически проверит, определению каких классов соответствует этот набор свойств. В "открытом мире" классы почти всегда пересекаются, потому что инстанция может иметь набор свойств, соответствующий нескольким классам сразу. Сответственно и сами классы имеют как правило сразу нескольких "родителей", так что иерархия наследования представляет из себя не дерево, а граф. Если вы хотите предотвратить пересечение классов, вы должны сформулировать аксиомы, в которых будет указано, например, что классы "обувная щётка" и "зубная щётка" дизъюнктны, т. е. не имеют общих инстанций.
Естественно, при этом подходе существует возможность того, что в вашу классификацию попадут нежелательные инстанции. Происходит этои из-за того, что ваше определение свойств класса недостаточно чёткое. Классический пример из реальной жизни -
платоновское определение "человека". Если вы определили свойства этого класса как "количество ног = 2" и "нет перьев", то будьте готовы обнаружить в этом классе инстанцию "ощипанный петух", полностью соответствующему вашему определению. Впрочем, решение этой проблемы в "открытом мире" тоже очень простое: вы просто добавляете свойства, уточняя определение класса ("плоские ногти" у Платона). После чего reasoner автоматически удалит из класса все лишние инстанции. Вы именно вносите уточнения в вашу онтологию, а не перестраиваете иерархию классов, как это обычно происходит в ООП. Таким образом, ваша модель будет открытой для новых фактов, не предусмотренных в ней изначально, и лучше подходит для долговременного сбора сложной информации.
Вот на этом я, пожалуй, и закончу теоретическую часть. Осталось только описание конкретных языков языков и инструментов для работы с базами знаний на их основе.