InterSystems iKnow. Часть вторая. Создание простого домена

  • Tutorial
Это продолжение моего рассказа про Natural Language Processing технологию Intersystems iKnow, начало здесь. Во второй части вы найдете описание практической работы с iKnow. Мы создадим домен, настроим его, загрузим текст. Затем, посмотрим и проанализируем результаты. Подробнее об этом под катом…


Начнем с создания домена. Домен в iKnow можно сравнить с областью в Caché или с почтовым ящиком в подъезде вашего дома. Это некоторый контейнер, куда загружаются тексты. Кроме текстов там хранится инструментарий, необходимый для их анализа, например, конфигурации, лоадеры, листеры, словари и т.д.
Существует два способа создания доменов. Один из них – использование класса %iKnow.Domain. При использовании этого подхода необходимо вручную писать код создания как самого домена, так и всех объектов внутри него. Этот процесс достаточно сложен, требует времени и опыта работы с iKnow, однако, позволяет реализовывать сложные iKnow-приложения с постпроцессингом проиндексированных данных.
Есть альтернативный способ, основанный на использовании класса %iKnow.DomainDefinition, который подходит для быстрого прототипирования. Он позволяет создать домен декларативным путем через описание его структуры в XML представлении. А сам объект домена создается автоматически при компиляции класса. Данный метод проще, компактней, позволяет быстрее сформировать новый домен с нуля. В этой статье я опишу второй способ работы и приведу примеры кода.
Замечание. Я создаю и тестирую код в версии Caché 2015.2 Field Test. Принципиальное отличие от предыдущих версий заключается в поддержке стемминга и лемматизации. В связи с этим будет встречаться отличие в ряде настроек, но об этом позже. Итак, приступим.

Шаг нулевой. Постановка задачи

Прежде чем начать писать код, я сформулирую задачу, которую буду решать. Мы напишем простейший агрегатор новостей. Для этого создадим домен в iKnow, который будет загружать статьи из RSS лент, а затем научим его разделять эти статьи по темам. Категории новостей предлагаю взять такие: “политика”, “экономика”, “спорт” и, к примеру, “угроза из космоса”.

Шаг первый. Создание домена

Создаем домен с помощью DomainDefinition. Для этого достаточно скомпилировать такой класс:

Class HabrDomain.News Extends %iKnow.DomainDefinition
{
   XData Domain {XMLNamespace =TEST]
   {
      <domain name="NewsAggregator" >
      </domain>
   }
}


Хочу отметить, что сам домен совершенно пустой, и создается как объект сразу после компиляции класса HabrDomain.News. Для того, чтобы в этом убедиться запустите команду
do $system.iKnow.ListDomains()
в терминале. Вы увидите, что сформирован домен NewsAggregator, c ID равным 1 (ID может отличаться, если вы уже создавали домены), без загруженных текстов (# of sources равно 0).

Шаг второй. Настройка домена

Под настройкой домена можно понимать весьма широкий круг действий, но сейчас речь пойдет о конфигурации домена. Конфигурация используется только при загрузке документов в домен и отвечает за то, как iKnow будет обрабатывать текст. Конфигурация создается как объект, поэтому её можно однажды создать, а потом многократно повторно использовать для разных доменов данной области. Теоретически, конфигурация не является обязательной, и все настройки могут быть заменены некими значениями “по умолчанию”, но, в этом случае, про работу с русскими текстами лучше сразу забыть.
Для описания конфигурации в DomainDefinition добавляем строку внутри тэгов Domain:

<configuration name="Russian" detectLanguage="false" languages="ru" stemming="DEFAULT" />


Согласно этой строке, мы создали конфигурацию с именем “Russian”, которая будет использовать для анализа текстов семантическую модель русского языка, при этом механизмы автоматического определения языка текстовых документов будут отключены. Параметр “stemming” со значением “DEFAULT” является обязательным (но не достаточным) условием для того, чтобы при анализе текста подключилась русская лемматизация.
Для завершения настройки стемминга добавим следом за конфигурацией еще одну строку:

<parameter name="$$$IKPSTEMMING" value="1" />


Шаг третий. Создание полей для метаданных

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

<metadata>
   <field name="PubDate" dataType="DATE" />
   <field name="Title" dataType="STRING" />
   <field name="Link" dataType="STRING" />
   <field name="Agency" dataType="STRING" />
   <field name="Country" dataType="STRING" />
</metadata>


Таким образом, мы описали 5 полей. PubDate будет хранить дату публикации статьи, Title – её заголовок, Link – ссылку на полный текст. В поле Agency мы будем загружать имя ресурса, из которого мы загрузили статью, а в Country – территориальную принадлежность источника.

Шаг четвертый. Описываем источники, откуда будем загружать статьи

При постановке задачи мы договорились, что тексты будут загружаться из RSS-лент. В качестве примера я возьму ленту http://static.feed.rbc.ru/rbc/internal/rss.rbc.ru/rbc.ru/mainnews.rss rbc.ru, где публикуются новости из всех разделов. Чтобы указать iKnow работать с этим ресурсом, добавим код:

<data>
<rss serverName="static.feed.rbc.ru" url="/rbc/internal/rss.rbc.ru/rbc.ru/mainnews.rss" textElements="title,description" >
   <converter converterClass="%iKnow.Source.Converter.Html" />
   <metadataValue field="Agency" value="RBC" />
   <metadataValue field="Country" value="Russia" />
</rss>
</data>


Теперь подробнее опишу поля данной записи. serverName – это имя сервера и первая часть ссылки на RSS-ленту, заканчивающаяся именем домена верхнего уровня (в нашем случае .ru). Вторая часть ссылки прописывается в параметр url. Обратите внимание, что начинается url всегда с “/”. Из каждой публикации мы будем загружать два текстовых поля – заголовок и текст (под текстом я понимаю тело статьи, которая публикуется в ленте; чаще всего это краткое вступление, а не полноценный материал)
Далее конвертер. Он в нашем случае стандартный %iKnow.Source.Converter.Html, а его назначение – удалить из загружаемого текста все html теги, чтобы получить чистый текст.
И, наконец, описываем загрузку метаданных. Чуть выше мы создали 5 полей, три из которых iKnow заполняет автоматически, это – дата публикации, заголовок и ссылка на полный текст статьи. Два оставшихся поля будут заполняться отсюда. В поле “Agency” будет писаться “RBC”, а в “Country” – “Russia”.

Шаг пятый. Словари

Одним из преимуществ технологии iKnow является то, что для базового анализа текста не используются словари, а используется компактная и быстрая семантическая языковая модель. Но есть ряд задач, в которых словари нам все-таки понадобятся. Одна из них – matching – отнесение статей к темам (например, статьи о спорте, о политике, об экономике или угрозе инопланетного вторжения). Иными словами, при описании домена мы можем задать термины, при упоминании которых в тексте статья будет отнесена к той или иной категории. Добавляем в класс такой код:

<matching>
   <dictionary name="Sport">
      <item name="Футбол" uri=":sport:футбол" >
         <term string="футбол" />
         <term string="пенальти" />
         <term string="угловой удар" />
         <term string="штрафной удар" />
      </item>
      <item name="Хоккей" uri=":sport:хоккей" >
         <term string="хоккей" />
         <term string="шайба" />
      </item>
      <item name="Баскетбол" uri=":sport:баскетбол" >
         <term string="баскетбол" />
      </item>
      <item name="Турнир" uri=":sport:турнир" >
         <term string="чемпионат" />
         <term string="соревнование" />
         <term string="гран-при" />
         <term string="кубок" />
         <term string="чемпион" />
      </item>
   </dictionary>
   <dictionary name="Политика">
      <item name="Политика" uri=":politics:политика" >
         <term string="президент" />
         <term string="премьер" />
         <term string="депутат" />
         <term string="парламент" />
         <term string="законодательство" />
      </item>
   </dictionary>
   <dictionary name="Экономика">
      <item name="Экономика" uri=":Economy:экономика">
         <term string="экономика" />
         <term string="бюджет" />
         <term string="рубль" />
         <term string="доллар" />
         <term string="евро" />
         <term string="нефть" />
      </item>
   </dictionary>
   <dictionary name="Инопланетное вторжение">
      <item name="Инопланетное вторжение" uri=":ThreatFromSpace:инопланетноеВторжение">
         <term string="инопланетянин" />
         <term string="гуманоид" />
         <term string="бластер" />
         <term string="имперский флот" />
         <term string="чубакка" />
      </item>
   </dictionary>
</matching>


В разделе matching содержится набор словарей . Каждый словарь описывает свою категорию, термины в которой разделены на предметы (подкатегории) и термины . Целью этой статьи является простая демонстрация возможностей и механизмов iKnow, в то время как для серьезной задачи словари должны также быть серьезными и весьма объемными.

Шаг шестой. Запуск
Теперь наш домен полностью описан.
полный текст класса:
Class HabrDomain.News Extends %iKnow.DomainDefinition
{

   XData Domain [ XMLNamespace = TEST ]
   {
      <domain name="NewsAggregator" >
      <configuration name="Russian" languages="ru" stemming="DEFAULT" />
      <parameter name="$$$IKPSTEMMING" value="1" />

      <metadata>
         <field name="PubDate" dataType="DATE" />
         <field name="Title" dataType="STRING" />
         <field name="Link" dataType="STRING" />
         <field name="Agency" dataType="STRING" />
         <field name="Country" dataType="STRING" />
      </metadata>

      <data>
       <rss serverName="static.feed.rbc.ru" url="/rbc/internal/rss.rbc.ru/rbc.ru/mainnews.rss" textElements="title,description" >
         <converter converterClass="%iKnow.Source.Converter.Html" />
         <metadataValue field="Agency" value="RBC" />
         <metadataValue field="Country" value="Russia" />
      </rss>
</data>

   <matching>
      <dictionary name="Sport">
         <item name="Футбол" uri=":sport:футбол" >
            <term string="футбол" />
            <term string="пенальти" />
            <term string="угловой удар" />
            <term string="штрафной удар" />
         </item>
         <item name="Хоккей" uri=":sport:хоккей" >
            <term string="хоккей" />
            <term string="шайба" />
         </item>
         <item name="Баскетбол" uri=":sport:баскетбол" >
            <term string="баскетбол" />
         </item>
         <item name="Турнир" uri=":sport:турнир" >
            <term string="чемпионат" />
            <term string="соревнование" />
            <term string="гран-при" />
            <term string="кубок" />
            <term string="чемпион" />
         </item>
      </dictionary>
      <dictionary name="Политика">
         <item name="Политика" uri=":politics:политика" >
            <term string="президент" />
            <term string="премьер" />
            <term string="депутат" />
            <term string="парламент" />
            <term string="законодательство" />
         </item>
      </dictionary>
      <dictionary name="Экономика">
         <item name="Экономика" uri=":Economy:экономика">
            <term string="экономика" />
            <term string="бюджет" />
            <term string="рубль" />
            <term string="доллар" />
            <term string="евро" />
            <term string="нефть" />
         </item>
      </dictionary>
      <dictionary name="Инопланетное вторжение">
         <item name="Инопланетное вторжение" uri=":ThreatFromSpace:инопланетноеВторжение">
            <term string="инопланетянин" />
            <term string="гуманоид" />
            <term string="бластер" />
            <term string="имперский флот" />
            <term string="чубакка" />
         </item>
      </dictionary>
   </matching>

   </domain>
   }

   ClassMethod DeleteDomain(DomainName As %String) As %Status
   {
   	set tSC = ##class(%iKnow.Domain).%OpenId(..%GetDomainId()).DropData(1, 1, 1, 1, 1)
	quit:$$$ISERR(tSC) tSC
	quit ##class(%iKnow.Domain).%DeleteId(..%GetDomainId())
   }

}




Несколько слов про метод DeleteDomain, который я добавил к коду. Созданный домен существует как объект класса %iKnowDomain, но удалить его можно только внутренними методами класса HabrDomain.News, поскольку именно он управляет доменом.
Наконец, мы можем запустить расчет.
do ##class(HabrDomain.News).%Build()
В результате в созданный при компиляции домен NewsAggregator будут добавлены статьи из указанных нами источников. Кроме того данные будут проанализированы на предмет вхождения в них маркеров из словаря Matching.

Шаг седьмой. Просмотр результатов

Для просмотра результатов удобнее всего воспользоваться одним из существующих UI, например Knowledge Portal, Indexing Results и Matching Results.

image
Рисунок 1. Knowledge Portal.

Knowledge Portal позволяет провести первичный анализ результатов работы iKnow. Здесь можно выбрать любой из созданных доменов, в нашем случае это NewsAggregator. Таблица “Top concepts” показывает частоту упоминания тех или иных концептов, при этом frequency – это число упоминаний концепта, а spread – число статей, в которых концепт присутствует. Если мы выберем любой концепт в этой таблице (сейчас выбран концепт “России”, Рисунок 1), то обновится содержимое таблиц “Similar Entities”, “Related Concepts”, “Paths”, “Sources”.
Таблица “Similar Concepts” выводит похожие концепты. В нашем случае похожими будут те концепты, где встречается слово “Россия”, но будут присутствовать дополнительные термины (например “посол Сербии в России”). Таблица “Related Concepts”, в нашем случае она получилось пустой из-за малого количества загруженных статей, будет содержать список концептов, которые наиболее часто упоминаются связанными с выбранным. Ниже в примере такие концепты выделены курсивом.
  • [Инфляция] в [России]
  • [дефицит торгового баланса евросоюза] с [россией]
  • [россия] оставляет [право] на [ответные меры]
Еще одной очень интересной таблицей является “Sources”. Отсюда можно открывать просмотр текста статьи, результаты индексирования и категоризации. С просмотром текста всё весьма просто. Единственный параметр, который можно выбирать в диалоговом окне – это количество выводимых предложений. Так, например, если мы установим 1, то iKnow покажет единственное, наиболее важное, по её мнению, предложение в статье.

image
Рисунок 2. Indexing Results.

Окно Indexing Results позволяет анализировать результаты индексирования. Здесь цветом выделяются концепты, подчеркнутым курсивом – связи, и серым курсивом – незначащие слова. Как правило, данное окно используется для проверки правильности настроек домена по результатам индексирования, но также оно очень удобно для чтения текстов статей (например, при составлении словарей).

image
Рисунок 3. Matching Results.

Наконец, третье доступное окно – Matching Results. Здесь можно видеть результаты категоризации статей по словарям, которые мы добавляли в описание домена. Выделенный рыжим цветом концепт в тексте означает, что он в точности соответствует термину из словаря. Если рыжим выделено только обрамление концепта – он является похожим на словарный.
Пришло время подвести итог. Мы научились создавать простейший агрегатор новостей. Для этого с помощью класса %iKnow.DomainDefinition был сформирован домен. В этом домене была создана конфигурация, поддерживающая русский язык и инструмент лемматизации. Были добавлены источники в виде RSS лент. И, наконец, мы создали словари для категоризации. После этого запустили построение домена и, с помощью стандартного UI, проанализировали результаты.
В статье показан пример создания домена iKnow на примере анализа новостей из RSS ленты. Для создания домена использовался класс %iKnow.DomainDefinition. Создана конфигурация домена с поддержкой русского языка и лемматизации, добавлен источник RSS ленты, создан простейший словарь для категоризации статей.
Класс DomainDefinition прекрасно подходит для быстрого создания доменов и прототипирования с использованием iKnow. В реальных приложениях словари терминов для категоризации и сантимент-анализа насчитывают сотни, а то и тысячи слов. Для таких проектов используется класс %iKnow.Domain, который позволяет выполнять также другие интересные задачи. Об этом речь пойдет в моей следующей статье.
InterSystems 63,91
Вендор: СУБД Caché, OLAP DeepSee, шина Ensemble
Поделиться публикацией
Похожие публикации
Комментарии 0

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

Самое читаемое