【发布时间】: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