【问题标题】:Spacy "is a" miningSpacy“是”采矿
【发布时间】:2019-06-30 04:34:31
【问题描述】:

我想使用 Spacy 匹配器从 Wikipedia 中挖掘“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 项目。

标签: python nlp spacy


【解决方案1】:

我认为您需要在这里进行一些句法分析。从句法的角度来看,你的句子看起来像

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

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

(我使用this question 中的方法绘制树木)。

现在,您应该提取子树,而不是提取子字符串。实现这一点的最小代码将首先找到“is a”模式,然后产生左右子树,如果它们以正确的依赖关系附加到“is a”:

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')。另一个改进是处理具有超过 2 个“is”子级的情况(例如副词)。

现在,您可以根据需要修剪子树,生成更小的谓词(如“大型猫”、“漫画猫”、“条猫”、“住在俄亥俄州的猫”等)。这种修剪的快速而肮脏的版本可能每次只看一个孩子:

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 来查看依赖树:更好的渲染,包括依赖标签和令牌排序。
【解决方案2】:

用这段代码设法做到了:

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

【讨论】:

    【解决方案3】:

    我认为这是因为部分匹配。正则表达式为您的模式提供所有可能的匹配项,其中也包括一个子字符串。如果Cape Town is the oldest cityTown is the oldest city 都满足您的模式条件。

    您可以过滤掉子字符串,或者另一种方法是将名词分块并用特定标签替换它们,然后应用模式。例如。

    句子 = Cape Town is the oldest city
    noun_chunked_sentence = Cape_Town is the oldest_city

    在此之后,您可以应用相同的模式,它应该可以工作。

    【讨论】:

      猜你喜欢
      • 2017-03-10
      • 2021-03-20
      • 1970-01-01
      • 1970-01-01
      • 2011-10-27
      • 1970-01-01
      • 2020-09-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多