Spacy "майнинг" - PullRequest
       8

Spacy "майнинг"

2 голосов
/ 28 июня 2019

Я хотел бы использовать Spacy matchers для определения отношений "is a" (и других) из Википедии для создания базы знаний.

У меня есть следующий код:

nlp = spacy.load("en_core_web_lg")
text = u"""Garfield is a large comic strip cat that lives in Ohio. Cape Town is the oldest city in South Africa."""
doc = nlp(text)
sentence_spans = list(doc.sents)
# Write a pattern
pattern = [
    {"POS": "PROPN", "OP": "+"}, 
    {"LEMMA": "be"}, 
    {"POS": "DET"}, 
    {"POS": "ADJ", "OP": "*"}, 
    {"POS": "NOUN", "OP": "+"}
]   

# Add the pattern to the matcher and apply the matcher to the doc
matcher.add("IS_A_PATTERN", None, pattern)
matches = matcher(doc)

# Iterate over the matches and print the span text
for match_id, start, end in matches:
    print("Match found:", doc[start:end].text)

К сожалению, это соответствует:

Match found: Garfield is a large comic strip
Match found: Garfield is a large comic strip cat
Match found: Town is the oldest city
Match found: Cape Town is the oldest city

, тогда как я просто хочу:

Match found: Garfield is a large comic strip cat
Match found: Cape Town is the oldest city

Кроме того, я не прочь заявить, что первая часть матча должна бытьпредмет предложения и последняя часть предикат.

Я также хотел бы вернуть это разделенное следующим образом:

['Garfield', 'is a', 'large comic strip cat', 'comic strip cat']
['Cape Town', 'is the', 'oldest city', 'city']

Так, чтобы я мог получить список городов.

Возможно ли что-либо из этого в Spacy или каким будет эквивалентный код Python?

Ответы [ 3 ]

2 голосов
/ 29 июня 2019

Я думаю, вам нужен синтаксический анализ. С синтаксической точки зрения ваши предложения выглядят как

                   is                             
    _______________|_____                          
   |      |             cat                       
   |      |    __________|________________         
   |      |   |    |     |     |        lives     
   |      |   |    |     |     |     _____|____    
   |      |   |    |     |     |    |          in 
   |      |   |    |     |     |    |          |   
Garfield  .   a  large comic strip that       Ohio

          is              
  ________|____            
 |   |        city        
 |   |     ____|______     
 |   |    |    |      in  
 |   |    |    |      |    
 |  Town  |    |    Africa
 |   |    |    |      |    
 .  Cape the oldest South 

(я использовал метод из этого вопроса для построения деревьев).

Теперь вместо извлечения подстрок вы должны извлечь поддеревья. Минимальный код для достижения этого сначала находит шаблон «is», а затем выдает левое и правое поддеревья, если они присоединены к «is» с правым видом зависимостей:

def get_head(sentence):
    toks = [t for t in sentence]
    for i, t in enumerate(toks):
        if t.lemma_ == 'be' and i + 1 < len(toks) and toks[i+1].pos_ == 'DET':
            yield t

def get_relations(text):
    doc = nlp(text)
    for sent in doc.sents:
        for head in get_head(sent):
            children = list(head.children)
            if len(children) < 2:
                continue
            l, r = children[0:2]
            # check that the left child is really a subject and the right one is a description
            if l.dep_ == 'nsubj' and r.dep_ == 'attr':
                yield l, r

for l, r in get_relations(text):
    print(list(l.subtree), list(r.subtree))

Будет выведено что-то вроде

[Garfield] [a, large, comic, strip, cat, that, lives, in, Ohio]
[Cape, Town] [the, oldest, city, in, South, Africa]

Таким образом, вы, по крайней мере, правильно отделяете левую часть от правой части. Если вы хотите, вы можете добавить больше фильтров (например, l.pos_ == 'PROPN'). Другим улучшением будет обработка случаев с более чем двумя детьми «есть» (например, наречия).

Теперь вы можете обрезать поддеревья по своему усмотрению, создавая даже меньшие предикаты (например, «большая кошка», «комический кот», «полосатый кот», «кошка, которая живет в Огайо» и т. Д.). Быстрая и грязная версия такой обрезки может смотреть каждый раз только на одного ребенка:

for l, r in get_relations(text):
    print(list(l.subtree), list(r.subtree))
    for c in r.children:
        words = [r] + list(c.subtree)
        print(' '.join([w.text for w in sorted(words, key=lambda x: x.i)]))

Это даст следующий результат

[Garfield], [a, large, comic, strip, cat, that, lives, in, Ohio]
a cat
large cat
comic cat
strip cat
cat that lives in Ohio
[Cape, Town], [the, oldest, city, in, South, Africa]
the city
oldest city
city in South Africa

Вы видите, что некоторые поддеревья неправильны: Кейптаун не является "самым старым городом" в мире. Но, похоже, вам нужны хотя бы некоторые семантические знания, чтобы отфильтровать такие неправильные поддеревья.

0 голосов
/ 29 июня 2019

Удалось сделать это с помощью этого кода:

doc = nlp("Cape Town (Afrikaans: Kaapstad, Dutch: Kapstadt) is the oldest city in the south west of South Africa.")
for chunk in doc.noun_chunks:
    if chunk.root.dep_ == 'nsubj' and chunk.root.head.text == 'is':
        subject_name = chunk.text
    elif chunk.root.dep_ == 'attr' and chunk.root.head.text == 'is':
        attr_full = chunk.text 
        attr_type = chunk.root.text
print("{:<25}{:<25}{}".format(subject_name, attr_full, attr_type))    

который печатает:

Cape Town                the oldest city          city
0 голосов
/ 28 июня 2019

Я думаю, что это из-за частичных совпадений.Регулярное выражение дает все возможные совпадения для вашего шаблона, который также включает подстроку.В случае Cape Town is the oldest city и Town is the oldest city оба удовлетворяют условию вашего паттерна.

Либо вы можете отфильтровать подстроки, либо другой метод будет разделять ваши существительные на части и заменять их определенным тегом, а затемприменить шаблон.Например,

предложение = Cape Town is the oldest city
noun_chunked_sentence = Cape_Town is the oldest_city

После этого вы можете применить тот же шаблон, и он должен работать.

...