Распознавание именованных сущностей с регулярным выражением: NLTK

Я играл с инструментарием NLTK. Я часто сталкивался с этой проблемой и искал решение в Интернете, но нигде не получил удовлетворительного ответа. Поэтому я размещаю свой запрос здесь.

Много раз NER не помечает последовательные NNP как один NE. Я думаю, что редактирование NER для использования RegexpTagger также может улучшить NER.

Пример:

Входные данные:

Барак Обама замечательный человек.

Выход:

Tree ('S', [Tree ('PERSON', [('Barack', 'NNP')]), Tree ('ORGANIZATION', [('Obama', 'NNP')]), ('is', 'VBZ'), ('a', 'DT'), ('great', 'JJ'), ('person', 'NN'), ('.', '.')])

в то время как

вход:

Бывший вице-президент Дик Чейни сказал консервативной радиоведущей Лоре Ингрэхэм, что он "удостоился чести" сравниваться с Дартом Вейдером, находясь на своем посту.

Выход:

Tree ('S', [('Former', 'JJ'), ('Vice', 'NNP'), ('President', 'NNP'), Tree ('NE', [('Dick', ' NNP '), (' Чейни ',' NNP ')]), ("сказали", "VBD"), ("консервативный", "JJ"), ("радио", "NN"), ("хост"), "NN"), Tree ("NE", [("Laura", "NNP"), ("Ingraham", "NNP")]), ("that", "IN"), ("он", 'PRP'), ('', ''), (' was ',' VBD '), (' honored ',' VBN '), ("' '", "' '"), (' to ',' TO '), (' be ', 'VB'), ('сравниваемые', 'VBN'), ('to', 'TO'), Tree('NE', [('Darth', 'NNP'), ('Vader', 'NNP')]), ('while', 'IN'), ('in', 'IN'), ('office', 'NN'), ('.', '.')])

Здесь вице /NNP, президент /NNP, (Дик /NNP, Чейни /NNP), правильно извлечен.

Так что я думаю, что если сначала используется nltk.ne_chunk, а затем, если два последовательных дерева являются NNP, есть большие шансы, что оба ссылаются на одну сущность.

Любое предложение будет по достоинству оценено. Я ищу недостатки в моем подходе.

Благодарю.

3 ответа

Решение
from nltk import ne_chunk, pos_tag, word_tokenize
from nltk.tree import Tree

def get_continuous_chunks(text):
    chunked = ne_chunk(pos_tag(word_tokenize(text)))
    prev = None
    continuous_chunk = []
    current_chunk = []

    for i in chunked:
        if type(i) == Tree:
            current_chunk.append(" ".join([token for token, pos in i.leaves()]))
        elif current_chunk:
            named_entity = " ".join(current_chunk)
            if named_entity not in continuous_chunk:
                continuous_chunk.append(named_entity)
                current_chunk = []
        else:
            continue

    if continuous_chunk:
        named_entity = " ".join(current_chunk)
        if named_entity not in continuous_chunk:
            continuous_chunk.append(named_entity)

    return continuous_chunk

txt = "Barack Obama is a great person." 
print get_continuous_chunks(txt)

[из]:

['Barack Obama']

Но учтите, что если предполагается, что непрерывный блок не будет одним NE, то вы бы объединили несколько NE в один. Я не могу думать о таком примере с моей головы, но я уверен, что это произойдет. Но если они не непрерывны, скрипт выше работает нормально:

>>> txt = "Barack Obama is the husband of Michelle Obama."  
>>> get_continuous_chunks(txt)
['Barack Obama', 'Michelle Obama']

В ответе @alvas есть ошибка. Ошибка забора. Обязательно запустите эту проверку elif и вне цикла, чтобы не пропустить NE, который появляется в конце предложения. Так:

from nltk import ne_chunk, pos_tag, word_tokenize
from nltk.tree import Tree

def get_continuous_chunks(text):
    chunked = ne_chunk(pos_tag(word_tokenize(text)))
    prev = None
    continuous_chunk = []
    current_chunk = []

    for i in chunked:
        if type(i) == Tree:
            current_chunk.append(" ".join([token for token, pos in i.leaves()]))
        elif current_chunk:
            named_entity = " ".join(current_chunk)
            if named_entity not in continuous_chunk:
                continuous_chunk.append(named_entity)
                current_chunk = []
        else:
            continue
    if current_chunk:
        named_entity = " ".join(current_chunk)
        if named_entity not in continuous_chunk:
            continuous_chunk.append(named_entity)
            current_chunk = []
    return continuous_chunk

txt = "Barack Obama is a great person and so is Michelle Obama." 
print get_continuous_chunks(txt)

@alvas отличный ответ. Это было действительно полезно. Я попытался описать ваше решение более функционально. Все еще должен улучшить это все же.

    def conditions(tree_node):
    return tree_node.height() == 2

    def coninuous_entities(self, input_text, file_handle):
      from nltk import ne_chunk, pos_tag, word_tokenize
      from nltk.tree import Tree

      # Note: Currently, the chunker categorizes only 2 'NNP' together.  
      docs = input_text.split('\n')
      for input_text in docs:
          chunked_data = ne_chunk(pos_tag(word_tokenize(input_text)))
          child_data = [subtree for subtree in chunked_data.subtrees(filter = self.filter_conditions)]

          named_entities = []
          for child in child_data:
              if type(child) == Tree:
                  named_entities.append(" ".join([token for token, pos in child.leaves()]))

          # Dump all entities to file for now, we will see how to go about that
          if file_handle is not None:
              file_handle.write('\n'.join(named_entities) + '\n')
      return named_entities

Используя функцию условий, можно добавить много условий для фильтрации.

Другие вопросы по тегам