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






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