如何使用 spaCy 获取依赖树?
How to get the dependency tree with spaCy?
我一直在尝试寻找如何使用 spaCy 获取依赖树,但我找不到任何关于如何获取树的信息,只能在 how to navigate the tree.
上找到
事实证明,树在文档中可用through the tokens。
你想找到树的根,你可以去看看文档:
def find_root(docu):
for token in docu:
if token.head is token:
return token
然后导航树,令牌有 API 得到 through the children
我还需要在完整代码下方这样做:
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 生成的依赖树,一种解决方案是将其转换为 nltk.tree.Tree
and use the 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
这棵树本身并不是一棵 object;您只需通过令牌之间的关系来导航它。这就是为什么文档谈论导航树,而不是 'getting' 它。
首先,让我们解析一些文本以获得 Doc
object:
>>> import spacy
>>> nlp = spacy.load('en_core_web_sm')
>>> doc = nlp('First, I wrote some sentences. Then spaCy parsed them. Hooray!')
doc
是一个 Sequence
of Token
objects:
>>> doc[0]
First
>>> doc[1]
,
>>> doc[2]
I
>>> doc[3]
wrote
但是它没有一个根令牌。我们解析了一个由三个句子组成的文本,因此存在三个不同的树,每个树都有自己的根。如果我们想从每个句子的根部开始解析,首先将句子区分为 object 会有所帮助。幸运的是,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
属性 向下导航树。例如,让我们在第一句中找到主语和动词的 object。每个child令牌的.dep_
属性;例如 'nsubj'
的 dep_
表示令牌是其 parent.
的 名义主题
>>> 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
我们同样可以通过查看这些令牌之一的 children:
>>> list(obj.children)
[some]
因此,使用上述属性,您可以浏览整个树。如果你想可视化一些例句的依赖树来帮助你理解结构,我建议玩 displaCy.
我对解析的了解还不够。然而,根据我的文献研究结果,我知道 spaCy 有一个 shift-reduce 依赖解析算法。这将解析 question/sentence,生成一个解析树。为了可视化这一点,您可以使用 DisplaCy,CSS 和 Javascript 的组合,与 Python 和 Cython 一起使用。
此外,您可以使用 SpaCy 库进行解析,并导入自然语言工具包 (NLTK)。希望这有帮助
您可以使用下面的库来查看您的依赖关系树,发现它非常有用!
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 调用还是什么,但是文档 class 上有一个 .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 有点用词不当,该方法本身不打印任何内容,而是 returns 一个字典列表,每个字典一个句子.
虽然 spaCy 库在过去 5 年中可能发生了一些变化,但 @Mark Amery 的方法非常有效。这就是我一直在做的,在副本的一页又一页中分解句子,以获得名义上描述的特征,以及与它们相关的 NP 或 VP。我们采用的另一种方法(在过去的 5 年里可能已经出现在 SpaCy 中)......如果你看一下 against your future 短语中的根 VB,并且注意 dep 的类型,这root的祖先和祖先的children,你基本上会找到指向主题的头,object和root。您可以将它们分解成基于同位词或连词等修饰语的子句,这些子句将告诉您这些子句是对功能描述的补充还是核心。这样你就可以重写句子了,我主要这样做是为了去掉多余的东西并创建由硬细节组成的片段。不知道是否对其他人有帮助,但这是我在与 SpaCy 基于张量的建模相比在纸上绘制 nsubj、dobj、conjuncts 和 pobjs 图表几周后遵循的策略。 IMO,值得注意的是,SpaCy 所做的标记似乎总是 100% 正确 - 每次,即使片段相距 20 个单词,写得很糟糕 run-ons 写得很糟糕。我永远不必再猜测它的输出 - 这显然是无价的。
我一直在尝试寻找如何使用 spaCy 获取依赖树,但我找不到任何关于如何获取树的信息,只能在 how to navigate the tree.
上找到事实证明,树在文档中可用through the tokens。
你想找到树的根,你可以去看看文档:
def find_root(docu):
for token in docu:
if token.head is token:
return token
然后导航树,令牌有 API 得到 through the children
我还需要在完整代码下方这样做:
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 生成的依赖树,一种解决方案是将其转换为 nltk.tree.Tree
and use the 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
这棵树本身并不是一棵 object;您只需通过令牌之间的关系来导航它。这就是为什么文档谈论导航树,而不是 'getting' 它。
首先,让我们解析一些文本以获得 Doc
object:
>>> import spacy
>>> nlp = spacy.load('en_core_web_sm')
>>> doc = nlp('First, I wrote some sentences. Then spaCy parsed them. Hooray!')
doc
是一个 Sequence
of Token
objects:
>>> doc[0]
First
>>> doc[1]
,
>>> doc[2]
I
>>> doc[3]
wrote
但是它没有一个根令牌。我们解析了一个由三个句子组成的文本,因此存在三个不同的树,每个树都有自己的根。如果我们想从每个句子的根部开始解析,首先将句子区分为 object 会有所帮助。幸运的是,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
属性 向下导航树。例如,让我们在第一句中找到主语和动词的 object。每个child令牌.dep_
属性;例如 'nsubj'
的 dep_
表示令牌是其 parent.
>>> 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
我们同样可以通过查看这些令牌之一的 children:
>>> list(obj.children)
[some]
因此,使用上述属性,您可以浏览整个树。如果你想可视化一些例句的依赖树来帮助你理解结构,我建议玩 displaCy.
我对解析的了解还不够。然而,根据我的文献研究结果,我知道 spaCy 有一个 shift-reduce 依赖解析算法。这将解析 question/sentence,生成一个解析树。为了可视化这一点,您可以使用 DisplaCy,CSS 和 Javascript 的组合,与 Python 和 Cython 一起使用。 此外,您可以使用 SpaCy 库进行解析,并导入自然语言工具包 (NLTK)。希望这有帮助
您可以使用下面的库来查看您的依赖关系树,发现它非常有用!
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 调用还是什么,但是文档 class 上有一个 .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 有点用词不当,该方法本身不打印任何内容,而是 returns 一个字典列表,每个字典一个句子.
虽然 spaCy 库在过去 5 年中可能发生了一些变化,但 @Mark Amery 的方法非常有效。这就是我一直在做的,在副本的一页又一页中分解句子,以获得名义上描述的特征,以及与它们相关的 NP 或 VP。我们采用的另一种方法(在过去的 5 年里可能已经出现在 SpaCy 中)......如果你看一下 against your future 短语中的根 VB,并且注意 dep 的类型,这root的祖先和祖先的children,你基本上会找到指向主题的头,object和root。您可以将它们分解成基于同位词或连词等修饰语的子句,这些子句将告诉您这些子句是对功能描述的补充还是核心。这样你就可以重写句子了,我主要这样做是为了去掉多余的东西并创建由硬细节组成的片段。不知道是否对其他人有帮助,但这是我在与 SpaCy 基于张量的建模相比在纸上绘制 nsubj、dobj、conjuncts 和 pobjs 图表几周后遵循的策略。 IMO,值得注意的是,SpaCy 所做的标记似乎总是 100% 正确 - 每次,即使片段相距 20 个单词,写得很糟糕 run-ons 写得很糟糕。我永远不必再猜测它的输出 - 这显然是无价的。