Онтоинженер: робота з поняттями

Привіт, Хабр! Мене звуть Даня, і я працюю в групі вилучення знань ДогадайтесьКакойКомпании. У двох постах я розповім,
  • як ми витягуємо факти і сутності з текстів,
  • хто такі онтоинженеры,
  • навіщо вони відокремлюють трупи від кісток,
  • причому тут Лев Толстой.
На Хабре вже було кілька публікацій, присвячених отримання інформації з неструктурованого тексту (багато чого шукається за тегами Text Mining, Information Extraction). Ось тут, наприклад, наведено короткий джентльменський набір того, що бажано зробити з текстом, перш ніж з нього буде зручно що-небудь отримати (спойлер: ми все це теж робимо). А ось тут колеги з Яндекса описують свій підхід з використанням КС-граматик (спойлер номер два: там теж замішаний Толстой). Загалом, тема для Хабра не нова, але і не можна сказати, що достатньо розкрита. Тому ми і вирішили поділитися нашим досвідом.

Ми в ABBYY любимо підходити до проблем фундаментально і придумувати універсальні, довгострокові, масштабовані рішення. Ось і тут, коли виникла задача отримувати інформацію з тексту, ми не стали ліпити нічого на коліні скриптів і регекспов, а застосували важку лінгвістичну артилерію — технологію ABBYY Compreno.

Чим далі в ліс: погані програмісти і хороші традиції
Парсер ABBYY Compreno перетворює текст в ліс з дерев, які поєднують у собі властивості граматики залежності (dependency grammar, її використовує знайомий багатьомстенфордський парсер) і граматики складових (constituency grammar). Не буду вдаватися в подробиці і йти в нетрі теорії синтаксису — тут достатньо розуміти, що вузли в деревах приблизно відповідають словам пропозиції, а дуги відображають залежності між ними. При цьому вузли забезпечені величезною кількістю супутньої лінгвістичної інформації. Ось так виглядає дерево для фрази Програміст написав поганий код.



А тепер порівняйте його з деревом для фрази Програміст ввів неправильний код



Зелений текст капсом — це семантичні класи, які вибираються для кожного слова в нашій універсальної семантичної ієрархії. Семантична ієрархія — це величезне дерево понять, організоване за принципом успадкування лінгвістичної інформації і охоплює всі частини мови; крім того, в нього закладена наша синтаксична модель. Семантичні класи в ієрархії традиційно називаються по-англійськи і відповідають одному конкретному значенню слова, деякого не зав'язаному на конкретний мову поняттю. Таким чином, в момент вибору семантичного класу дозволяється лексична неоднозначність. Приклад такого вибору видно на слові код, для якого у першому реченні обраний СК «CODE_OF_PROGRAM» (код комп'ютерної програми), а в другому — СК «CODE» (код як шифр, пароль). На рішення системи в таких випадках впливає як статистика, так і обмеження лінгвістичної структури, задані всередині нашої семантичної ієрархії.



Так виглядають фрагменти ієрархії, в яких перебувають згадані класи

А ось ще один приклад вирішення неоднозначності Програміст ввів традицію кодити без багів. Тут, як ми бачимо, обраний зовсім інший семантичний клас для словоформи ввів, оскільки слово використовується зовсім в іншому значенні.



Синім в деревах відображаються позиції вузлівповерхневому синтаксис — це формальна структура пропозиції, що не має прямого відношення до його змістом. Це дуже близько до шкільної моделі з підметом, присудком, додатком і т. п. Червоним кольором вказані глибинні позиції — вони вже показують, яку роль відіграє елемент у описуваної ситуації. Тому якщо перевести перший приклад про програміста в пасив — написаний Програмістом поганий код — в дереві зміниться поверхневий Subject (т. к. підлягає там стає вже не програміст, код), але не глибинний Agent (т. к. в реальності, яку описує фраза, дія, як і раніше, здійснює програміст):



Крім семантико-синтаксичних дерев парсер ABBYY Compreno повертає інформацію про недеревних зв'язки між вузлами. Наприклад, для фрази Вася сидів за комп'ютером і кодил для дієслова «кодити» буде відновлений нульовою суб'єкт, який буде пов'язаний недеревної зв'язком з вузлом «Вася»:



Те ж саме відбудеться і в реченні Вася пішов писати код — з точки зору граматичної структури Вася активно виконує тільки дія, що позначається дієсловом «пішов», але ми-то знаємо, хто пише код:



Не вдасться Васі і приховати свої темні справи, сховавшись за займенником. В прикладах типу "Вася пішов.", "Він пішов кодити" або "Васі подобається його код" Compreno відновить анафорическую зв'язок і замінить займенники ("", "") на Васю:







Створення світу
Докладніше про те, як працює парсер ABBYY Compreno, можна почитати тут. Я ж перейду безпосередньо до теми поста і почну розповідати, як організовано витяг фактів і сутностей на базі цієї лінгвістичної платформи. Тут-то і вступають у справу онтоинженеры — фахівці зі створення формальних моделей світу (онтологій) і отримання інформації, відповідної цим моделям. Завдання онтоинженера — спочатку придумати формальне представлення деяких інформаційних об'єктів, які потрібно витягувати, а потім розробити систему правил, за якими вони будуть вилучатись з лісу Compreno-дерев.

Перший етап роботи — розробку онтологічної моделі можна порівняти із створенням класів в ООП. Наприклад, якщо перед онтоинженером поставлено завдання добувати з тексту персон, необхідно створити відповідний концепт (це слово в онтологічному моделюванні використовується як еквівалент класу), вбудувати його в онтологію, тобто встановити спадкування з іншими концептами, і створити необхідні атрибути — ім'я, прізвище і т. п. Для цього ми використовуємо мова OWL — стандарт опису онтологій, підтримуваний консорціумом W3С. Персона в нашій онтології буде виглядати так:

<owl:Class rdf:about="http://www.abbyy.com/ns/BasicEntity#Person">
<rdfs:subClassOf rdf:resource="http://www.abbyy.com/ns/Basic#HumanLikeSubject"/>
<rdfs:subClassOf rdf:resource="http://www.abbyy.com/ns/Basic#BasicEntity"/>
<rdfs:subClassOf rdf:resource="http://www.abbyy.com/ns/Basic#IdentifiableThing"/>
<rdfs:subClassOf rdf:resource="http://www.abbyy.com/ns/Basic#EntityLikeSubject"/>
<Aux:LeadingParent rdf:resource="http://www.abbyy.com/ns/Basic#BasicEntity"/>
<rdfs:label xml:lang="En">Person</rdfs:label>
<rdfs:label xml:lang="Ru">Персона</rdfs:label>
<rdfs:comment xml:lang="En">For example: Mikhail Yuryevich Lermontov was born on October 15, 1814 in Moscow.</rdfs:comment>
<rdfs:comment xml:lang="Ru">Наприклад: Михайло Юрійович Лермонтов народився 15 жовтня 1814 року в Москві.</rdfs:comment>
</owl:Class>

А так — її атрибут «Прізвище»:

<owl:DataProperty rdf:about="http://www.abbyy.com/ns/BasicEntity#surname">
<rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#string"/>
<rdfs:domain rdf:resource="http://www.abbyy.com/ns/BasicEntity#Person"/>
<rdfs:label xml:lang="En">Surname</rdfs:label>
<rdfs:label xml:lang="Ru">Прізвище</rdfs:label>
<rdfs:comment xml:lang="En">Mikhail Yuryevich Lermontov: Surname - Lermontov</rdfs:comment>
<rdfs:comment xml:lang="Ru">Михайло Юрійович Лермонтов: Прізвище - Лермонтов</rdfs:comment>
</owl:DataProperty>

Концепт, для якого задається атрибут, називається областю визначення (domain) цього атрибута. В даному випадку область визначення — об'єкти, що належать концепту Person. Тип даних, які можуть заповнювати атрибут, задається в області значення (range). В даному випадку це рядок.
Для зручності роботи онтоинженеров створена спеціальна графічна середовище розробки онтологій:



Гуртки позначають концепти; гуртки, з'єднані лініями — атрибути відносини концептів,
В залежності від концепту заповнювачами його атрибутів можуть бути як прості типи даних (рядок, число, логічне значення), так і об'єкти, що відносяться до інших концептів. Особливо це актуально для фактів, у яких учасниками виступають витягнуті іншими правилами сутності. Майже всі факти з декількома можливими учасниками ми моделюємо не як відносини, записувані всередину сутностей, а як окремі інформаційні об'єкти, аналогічні сутностей (до речі, це теж рекомендація W3C). Так виглядає в OWL-запису наш факт купівлі-продажу (Purchase And Sale):

<owl:Class rdf:about="http://www.abbyy.com/ns/BasicFact#PurchaseAndSale">
<rdfs:subClassOf rdf:resource="http://www.abbyy.com/ns/BasicFact#OperationWithProperty"/>
<rdfs:label xml:lang="En">Purchase And Sale</rdfs:label>
<rdfs:label xml:lang="Ru">Купівля-продаж</rdfs:label>
</owl:Class>

А так — його відносини:

<owl:ObjectProperty rdf:about="http://www.abbyy.com/ns/BasicFact#seller">
<rdfs:range rdf:resource="http://www.abbyy.com/ns/Basic#Subject"/>
<rdfs:domain rdf:resource="http://www.abbyy.com/ns/BasicFact#PurchaseAndSale"/>
<rdfs:label xml:lang="En">Seller</rdfs:label>
<rdfs:label xml:lang="Ru">Продавець</rdfs:label>
</owl:ObjectProperty> 

<owl:ObjectProperty rdf:about="http://www.abbyy.com/ns/BasicFact#customer">
<rdfs:range rdf:resource="http://www.abbyy.com/ns/Basic#Subject"/>
<rdfs:domain rdf:resource="http://www.abbyy.com/ns/BasicFact#PurchaseAndSale"/>
<rdfs:label xml:lang="En">Customer</rdfs:label>
<rdfs:label xml:lang="Ru">Покупець</rdfs:label>
</owl:ObjectProperty>

<owl:ObjectProperty rdf:about="http://www.abbyy.com/ns/BasicFact#price">
<rdfs:range rdf:resource="http://www.abbyy.com/ns/BasicEntity#Money"/>
<rdfs:domain rdf:resource="http://www.abbyy.com/ns/BasicFact#PurchaseAndSale"/>
<rdfs:label xml:lang="En">Price</rdfs:label>
<rdfs:label xml:lang="Ru">Ціна</rdfs:label>
</owl:ObjectProperty>

<owl:ObjectProperty rdf:about="http://www.abbyy.com/ns/BasicFact#property">
<rdfs:range rdf:resource="http://www.abbyy.com/ns/Basic#ExtendedProfit"/>
<rdfs:domain rdf:resource="http://www.abbyy.com/ns/BasicFact#OperationWithProperty"/>
<rdfs:label xml:lang="En">Property</rdfs:label>
<rdfs:label xml:lang="Ru">Майно</rdfs:label>
</owl:ObjectProperty>

Як видно по області значення (range), всі ці відносини заповнюються не простими типами даних, а посиланнями на об'єкти інших концептів. Наприклад, ставлення price може заповнюватися лише «грошовими» сутностями, т. е. об'єктами концепту BasicEntity#Money або його нащадків.

Про те, як це працює і яким чином одні об'єкти потрапляють у відношення до іншим, читайте в наступній статті.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.