Рекурсия в RegexpParser НЛТК - PullRequest
4 голосов
/ 19 апреля 2019

На основе грамматики в главе 7 Книги NLTK :

grammar = r"""
      NP: {<DT|JJ|NN.*>+} # ...
"""

Я хочу расширить NP (существительное), включив в него несколько NP , к которым присоединяются CC (координирующие соединения: и ) или , (запятые) для захвата имен существительных, таких как:

  • Дом и дерево
  • яблоко, апельсин и манго
  • Автомобиль, дом и самолет

Я не могу заставить мою модифицированную грамматику захватывать их как один NP :

import nltk

grammar = r"""
  NP: {<DT|JJ|NN.*>+(<CC|,>+<NP>)?}
"""

sentence = 'The house and tree'
chunkParser = nltk.RegexpParser(grammar)
words = nltk.word_tokenize(sentence)
tagged = nltk.pos_tag(words)
print(chunkParser.parse(tagged))

Результат:

(S (NP The/DT house/NN) and/CC (NP tree/NN))

Я пытался переместить NP в начало: NP: {(<NP><CC|,>+)?<DT|JJ|NN.*>+}, но я получаю тот же результат

(S (NP The/DT house/NN) and/CC (NP tree/NN))

1 Ответ

4 голосов
/ 23 апреля 2019

Позволяет начать с малого и правильно захватить NP (существительные фразы):

import nltk

grammar = r"""
  NP: {<DT|JJ|NN.*>+}
"""

sentence = 'The house and tree'
chunkParser = nltk.RegexpParser(grammar)
words = nltk.word_tokenize(sentence)
tagged = nltk.pos_tag(words)
print(chunkParser.parse(tagged))

[выход]:

(S (NP The/DT house/NN) and/CC (NP tree/NN))

Теперь попробуем поймать это and/CC. Просто добавьте фразу более высокого уровня, которая использует правило <NP>:

grammar = r"""
  NP: {<DT|JJ|NN.*>+}
  CNP: {<NP><CC><NP>}
"""

sentence = 'The house and tree'
chunkParser = nltk.RegexpParser(grammar)
words = nltk.word_tokenize(sentence)
tagged = nltk.pos_tag(words)
print(chunkParser.parse(tagged))

[выход]:

(S (CNP (NP The/DT house/NN) and/CC (NP tree/NN)))

Теперь, когда мы ловим NP CC NP фраз, давайте немного придумаем и посмотрим, поймает ли он запятые:

grammar = r"""
  NP: {<DT|JJ|NN.*>+}
  CNP: {<NP><CC|,><NP>}
"""

sentence = 'The house, the bear and tree'
chunkParser = nltk.RegexpParser(grammar)
words = nltk.word_tokenize(sentence)
tagged = nltk.pos_tag(words)
print(chunkParser.parse(tagged))

Теперь мы видим, что он ограничен ловлей первого ограниченного слева NP CC|, NP и оставил последний NP один.

Так как мы знаем, что в английском языке конъюнктивные фразы имеют ограниченное в левой части соединение и правую ограниченную NP, то есть CC|, NP, например, and the tree, мы видим, что шаблон CC|, NP является повторяющимся, поэтому мы можем использовать его в качестве промежуточного представления.

grammar = r"""
  NP: {<DT|JJ|NN.*>+}
  XNP: {<CC|,><NP>}
  CNP: {<NP><XNP>+}
"""

sentence = 'The house, the bear and tree'
chunkParser = nltk.RegexpParser(grammar)
words = nltk.word_tokenize(sentence)
tagged = nltk.pos_tag(words)
print(chunkParser.parse(tagged))

[выход]:

(S
  (CNP
    (NP The/DT house/NN)
    (XNP ,/, (NP the/DT bear/NN))
    (XNP and/CC (NP tree/NN))))

В конечном счете, грамматика CNP (Conjunctive NPs) фиксирует соединение цепочечной фразы в английском языке, даже сложные, например,

import nltk

grammar = r"""
  NP: {<DT|JJ|NN.*>+}
  XNP: {<CC|,><NP>}
  CNP: {<NP><XNP>+}
"""

sentence = 'The house, the bear, the green house and a tree went to the park or the river.'
chunkParser = nltk.RegexpParser(grammar)
words = nltk.word_tokenize(sentence)
tagged = nltk.pos_tag(words)
print(chunkParser.parse(tagged))

[выход]:

(S
  (CNP
    (NP The/DT house/NN)
    (XNP ,/, (NP the/DT bear/NN))
    (XNP ,/, (NP the/DT green/JJ house/NN))
    (XNP and/CC (NP a/DT tree/JJ)))
  went/VBD
  to/TO
  (CNP (NP the/DT park/NN) (XNP or/CC (NP the/DT river/NN)))
  ./.)

А если вам просто интересно извлечь фразы из существительного, из Как пройти по объекту дерева NLTK? :

noun_phrases = []

def traverse_tree(tree):
    if tree.label() == 'CNP':
        noun_phrases.append(' '.join([token for token, tag in tree.leaves()]))
    for subtree in tree:
        if type(subtree) == nltk.tree.Tree:
            traverse_tree(subtree)

    return noun_phrases

sentence = 'The house, the bear, the green house and a tree went to the park or the river.'
chunkParser = nltk.RegexpParser(grammar)
words = nltk.word_tokenize(sentence)
tagged = nltk.pos_tag(words)
traverse_tree(chunkParser.parse(tagged))

[выход]:

['The house , the bear , the green house and a tree', 'the park or the river']

Также см. Python (NLTK) - более эффективный способ извлечения имен существительных?

...