【问题标题】:Token extension versus matcher versus phrase matcher vs entity ruler in spaCyspaCy中的令牌扩展与匹配器与短语匹配器与实体标尺
【发布时间】:2019-09-15 01:16:34
【问题描述】:

我正在尝试找出提取实体的最佳方法(快速),例如一个月。我使用spaCy 提出了 5 种不同的方法。

初始设置

对于每个解决方案,我都会从初始设置开始

import spacy.lang.en    
nlp = spacy.lang.en.English()
text = 'I am trying to extract January as efficient as possible. But what is the best solution?'

解决方法:使用extension attributes(仅限单token匹配)

import spacy.tokens
NORM_EXCEPTIONS = {
    'jan': 'MONTH', 'january': 'MONTH'
}
spacy.tokens.Token.set_extension('norm', getter=lambda t: NORM_EXCEPTIONS.get(t.text.lower(), t.norm_))
def time_this():
    doc = nlp(text)
    assert [t for t in doc if t._.norm == 'MONTH'] == [doc[5]]

%timeit time_this()

每个循环 76.4 µs ± 169 ns(平均值 ± 标准偏差,7 次运行,每次 10000 个循环)

解决方案:通过entity ruler使用短语匹配器

import spacy.pipeline
ruler = spacy.pipeline.EntityRuler(nlp)
ruler.phrase_matcher = spacy.matcher.PhraseMatcher(nlp.vocab, attr="LOWER")
ruler.add_patterns([{'label': 'MONTH', 'pattern': 'jan'}, {'label': 'MONTH', 'pattern': 'january'}])
nlp.add_pipe(ruler)
def time_this():
    doc = nlp(text)
    assert [t for t in doc.ents] == [doc[5:6]]
%timeit time_this()

每个循环 131 µs ± 579 ns(7 次运行的平均值 ± 标准偏差,每次 10000 次循环)

解决方案:通过实体标尺使用令牌匹配器

import spacy.pipeline
ruler = spacy.pipeline.EntityRuler(nlp)
ruler.add_patterns([{'label': 'MONTH', 'pattern': [{'lower': {'IN': ['jan', 'january']}}]}])
nlp.add_pipe(ruler)
def time_this():
    doc = nlp(text)
    assert [t for t in doc.ents] == [doc[5:6]]
%timeit time_this()

每个循环 72.6 µs ± 76.7 ns(平均值 ± 标准偏差,7 次运行,每次 10000 次循环)

解决方法:直接使用phrase matcher

import spacy.matcher
phrase_matcher = spacy.matcher.PhraseMatcher(nlp.vocab, attr="LOWER")
phrase_matcher.add('MONTH', None, nlp('jan'), nlp('january'))
def time_this():
    doc = nlp(text)
    matches = [m for m in filter(lambda x: x[0] == doc.vocab.strings['MONTH'], phrase_matcher(doc))]
    assert [doc[m[1]:m[2]] for m in matches] == [doc[5:6]]
%timeit time_this()

每个循环 115 µs ± 537 ns(7 次运行的平均值 ± 标准偏差,每次 10000 次循环)

解决方法:直接使用token matcher

import spacy.matcher
matcher = spacy.matcher.Matcher(nlp.vocab)
matcher.add('MONTH', None, [{'lower': {'IN': ['jan', 'january']}}])
def time_this():
    doc = nlp(text)
    matches = [m for m in filter(lambda x: x[0] == doc.vocab.strings['MONTH'], matcher(doc))]
    assert [doc[m[1]:m[2]] for m in matches] == [doc[5:6]]
%timeit time_this()

每个循环 55.5 µs ± 459 ns(7 次运行的平均值 ± 标准偏差,每次 10000 次循环)

结论

自定义属性仅限于单个标记匹配,并且标记匹配器似乎更快,因此似乎更可取。 EntityRuler 似乎是最慢的,这并不奇怪,因为它正在更改 Doc.ents。然而,在Doc.ents 中有匹配项非常方便,因此您可能仍需要考虑这种方法。

我很惊讶令牌匹配器优于短语匹配器。我以为会相反:

如果您需要匹配大型术语列表,您还可以使用 PhraseMatcher 并创建 Doc 对象而不是令牌模式,这总体上效率更高

问题

我是否遗漏了一些重要的东西,或者我可以相信更大规模的分析吗?

【问题讨论】:

    标签: python performance spacy


    【解决方案1】:

    我认为归根结底,这一切都归结为在速度、代码的可维护性以及这段逻辑适合您的应用程序大图的方式之间找到最佳权衡。在文本中查找一些字符串不太可能是您尝试做的最终目标——否则,您可能不会使用 spaCy 并且会坚持使用正则表达式。您的应用程序需要如何“使用”匹配结果以及匹配在更大的上下文中的含义应该会激发您选择的方法。

    正如您在结论中提到的,如果您的匹配项根据定义是“命名实体”,则将它们添加到 doc.ents 非常有意义,甚至可以为您提供一种将逻辑与统计预测相结合的简单方法。即使它增加了更多的开销,它仍然可能胜过任何你必须自己编写的脚手架。

    对于每个解决方案,我都会从初始设置开始

    如果您在同一会话中运行实验,例如在笔记本中,您可能希望在初始设置中包含 Doc 对象的创建。否则,词汇条目的缓存理论上可能意味着nlp(text) 的第一次调用比后续调用慢。不过,这可能微不足道。

    我很惊讶令牌匹配器优于短语匹配器。我以为是相反的

    一种可能的解释是,您正在以非常小的规模和单标记模式分析方法,其中短语匹配器引擎与常规标记匹配器相比并没有真正的优势。另一个因素可能是匹配不同属性(例如LOWER 而不是TEXT/ORTH)需要反映匹配属性值的creating a new Doc during matching。这应该很便宜,但它仍然是创建的一个额外对象。所以一个测试Doc"extract January"实际上会变成"extract january"(当匹配LOWER时)甚至"VERB PROPN"匹配POS。这就是使匹配其他属性起作用的技巧。

    关于PhraseMatcher 工作原理及其机制通常更快的原因的一些背景知识:当您将Doc 对象添加到PhraseMatcher 时,它会在模式中包含的标记上设置标志,表明它们正在匹配给定的模式。然后它调用常规的Matcher 并使用先前设置的标志添加基于令牌的模式。当你匹配时,spaCy 只需要检查标志而不检索任何令牌属性——这应该使匹配本身在规模上显着加快。

    这实际上带来了另一种方法,您可以进行分析以进行比较:使用Vocab.add_flag 在相应的词位上设置一个布尔标志(词汇中的条目,而不是上下文相关的标记)。词汇条目被缓存,因此您应该只需要为像"january" 这样的词位计算一次标志。但是,这种方法仅对单个令牌才真正有意义,因此相对有限。

    我是否遗漏了一些重要的东西,或者我可以相信更大规模的分析吗?

    如果您想获得任何有意义的见解,您应该至少在中等规模上进行基准测试。您不希望在同一个小示例上循环 10000 次,而是在每次测试仅处理 一次 的数据集上进行基准测试。例如,数百个与您实际使用的数据相似的文档。缓存效果(在 spaCy 中,还有你的 CPU 中)、内存分配的差异等等都会产生影响。

    最后,直接使用 spaCy 的Cython API 总是最快的。因此,如果速度是您的首要关注点并且您想要优化的全部内容,那么 Cython 将是您的最佳选择。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-08-17
      相关资源
      最近更新 更多