Вычисление имен Norton Recognizer (NER) в Stanford Nancy из формата NLTK
Я использую NER в NLTK для поиска лиц, мест и организаций в предложениях. Я могу сделать такие результаты следующим образом:
[(u'Remaking', u'O'), (u'The', u'O'), (u'Republican', u'ORGANIZATION'), (u'Party', u'ORGANIZATION')]
Можно ли объединить все вместе, используя это?
Я хочу так:
u'Remaking'/ u'O', u'The'/u'O', (u'Republican', u'Party')/u'ORGANIZATION'
Спасибо!
Ответы
Ответ 1
Вы можете использовать стандартный способ NLTK для представления фрагментов с помощью nltk.Tree. Это может означать, что вам нужно немного изменить свое представление.
То, что я обычно делаю, представляет предложения NER как списки триплетов:
sentence = [('Andrew', 'NNP', 'PERSON'), ('is', 'VBZ', 'O'), ('part', 'NN', 'O'), ('of', 'IN', 'O'), ('the', 'DT', 'O'), ('Republican', 'NNP', 'ORGANIZATION'), ('Party', 'NNP', 'ORGANIZATION'), ('in', 'IN', 'O'), ('Dallas', 'NNP', 'LOCATION')]
Я делаю это, когда использую внешний инструмент для привязки предложения NER. Теперь вы можете преобразовать это предложение в представление NLTK:
from nltk import Tree
def IOB_to_tree(iob_tagged):
root = Tree('S', [])
for token in iob_tagged:
if token[2] == 'O':
root.append((token[0], token[1]))
else:
try:
if root[-1].label() == token[2]:
root[-1].append((token[0], token[1]))
else:
root.append(Tree(token[2], [(token[0], token[1])]))
except:
root.append(Tree(token[2], [(token[0], token[1])]))
return root
sentence = [('Andrew', 'NNP', 'PERSON'), ('is', 'VBZ', 'O'), ('part', 'NN', 'O'), ('of', 'IN', 'O'), ('the', 'DT', 'O'), ('Republican', 'NNP', 'ORGANIZATION'), ('Party', 'NNP', 'ORGANIZATION'), ('in', 'IN', 'O'), ('Dallas', 'NNP', 'LOCATION')]
print IOB_to_tree(sentence)
Изменение вида представления имеет смысл, потому что вам, безусловно, нужны метки POS для тегов NER.
Конечный результат должен выглядеть так:
(S
(PERSON Andrew/NNP)
is/VBZ
part/NN
of/IN
the/DT
(ORGANIZATION Republican/NNP Party/NNP)
in/IN
(LOCATION Dallas/NNP))
Ответ 2
Он выглядит долго, но он выполняет работу:
ner_output = [(u'Remaking', u'O'), (u'The', u'O'), (u'Republican', u'ORGANIZATION'), (u'Party', u'ORGANIZATION')]
chunked, pos = [], ""
for i, word_pos in enumerate(ner_output):
word, pos = word_pos
if pos in ['PERSON', 'ORGANIZATION', 'LOCATION'] and pos == prev_tag:
chunked[-1]+=word_pos
else:
chunked.append(word_pos)
prev_tag = pos
clean_chunked = [tuple([" ".join(wordpos[::2]), wordpos[-1]]) if len(wordpos)!=2 else wordpos for wordpos in chunked]
print clean_chunked
[выход]:
[(u'Remaking', u'O'), (u'The', u'O'), (u'Republican Party', u'ORGANIZATION')]
Подробнее:
Первый for-loop "с памятью" выполняет примерно следующее:
[(u'Remaking', u'O'), (u'The', u'O'), (u'Republican', u'ORGANIZATION', u'Party', u'ORGANIZATION')]
Вы поймете, что все имена Enitties будут содержать более двух элементов в кортеже, и вы хотите, чтобы слова были как элементы в списке, т.е. 'Republican Party'
в (u'Republican', u'ORGANIZATION', u'Party', u'ORGANIZATION')
, поэтому вы сделаете что-то вроде это для получения четных элементов:
>>> x = [0,1,2,3,4,5,6]
>>> x[::2]
[0, 2, 4, 6]
>>> x[1::2]
[1, 3, 5]
Затем вы также поняли, что последний элемент в корте NE - это тег, который вы хотите, поэтому вы будете делать `
>>> x = (u'Republican', u'ORGANIZATION', u'Party', u'ORGANIZATION')
>>> x[::2]
(u'Republican', u'Party')
>>> x[-1]
u'ORGANIZATION'
Это немного ad hoc и vebose, но я надеюсь, что это поможет. И здесь это функция, Благословенное Рождество:
ner_output = [(u'Remaking', u'O'), (u'The', u'O'), (u'Republican', u'ORGANIZATION'), (u'Party', u'ORGANIZATION')]
def rechunk(ner_output):
chunked, pos = [], ""
for i, word_pos in enumerate(ner_output):
word, pos = word_pos
if pos in ['PERSON', 'ORGANIZATION', 'LOCATION'] and pos == prev_tag:
chunked[-1]+=word_pos
else:
chunked.append(word_pos)
prev_tag = pos
clean_chunked = [tuple([" ".join(wordpos[::2]), wordpos[-1]])
if len(wordpos)!=2 else wordpos for wordpos in chunked]
return clean_chunked
print rechunk(ner_output)
Ответ 3
На самом деле это происходит в следующей версии CoreNLP под названием MentionsAnnotator
. Скорее всего, он не будет доступен напрямую из NLTK, если только люди NLTK не захотят его поддерживать, а также стандартный интерфейс Stanford NER.
В любом случае на данный момент вам нужно будет скопировать код, с которым я связан (который использует LabeledChunkIdentifier
для грязная работа) или написать собственный постпроцессор в Python.
Ответ 4
Вот еще одна краткая реализация для группировки результатов Stanford NER с использованием итератора group itertools:
def grouptags(tags, ignore="O", join=" "):
from itertools import groupby
for c,g in groupby(tags, lambda t: t[1]):
if ignore is None or c != ignore:
if join is None:
entity = [e for e,_ in g]
else:
entity = join.join(e for e,_ in g)
yield(c, entity)
Функция grouptags имеет две опции:
- игнорировать: указать класс, который игнорируется и исключается из вывода (по умолчанию: "O" ). Если "Нет", все объекты возвращаются.
- join: укажите символ, используемый для соединения частей (по умолчанию: ""). Если None, части возвращаются в виде списка.