Spacy «является» майнингом

Я хотел бы использовать сопоставители 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?

Я не уверен, какова ваша цель, но есть большая вероятность, что вам не имеет смысла начинать здесь с нуля. Многие люди рассматривали методы извлечения информации из данных Википедии. Одним из мест, где можно начать поиск существующих инструментов/методов, может быть проект DBpedia.

aab 29.06.2019 14:20
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
1
179
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

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

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

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

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

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

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

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

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

Определенно правильный подход, но я бы рекомендовал вместо этого использовать displacy для просмотра деревьев зависимостей: гораздо лучший рендеринг, включая метки зависимостей и порядок токенов.

aab 29.06.2019 14:10
Ответ принят как подходящий

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

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

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