我一直在尝试找到如何使用 spaCy 获取依赖树,但我找不到任何关于如何获取树的信息,只有关于如何导航树。
8 回答
如果有人想轻松查看 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
树本身不是一个对象。您只需通过令牌之间的关系导航它。这就是为什么文档谈论导航树,而不是“获取”它。
首先,让我们解析一些文本来获取一个Doc
对象:
>>> import spacy
>>> nlp = spacy.load('en_core_web_sm')
>>> doc = nlp('First, I wrote some sentences. Then spaCy parsed them. Hooray!')
>>> 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。
您可以使用下面的库来查看您的依赖关系树,发现它非常有帮助!
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)
我不知道这是一个新的 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 有点用词不当,该方法本身不打印任何内容,而是返回一个字典列表,每个句子一个。
我还需要在完整代码下面这样做:
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)
虽然 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 个字也是如此。我从来不用猜测它的输出——这显然是无价的。
我对解析还没有足够的了解。然而,我的文献研究的结果是知道 spaCy 有一个 shift-reduce 依赖解析算法。这会解析问题/句子,从而生成解析树。为了可视化这一点,您可以使用 DisplaCy,它是 CSS 和 Javascript 的组合,可与 Python 和 Cython 一起使用。此外,您可以使用 SpaCy 库进行解析,并导入自然语言工具包 (NLTK)。希望这可以帮助