Я хотел бы использовать средства сопоставления Spacy для определения отношений "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?

2
Superdooperhero 28 Июн 2019 в 17:35

3 ответа

Лучший ответ

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

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
Superdooperhero 29 Июн 2019 в 20:34

Я думаю, что это из-за частичных совпадений. Регулярное выражение дает все возможные совпадения для вашего шаблона, который также включает подстроку. В случае 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

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

-1
ashutosh singh 28 Июн 2019 в 14:49

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

                   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

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

2
David Dale 28 Июн 2019 в 22:08