Извлечение сущностей из текста с помощью Stanford NLP с нуля

Данная статья предназначена для тех, кто никогда не работал со Stanford nlp и столкнулся с необходимостью в кратчайшие сроки изучить его и применить.

Данный софт достаточно распространен, и, в частности, наша фирма — БалтИнфоКом — использует эту программу.

Для начала надо понять простую вещь: Stanford NLP работает по принципу аннотирования слов, то есть на каждое слово «навешиваются» одна или более аннотаций, например POS (Part of Speech — часть речи), NER (Named-Entity Recognizing – именованная сущность) и т.д.

Первое, что видит новичок, зайдя на сайт Stanford NLP в раздел "быстрый старт", это следующую конструкцию:

Properties props = new Properties();
props.setProperty("annotators", "tokenize,ssplit,pos,lemma,ner,regexner,parse,depparse,coref");
StanfordCoreNLP pipeline = new StanfordCoreNLP(props);
// create a document object
CoreDocument document = new CoreDocument(text);
// annnotate the document
pipeline.annotate(document);

Здесь StanfordCoreNLP – это конвейер, на вход которому подается наш текст, предварительно упакованный в обьект CoreDocument. StanfordCoreNLP, это самый главный и часто используемый объект во всей структуре, с помощью которого происходит вся основная работа.

Сначала задаем параметры в StanfordCoreNLP и указываем, осуществление каких действий нам нужно. При этом все возможные комбинации этих параметров можно найти на официальном сайте по этой ссылке.

  • tokenize – соответственно разбиение на токены
  • ssplit – разбиение на предложение
  • pos — определение части речи
  • lemma – добавление к каждому слову его начальной формы
  • ner — определение именованных сущностей, таких как «Организация», «Лицо» и т.д.
  • regexner — определение именованных сущностей посредством регулярных выражений
  • parse — разбор каждого слова по семантике (род, число и так далее)
  • depparse — разбор синтаксических зависимостей между словами в предложении
  • coref- поиск упоминания одной и той же именованной сущности в тексте, например «Мария» и «она»

Вот пример того, как аннотаторы (parse и depparse) работают вместе:

image

Если Вам непонятны аннотации над токенами, то на этих сайтах вы найдете их значения: значения связей в предложениях, значения частей речи.

По каждому из этих параметров можно найти дополнительные флаги для более тонкой настройки здесь в разделе «Аnnоtаtоrs».

Данные конструкции задаются, если Вы хотите использовать встроенные модели Stanford NLP, но Вы также можете их задать вручную с помощью метода addAnnotator(Annotator…) или через пополнение параметров перед созданием объекта StanfordCoreNLP.

Теперь о том, какими способами можно извлечь именованные сущности из текста. Для этого в Stanford NLP есть три встроенных класса основанных на регулярных выражениях и один класс, предназначенный для разметки токенов через модель.

Классы, основанные на регулярных выражениях:

  • TokensRegexAnnotator – аннотатор, работающий по правилам – SequenceMatchRules.
    Рассмотрим пример маппинга для него, построенного на данных правилах.

    ner = { type: "CLASS", value:
    "edu.stanford.nlp.ling.CoreAnnotations$NamedEntityTagAnnotation" }
    
    $EMAIL = "/.*([A-z0-9А-я]+?)(@)([A-z0-9А-я]+?).*/"
    
    {
    ruleType: "tokens",
    pattern: (([]) ($EMAIL)),
    action: (Annotate($0, ner, "MAIL")),
    priority:0
    } 

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

    После этого создаем блок, в котором задаем тип правил. Затем шаблон для сравнения (в нашем случае мы говорим, что нам нужен «[]» — любой токен, после которого идет наша переменная «$EMAIL». После этого задаем действие, в нашем случае мы хотим проаннотировать токен.

    Обратите внимание, в примере специально «[]» и «$EMAIL» заключены в круглые скобки, потому что $0 указывает, какую группу захвата мы хотим выделить из найденного шаблона, при этом под группой захвата подразумевается группа, заключенная в круглые скобки. Если задать 0, то во фразе «почта sobaka@mail.ru» все токены будут проаннотированы как «MAIL». Если задать 1(то есть первая группа захвата), то только слово «почта» будет проаннотировано; если 2, то только «sobaka@mail.ru».

    Для ситуаций, когда по двум правилам один и тот же токен может быть определен по-разному, можно выставить приоритет правила относительно другого. Например, в случае следующей фразы — «House 25 $», могут быть два противоречащих правила, по одному из которых число 25 будет определено как номер дома, а по второму – как его стоимость.
  • RegexNERAnnotator – этот аннотатор работает, используя классификатор RegexNERSequenceClassifier.

    Маппинг для него выглядит следующим образом

    regex1	TYPE	overwritableType1,Type2...	priority

    Здесь regex1 – регулярное выражение в формате TokenSequencePattern.

    TYPE – имя именованной сущности.
    overwritableType1,Type2… – типы, которые мы можем заменить, в случаях спорной ситуации.
    Priority – приоритет для спорных ситуаций, описанных выше.
    Обратите внимание, что в данном маппинге все колонки должны быть разделены табом.
  • TokensRegexNERAnnotator
    Данный аннотатор отличается от предыдущего тем, что использует библиотеку TokensRegex для регулярных выражений, ту же, что и первый аннотатор, что позволяет использовать более гибкие правила для сопоставления; а также тем, что может записывать значения тегов, отличных от тега NER.
    Маппинг для него составляется по правилам RegexNERAnnotator

Разметка текста через модель с помощью NERClassifierCombiner
Для того, чтобы воспользоваться этим классом, надо сначала иметь или обучить свою модель.

О том, как это сделать можно найти здесь;
После того, как Вы обучили модель, остается только создать NERClassifierCombiner, указав в нем путь к модели, и вызвать метод classify.

NERClassifierCombiner classifier = new NERClassifierCombiner(false, false, serialized_model);
String text = "Some lucky people working in BaltInfoCom Org.";
List<List<CoreLabel>> out = classifier.classify(text);

Полный список аннотаторов можно посмотреть здесь.

В дополнение к вышесказанному, если Вам требуется использовать Stanford NLP для русского языка, то могу посоветовать зайти сюда. Здесь есть модели для определения частей речи (pos-tagger) и для выявления связей в предложении (dependency parser).

Виды тэггеров, представленных там:
russian-ud-pos.tagger — просто тэггер,
russian-ud-mfmini.tagger — с основным списком morphological features,
russian-ud-mf.tagger — с полным списком morphological features, пример маппинга для которого можно посмотреть здесь.
Поделиться публикацией

Комментарии 6

    0
    А как оно вам по сравнению скажем с OpenNLP?

    И еще: зашел про ссылке «сюда», где про русский — оказалось что это клон оригинального репозитория, куда включена поддержка русского. При этом текущая версия 3.9.1, а версия клона только 3.7.0. Что прискорбно.
      0
      Так как конкретно моя задача заключалась в том, чтобы анализировать новостные ленты, то могу сказать, что Stanford показал себя лучше при извлечении именованных сущностей на таких текстах. И, лично мне, Stanford представился, чисто интуитивно, более понятным (в частности, при составлении правил)

      Да, это верно, русская поддержка Stanford-а это клон, но, на текущий момент, отличие в версии 3.9.1 и 3.7.0 в том, что появилась поддержка Китайского и Испанского через встроенные модели, а в версии 3.8 появилась поддержка более ранних версий. Принципиальных же изменений в основе Stanford-а пока что нет, так что модели можно все равно использовать с таким же успехом.
        0
        Про поддержку более ранних версий в 3.8 немного недопонял. Но в целом да, я посмотрел release notes, и на первый взгляд, после 3.7.0 ничего особо нужного не обнаружил. Так что будем пробовать на 3.7 наверное.
          0
          Пардон, да, все в порядке и поддержек старых версий никаких не добавилось.
      0
      Спасибо за статью! Что-нибудь еще пробовали для обработки текстов? Например, есть от Яндекса наработки.
        0
        Да, пожалуйста! У Яндекса есть Томита-парсер. О нем могу сказать, что по сравнению со Stanford NLP, правила составления грамматик не слишком очевидные, по крайней мере лично мне, за один присест (за один вечер) не удалось полностью разобраться как они работают. К тому же, насколько я знаю, самые лучшие свои наработки в Томита-парсере Яндекс оставил себе и не включил в общедоступный релиз.

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