65

我一直在尝试找到如何使用 spaCy 获取依赖树,但我找不到任何关于如何获取树的信息,只有关于如何导航树

4

8 回答 8

78

如果有人想轻松查看 spacy 生成的依赖树,一种解决方案是将其转换为 annltk.tree.Tree并使用该nltk.tree.Tree.pretty_print方法。这是一个例子:

import spacy
from nltk import Tree


en_nlp = spacy.load('en')

doc = en_nlp("The quick brown fox jumps over the lazy dog.")

def to_nltk_tree(node):
    if node.n_lefts + node.n_rights > 0:
        return Tree(node.orth_, [to_nltk_tree(child) for child in node.children])
    else:
        return node.orth_


[to_nltk_tree(sent.root).pretty_print() for sent in doc.sents]

输出:

                jumps                  
  ________________|____________         
 |    |     |     |    |      over     
 |    |     |     |    |       |        
 |    |     |     |    |      dog      
 |    |     |     |    |    ___|____    
The quick brown  fox   .  the      lazy

编辑:要更改令牌表示,您可以这样做:

def tok_format(tok):
    return "_".join([tok.orth_, tok.tag_])


def to_nltk_tree(node):
    if node.n_lefts + node.n_rights > 0:
        return Tree(tok_format(node), [to_nltk_tree(child) for child in node.children])
    else:
        return tok_format(node)

结果是:

                         jumps_VBZ                           
   __________________________|___________________             
  |       |        |         |      |         over_IN        
  |       |        |         |      |            |            
  |       |        |         |      |          dog_NN        
  |       |        |         |      |     _______|_______     
The_DT quick_JJ brown_JJ   fox_NN  ._. the_DT         lazy_JJ
于 2016-08-23T14:41:34.537 回答
61

树本身不是一个对象。您只需通过令牌之间的关系导航它。这就是为什么文档谈论导航树,而不是“获取”它。

首先,让我们解析一些文本来获取一个Doc对象:

>>> import spacy
>>> nlp = spacy.load('en_core_web_sm')
>>> doc = nlp('First, I wrote some sentences. Then spaCy parsed them. Hooray!')

doc是一个Sequence对象Token

>>> doc[0]
First
>>> doc[1]
,
>>> doc[2]
I
>>> doc[3]
wrote

但它没有单个根令牌。我们解析了一个由三个句子组成的文本,所以有三棵不同的树,每棵树都有自己的根。如果我们想从每个句子的根开始解析,首先将句子作为不同的对象是有帮助的。幸运的是,通过属性doc向我们公开了这些:.sents

>>> sentences = list(doc.sents)
>>> for sentence in sentences:
...     print(sentence)
... 
First, I wrote some sentences.
Then spaCy parsed them.
Hooray!

这些句子中的每一个都是一个Span具有.root指向其根标记的属性。通常,词根标记将是句子的主要动词(尽管对于不寻常的句子结构可能不是这样,例如没有动词的句子):

>>> for sentence in sentences:
...     print(sentence.root)
... 
wrote
parsed
Hooray

找到根标记后,我们可以通过.children每个标记的属性向下导航树。例如,让我们找出第一句中动词的主语和宾语。.dep_每个子代币的属性描述了它与父代的关系;例如 a dep_of'nsubj'意味着一个记号是其父代的名义主语。

>>> root_token = sentences[0].root
>>> for child in root_token.children:
...     if child.dep_ == 'nsubj':
...         subj = child
...     if child.dep_ == 'dobj':
...         obj = child
... 
>>> subj
I
>>> obj
sentences

我们同样可以通过查看这些令牌的其中一个子代来继续往下走:

>>> list(obj.children)
[some]

因此,使用上面的属性,您可以导航整个树。如果你想可视化一些依赖关系树来帮助你理解结构,我建议使用displaCy

于 2016-10-29T15:26:38.353 回答
20

您可以使用下面的库来查看您的依赖关系树,发现它非常有帮助!

import spacy
from spacy import displacy

nlp = spacy.load('en')
doc = nlp(u'This is a sentence.')
displacy.serve(doc, style='dep')

你可以用浏览器打开它,它看起来像: 置换服务输出

要生成 SVG 文件:

from pathlib import Path

output_path = Path("yourpath/.svg")
svg = displacy.render(doc, style='dep')
with output_path.open("w", encoding="utf-8") as fh:
    fh.write(svg)
于 2018-08-06T19:51:27.947 回答
8

我不知道这是一个新的 API 调用还是什么,但是 Document 类上有一个 .print_tree() 方法可以快速完成这项工作。

https://spacy.io/api/doc#print_tree

它将依赖关系树转储到 JSON。它处理多个句子根和所有这些:

    import spacy    
    nlp = spacy.load('en')
    doc1 = nlp(u'This is the way the world ends.  So you say.')  
    print(doc1.print_tree(light=True))

名称print _tree 有点用词不当,该方法本身不打印任何内容,而是返回一个字典列表,每个句子一个。

于 2018-11-12T06:12:19.967 回答
7

事实证明,该树可通过文档中的标记获得。

您是否想找到树的根,您可以浏览文档:

def find_root(docu):
    for token in docu:
        if token.head is token:
            return token

然后导航树,令牌有 API 来通过孩子

于 2016-04-14T02:07:11.767 回答
6

我还需要在完整代码下面这样做:

import sys
def showTree(sent):
    def __showTree(token):
        sys.stdout.write("{")
        [__showTree(t) for t in token.lefts]
        sys.stdout.write("%s->%s(%s)" % (token,token.dep_,token.tag_))
        [__showTree(t) for t in token.rights]
        sys.stdout.write("}")
    return __showTree(sent.root)

如果你想要终端的间距:

def showTree(sent):
    def __showTree(token, level):
        tab = "\t" * level
        sys.stdout.write("\n%s{" % (tab))
        [__showTree(t, level+1) for t in token.lefts]
        sys.stdout.write("\n%s\t%s [%s] (%s)" % (tab,token,token.dep_,token.tag_))
        [__showTree(t, level+1) for t in token.rights]
        sys.stdout.write("\n%s}" % (tab))
    return __showTree(sent.root, 1)
于 2016-08-10T20:40:29.263 回答
1

虽然 spaCy 库在过去 5 年中可能发生了一些变化,但 @Mark Amery 的方法非常有效。这就是我一直在做的分解页面和副本页面中的句子,以获得名义上描述的功能,以及与它们相关联的 NP 或 VP。我们采取的另一种方法(这可能在过去 5 年中出现在 SpaCy 中)......祖先的孩子,你基本上会找到指向主语、宾语和根的头。您可以将它们分解为基于同位词或连词等修饰语的子句,这将告诉您这些子句是对功能描述的补充还是核心。这样你就可以重写句子,我主要这样做是为了去除多余的东西并创建包含硬细节的片段。不知道是否对其他人有帮助,但这是我在与 SpaCy 基于张量的建模相比,在纸上绘制 nsubj、dobj、连词和 pobj 数周后遵循的策略。IMO,值得注意的是,SpaCy 所做的标记似乎总是 100% 正确 - 每次,即使片段在写得非常糟糕的连续运行中相距 20 个字也是如此。我从来不用猜测它的输出——这显然是无价的。和纸上的 pobjs 与 SpaCy 基于张量的建模相比。IMO,值得注意的是,SpaCy 所做的标记似乎总是 100% 正确 - 每次,即使片段在写得非常糟糕的连续运行中相距 20 个字也是如此。我从来不用猜测它的输出——这显然是无价的。和纸上的 pobjs 与 SpaCy 基于张量的建模相比。IMO,值得注意的是,SpaCy 所做的标记似乎总是 100% 正确 - 每次,即使片段在写得非常糟糕的连续运行中相距 20 个字也是如此。我从来不用猜测它的输出——这显然是无价的。

于 2021-08-06T14:58:20.420 回答
-2

我对解析还没有足够的了解。然而,我的文献研究的结果是知道 spaCy 有一个 shift-reduce 依赖解析算法。这会解析问题/句子,从而生成解析树。为了可视化这一点,您可以使用 DisplaCy,它是 CSS 和 Javascript 的组合,可与 Python 和 Cython 一起使用。此外,您可以使用 SpaCy 库进行解析,并导入自然语言工具包 (NLTK)。希望这可以帮助

于 2017-02-20T09:41:12.043 回答