Обзор возможностей OWL 1 и OWL 2

Эта страница представляет собой главу из нашего методического пособия
"Введение в онтологическое моделирование" (нажмите для перехода к полной версии в формате PDF).

Ранее мы весьма поверхностно рассмотрели основные концепции RDF/RDFS/OWL, связанные с описанием классов, свойств, связей и индивидуальных объектов. Настало время узнать более подробно, какие технические средства моделирования предлагает стандарт OWL, но для этого нужно сначала рассмотреть одну возможность RDF, о которой мы умолчали – возможность создавать группировки объектов, не являющиеся классами.

Итак, RDF позволяет объявлять контейнеры трех типов:

1. rdf:Bag, объединяющий элементы модели или литералы без указания порядка их следования в группе.

2. rdf:Seq, делающий то же самое с указанием порядка следования элементов.

3. rdf:Alt, представляющий группу элементов модели или литералов, которые являются в определенном смысле альтернативами – например, для определенного значения некоторого свойства. Например, этот способ можно использовать для указания перечня страниц Интернета, где можно получить какую-либо информацию. При использовании такого контейнера приложение может предложить пользователю выбрать любой из содержащихся в нем вариантов.

Все эти способы применяются, как правило, для указания значений каких-либо свойств. Например, если в группу входят несколько студентов, значением свойства «имеет студента» для группы может быть один из перечисленных контейнеров, объединяющий конкретных студентов.

Все перечисленные способы группировки не утверждают, что в создаваемых группах содержатся только перечисленные члены – иными словами, эти группы «открыты». Для создания «закрытых» групп, в которых участвует четко определенное число элементов, используются коллекции RDF. Для указания того, что определенный контейнер является коллекцией, используется атрибут rdf:parseType="Collection". Не будем останавливаться на конкретном синтаксисе, который можно найти в стандарте и многочисленных примерах к нему – для нас упоминание о коллекциях важно потому, что они потребуются при рассказе о возможностях собственно OWL.

Прежде всего отметим, что OWL определяет два предустановленных класса – owl:Thing и owl:Nothing. Первый из них объединяет все индивидуальные объекты модели, а его подклассами являются все ее классы. Второй не имеет экземпляров, и является подклассом любого класса онтологии.

Начнем обзор с рассмотрения способов объявления классов. Мы говорили, что класс – это именованная (или анонимная) группа, объединяющая индивидуальные объекты или другие классы. До сих пор мы использовали только один способ их объявления; между тем, OWL 1 предлагает их целых шесть.

1. Объявление класса по идентификатору – это уже знакомый нам способ явного объявления именованного класса.

2. Перечисление. В этом случае класс задается явным перечислением входящих в него индивидуальных объектов. В синтаксисе RDF/XML такое определение может выглядеть следующим образом:

<owl:Class rdf:ID="#TerrestrialPlanet">
  <owl:oneOf rdf:parseType="Collection">
    <owl:Thing rdf:about="#Earth"/>
    <owl:Thing rdf:about="#Mars"/>
    <owl:Thing rdf:about="#Venus"/>
    <owl:Thing rdf:about="#Mercury"/>
  </owl:oneOf>
</owl:Class>

При таком объявлении (а также при всех последующих) класс может быть и анонимным, т.е. не обязан иметь идентификатор.

3. Ограничение на свойства. Объявляет анонимный класс, который состоит из всех индивидуальных объектов, удовлетворяющих определенному условию. Условия могут быть двух типов. Первое из них формулируется как «все индивидуальные объекты, значение некого свойства для которых является объектом определенного типа». Например, так можно выделить класс объектов, которые ссылаются каким-либо определенным свойством на объекты класса «Процесс», с целью выделить в модели все сущности, связанные с процессами. В RDF/XML условие может выглядеть так:

<owl:Restriction>
  <owl:onProperty rdf:resource="#usedInProcess" />
  <owl:allValuesFrom rdf:resource="#Process"  />
</owl:Restriction>

Условие owl:allValuesFrom утверждает, что все значения свойства usedInProcess подходящих объектов должны указывать на объекты класса Process. Нюанс здесь состоит в том, что OWL использует концепцию «открытого мира», в соответствии с которой считается, что имеющаяся у нас онтология описывает не всю информацию – чего-то мы можем и не знать. Это напрямую влияет на применение правил логического вывода. Например, то, что все значения свойства usedInProcess определенного объекта относятся к классу Process, само по себе не сделает его членом того класса, для которого описано ограничение: считается, что могут существовать и другие значения свойства usedInProcess, о которых в нашей модели просто ничего не известно. Для того, чтобы вывод был получен, утверждение нужно «закрыть». Для этого нужно описать ограничение на количество значений, которые может принимать свойство usedInProcess. Если будет известно, что оно принимает не более одного значения, это значение установлено, и указывает на объект класса Process – тогда вывод состоится.

Похожим образом описывается условие someValuesFrom, которое утверждает, что хотя бы одно из значений указанного свойства должно попадать в установленный диапазон. Например, таким образом можно описать тот факт, что членами класса «Работник», являющегося подклассом класса «Персоны», являются все объекты, которые имеют значение свойства «работает в», равное какому-либо объекту класса «Организация»:

<owl:Class rdf:about="#Employee">
    <owl:equivalentClass>
        <owl:Restriction>
            <owl:onProperty rdf:resource="#worksIn"/>
            <owl:someValuesFrom rdf:resource="#Organization"/>
        </owl:Restriction>
    </owl:equivalentClass>
    <rdfs:subClassOf rdf:resource="#Person"/>
</owl:Class>

Еще один вариант условия, owl:hasValue, утверждает, что значение свойства должно соответствовать конкретному индивидуальному объекту. Например, так можно выделить в класс все дочерние предприятия определенного холдинга:

<owl:Restriction>
  <owl:onProperty rdf:resource="#hasOwnerOrganization" />
  <owl:hasValue rdf:resource="#AAAHolding" />
</owl:Restriction>

Второй тип ограничений налагается на количество значений, которые может принимать свойство. Например, футбольная команда состоит из 11 игроков; следовательно, можно объявить класс, состоящий из объектов, у которых свойство «имеет игрока» принимает 11 значений – это будет класс футбольных команд. Можно описывать условия на минимальное, максимальное и точное количество значений при помощи терминов owl:minCardinality, owl:maxCardinality и owl:cardinality:

<owl:Restriction>
  <owl:onProperty rdf:resource="#hasParent" />
  <owl:maxCardinality rdf:datatype="&xsd;nonNegativeInteger">2</owl:maxCardinality>
</owl:Restriction>

С этим связан очень важный момент: описание ограничений в онтологии не налагает на самом деле никаких ограничений на то, какие значения могут принимать те или иные атрибуты. Вместо этого, ограничения утверждают, что все объекты, для которых они выполняются, относятся к определенному классу. Например, в случае с указанным выше ограничением, ничто не помешает нам присвоить трех родителей одному объекту. Однако, если мы создадим объект «Дружок», родителем которого объявим объект «Бобик», то с удивлением обнаружим в результатах логического вывода, что «Дружок» является человеком – ведь он имеет не более двух родителей, а ограничение мы налагали именно на класс «Человек».

4. Пересечение двух и более классов. Под «пересечением» понимаются объекты, входящие во все перечисленные классы одновременно (логическое «И»). Например, пересечением классов «Музей» и «Дворец» будут дворцы, эксплуатируемые как музеи – Эрмитаж, Лувр и т.д. Третьяковская галерея и Большой Кремлевский дворец в это пересечение не попадут. В RDF/XML пересечение объявляется так:

<owl:Class>
  <owl:intersectionOf rdf:parseType="Collection">
    <owl:Class rdf:about="#Palace"/>
    <owl:Class rdf:about="#Museum"/>
  </owl:intersectionOf>
</owl:Class>

5. Объединение двух и более классов. Операция объединения соответствует логическому «ИЛИ»: ее результатом будут все индивидуальные объекты, входящие хотя бы в один из объединяемых классов. Так, можно объявить класс домашних животных, как объединение классов кошек и собак:

<owl:Class>
  <owl:unionOf rdf:parseType="Collection">
    <owl:Class rdf:about="#Cat"/>
    <owl:Class rdf:about="#Dog"/>
  </owl:intersectionOf>
</owl:Class>

6. Дополнение описания класса. Эта операция соответствует логическому «НЕ»: ее результатом являются все индивидуальные объекты, не относящиеся к указанному классу. Например, мы можем объявить класс «Объекты» как все, что не относится к «Субъектам»:

<owl:Class>
  <owl:complementOf>
    <owl:Class rdf:about="#Subject"/>
  </owl:complementOf>
</owl:Class>

Состав членов классов, объявленных способами, описанными в п.п. 4-6, может определить только машина логического вывода.

После того, как классы описаны, можно делать логические утверждения о них – аксиомы о классах. Существует три типа таких аксиом:

1. Является подклассом (rdfs:subClassOf) – этот тип утверждения о классе нам уже хорошо знаком. В сочетании с объявлением класса путем наложения ограничения на свойства, он позволяет установить конкретные ограничения на свойства членов тех или иных классов:

<owl:Class rdf:about="#Company">
  <rdfs:subClassOf>
    <owl:Restriction>
      <owl:onProperty rdf:resource="#hasDirector" />
      <owl:cardinality rdf:datatype="&xsd;nonNegativeInteger">1</owl:cardinality>
    </owl:Restriction>
  </rdfs:subClassOf>
</owl:Class>

В этом примере утверждается, что класс «Компания» является подклассом анонимного класса, элементы которого имеют ровно одно значение свойства «имеет Директора». Следовательно, каждая компания должна иметь ровно одного директора. Напомним, что каждый класс может являться подклассом любого количества классов одновременно – это позволяет, в том числе, налагать сразу несколько подобных ограничений.

2. Эквивалентность классов, owl:equivalentClass. Это выражение позволяет констатировать, что два класса с разными идентификаторами имеют в точности совпадающие значения.

3. Разобщенность классов, owl:disjointWith. Такое утверждение констатирует, что два класса не имеют общих членов – например, классы «Собаки» и «Кошки» являются разобщенными.

Перейдем к рассказу о свойствах. Как мы помним, свойства в OWL бывают двух основных типов: свойства-литералы (owl:DatatypeProperty) и свойства-связи (owl:ObjectProperty). И те, и другие являются подклассом свойства вообще: rdf:Property. Кроме них, существуют и дополнительные, служебные виды свойств, такие как owl:AnnotationProperty.

Свойства могут образовывать иерархии точно так же, как классы, при помощи аксиомы rdfs:subPropertyOf. Например, объявление

<owl:ObjectProperty rdf:ID="isMother">
  <rdfs:subPropertyOf rdf:resource="#isParent"/>
</owl:ObjectProperty>

означает, что свойство «является матерью» – специализация свойства «является родителем». Следовательно, по правилам логического вывода, если A является матерью B, то A является и родителем B, но не наоборот.

Интересно, что в OWL DL свойства-литералы и свойства-связи разобщены, а в OWL Full свойства-литералы являются подтипом свойств-связей.

Уже знакомые нам аксиомы rdfs:domain и rdfs:range используются для указания классов, к объектам которых применимо данное свойство, и диапазонов значений свойств. Важно отметить, что указание нескольких domain и range для свойства интерпретируется так, будто между ними стоит операция логического «И», то есть (в случае domain) свойством могут обладать только объекты, относящиеся одновременно ко всем перечисленным классам – эта особенность стандарта тоже не слишком интуитивна. Если требуется выразить тот факт, что свойством может обладать объединение подклассов – необходимо использовать owl:unionOf:

<owl:ObjectProperty rdf:ID="hasBankAccount">
  <rdfs:domain>
    <owl:Class>    
      <owl:unionOf rdf:parseType="Collection">
        <owl:Class rdf:about="#Person"/>
        <owl:Class rdf:about="#Organization"/>
      </owl:unionOf>
    </owl:Class>    
  </rdfs:domain>
</owl:ObjectProperty>

В данном случае утверждается, что владельцем банковского счета могут быть как персоны, так и организации.

Аксиома owl:FunctionalProperty является «глобальным ограничением»: она констатирует, что каждый носитель этого свойства может иметь одно и только одно его значение. Owl:FunctionalProperty является подклассом rdf:Property. Для того, чтобы объявить свойство функциональным, нужно отнести его к этому типу, наряду с основным имеющимся у него типом – ObjectProperty или DatatypeProperty:

<owl:ObjectProperty rdf:ID="husband">
  <rdf:type    rdf:resource="&owl;FunctionalProperty" />
  <rdfs:domain rdf:resource="#Woman" />
  <rdfs:range  rdf:resource="#Man" />
</owl:ObjectProperty>

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

Могут существовать и обратные функциональные свойства, owl:InverseFunctionalProperty – в этом случае на каждый объект может указывать только одна связь данного типа. Например, выражение

<owl:InverseFunctionalProperty rdf:ID="biologicalMotherOf">
  <rdfs:domain rdf:resource="#Woman"/>
  <rdfs:range rdf:resource="#Human"/>
</owl:InverseFunctionalProperty>

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

Свойства могут находиться в определенных отношениях между собой. Выражение owl:equivalentProperties констатирует, что два свойства, обладающие разными идентификаторами, имеют один и тот же смысл. Выражение owl:inverseOf указывает, что одно свойство является обратным другому:

<owl:ObjectProperty rdf:ID="hasChild">
  <owl:inverseOf rdf:resource="#hasParent"/>
</owl:ObjectProperty>

Очевидно, что если A является родителем B, то B является ребенком A. Такие утверждения образуют одно из правил, учитываемых машинами логического вывода.

Наконец, свойства сами по себе могут обладать определенными логическими характеристиками. Свойство может быть транзитивным, owl:TransitiveProperty – это означает, что если A связано неким свойством с B, а B с C, то A связано этим же свойством с C. Такое объявление полезно для автоматического вычисления на описаниях вложенных структур. Например, описывая состав какого-либо изделия, мы будем использовать свойство «является частью» для его декомпозиции до нужного нам уровня. Например, турбину электростанции можно рекурсивно декомпозировать на узлы, компоненты и т.д., однако очевидно, что самый последний болт является частью турбины в целом, а не только ее конкретного узла. Для этого и пригодится определение транзитивного свойства:

<owl:ObjectProperty rdf:ID="isPartOf">
  <rdf:type rdf:resource="&owl;TransitiveProperty"/>
  <rdfs:domain rdf:resource="#Asset"/>
  <rdfs:range  rdf:resource="#Asset"/>
</owl:ObjectProperty>

Впрочем, owl:TransitiveProperty является подтипом owl:ObjectProperty, поэтому объявлять ObjectProperty в явном виде в данном случае не обязательно.

Свойство может быть симметричным: это означает, что если A имеет некую связь с B, то и B имеет такую же связь с A. Например, для отношения «является супругом» запись будет такой:

<owl:SymmetricProperty rdf:ID="hasSpouse">
  <rdfs:domain rdf:resource="#Human"/>
  <rdfs:range  rdf:resource="#Human"/>
</owl:SymmetricProperty>

Рассмотрим особенности OWL, связанные с описанием индивидуальных объектов. Прежде всего отметим, что индивидуальные объекты, как и классы, могут быть анонимными, то есть не обладать уникальными идентификаторами. Это не слишком удобно при работе с ними через SPARQL-запросы, так как подобные сущности представляются пустыми узлами, blank nodes; однако, стандарт допускает такой способ работы.

Другой важный момент связан с тем, что один и тот же объект может иметь несколько разных идентификаторов в модели. По умолчанию считается, что у машины логического вывода нет информации о том, чтобы считать любые два объекта модели эквивалентными или различными. Для явного указания таких фактов используются выражения owl:sameAs, owl:differentFrom и owl:AllDifferent. Как понятно из названия, owl:sameAs констатирует эквивалентность объектов:

<rdf:Description rdf:about="#ЛевНиколаевичТолстой">
  <owl:sameAs rdf:resource="#ЛевТолстой"/>
</rdf:Description>

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

<owl:AllDifferent>
  <owl:distinctMembers rdf:parseType="Collection">
    <Person rdf:about="#KarlMarx"/>
    <Person rdf:about="#FriedrichEngels"/>
  </owl:distinctMembers>
</owl:AllDifferent>

Все описанное нами в этой главе относится к стандарту OWL 1. Стандарт OWL 2, принятый в 2012 году, существенно расширяет его возможности. Прежде всего, он определяет три «профиля», каждый из которых налагает определенные ограничения на возможности языка, с целью достижения приемлемой производительности в тех или иных практических ситуациях. Перечислим профили OWL 2:

1. OWL 2 EL оптимизирован для обработки онтологий с большим числом классов и свойств.

2. OWL 2 QL больше подходит для онтологий с большим количеством индивидуальных объектов.

3. OWL 2 RL применяется в случаях, когда нужно добиться приемлемой производительности без существенной потери выразительности языка.

Перечислим некоторые наиболее интересные функциональные возможности OWL 2.

  • Возможность описывать цепочки свойств, что позволяет, например, описать отношения между тремя объектами сразу, которой так не хватало в OWL 1. Для этого используется выражение ObjectPropertyChain вместе с SubObjectPropertyOf. Например, если ObjectPropertyChain связывает свойства «находится в» и «является частью», и эта цепочка является SubObjectPropertyOf для свойства «находится в», то турбина, являющаяся частью агрегата, находящего в цеху №1, будет считаться тоже находящейся в цеху №1. Еще один классический пример использования цепочек свойств – отношение «является дядей», которое устанавливается при наличии двух отношений между тремя субъектами: A имеет родителя B, B имеет брата C.
  • Возможность объявлять определенные наборы свойств ключевыми (HasKey) – используется для того, чтобы констатировать, что каждый экземпляр какого-либо класса исчерпывающе идентифицируется набором значений своих ключевых свойств.
  • Явное описание не рефлексивных, не симметричных, разделенных свойств (IrreflexiveObjectProperty, AsymmetricObjectProperty, DisjointObjectProperties).
  • Отрицательные утверждения (например, при помощи выражения NegativeObjectPropertyAssertion можно записать аксиому «Россия не расположена в Африке»).

Рассказав о возможностях OWL, мы обязаны упомянуть о его ограничениях.

Одним из важнейших из них считается невозможность выполнять вычисления на основе нечеткой логики. Действительно, мы можем напрямую записать в модели факт «В Петербурге часто идет дождь», но это не позволит автоматически в одних случаях получать вывод о том, что дождь идет, а в других – нет. Однако на уровне ПО, работающего с моделью, никто не мешает сопоставить предикату «часто» конкретную функцию, которая будет выдавать некое распределение, на основании которого система имитационного моделирования будет попеременно выдавать разные варианты погоды в Петербурге. Кроме того, можно задать более формальное описание в самой модели, выразив сведения о том, что вероятность дождя в такие-то периоды имеет определённое значение. Пример подобного использования модели мы про демонстрирует в главе 7.

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

Аналогичным образом, недостатком OWL иногда называют невозможность выражать сведения о субъективной точке зрения: «Иван считает, что пингвины живут в Арктике». Иван, конечно, ошибается, но инструментальных средств для выражения этого в OWL, казалось бы, нет. Однако никто не мешает в модели создать класс объектов «Мнение», который будет связывать субъекта с теми фактами, которые он считает истинными.

Наконец, проблемой может показаться описание исключений. Факт «птицы летают» на первый взгляд кажется истинным, однако куры и пингвины не летают. Это говорит только о том, что утверждение сформулировано неправильно, и его нужно уточнить. Летают не птицы, а только определенные их виды – это и нужно отразить в модели.

Действительно трудноразрешимой проблемой для OWL является представление утверждений о классах. Если «Собака» - класс, то выразить напрямую сведения о том, что все собаки умеют лаять, будет сложно. Для этого потребуется создать класс «Умение», один из объектов которого будет соединять класс Собаки и умение лаять. ПО, использующее модель, сможет интерпретировать эту информацию, но задействовать её в получении логических выводов средствами reasoner'а не получится. Другой вариант решения проблемы – создание правила, которое гласит, что если X – собака, то X умеет лаять. Это сработает, но большими массивами правил трудно управлять, особенно если онтология подвергается рефакторингу.

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