加载spacy模型会减慢我的单元测试的运行速度。有没有办法模拟spacy模型或Doc对象以加快单元测试?
当前慢速测试的示例
import spacy
nlp = spacy.load("en_core_web_sm")
def test_entities():
text = u"Google is a company."
doc = nlp(text)
assert doc.ents[0].text == u"Google"
基于文档,我的方法是
手动构造Vocab和Doc并将实体设置为元组。
from spacy.vocab import Vocab
from spacy.tokens import Doc
def test()
alphanum_words = u"Google Facebook are companies".split(" ")
labels = [u"ORG"]
words = alphanum_words + [u"."]
spaces = len(words) * [True]
spaces[-1] = False
spaces[-2] = False
vocab = Vocab(strings=(alphanum_words + labels))
doc = Doc(vocab, words=words, spaces=spaces)
def get_hash(text):
return vocab.strings[text]
entity_tuples = tuple([(get_hash(labels[0]), 0, 1)])
doc.ents = entity_tuples
assert doc.ents[0].text == u"Google"
是否有更干净的Pythonic解决方案来模拟spacy对象用于实体的单元测试?
答案 0 :(得分:2)
这实际上是一个很好的问题!我想说您的直觉绝对是正确的:如果您需要的只是处于给定状态并带有给定注释的Doc
对象,请始终尽可能手动创建它。并且除非您明确地测试统计模型,否则请避免将其加载到单元测试中。它使测试变慢,并且引入了太多不必要的差异。这也非常符合单元测试的理念:您想一次针对一件事编写独立的测试(不是一件事情,而是一堆第三方库代码以及统计信息)型号)。
一些常规提示和想法:
Doc
。避免加载模型或Language
子类。doc.text
,否则您将不必设置spaces
。实际上,我在编写的80%的测试中都忽略了这一点,因为它只有在将令牌重新放在一起时才真正有意义。Doc
对象,则可以考虑使用实用程序功能,类似于我们在spaCy测试套件中使用的get_doc
helper。 (该功能还显示了如何在需要时手动设置各个注释。)Vocab
。根据您要测试的内容,您可能需要显式使用English
词汇。在spaCy测试套件中,我们通过在conftest.py
中设置en_vocab
fixture来完成此操作。doc.ents
设置为元组列表之外,还可以使其成为Span
对象的列表。这看起来更直接,更易于阅读,在spaCy v2.1 +中,您还可以将字符串作为标签传递:def test_entities(en_vocab):
doc = Doc(en_vocab, words=["Hello", "world"])
doc.ents = [Span(doc, 0, 1, label="ORG")]
assert doc.ents[0].text == "Hello"
English
之类的语言类中进行测试,请将其放入会话范围的固定装置中。这意味着每个会话只会加载一次,而不是每个测试一次。语言类是延迟加载的,并且可能还需要一些时间才能加载,具体取决于它们所包含的数据。因此,您只想执行一次。# Note: You probably don't have to do any of this, unless you're testing your
# own custom models or language classes.
@pytest.fixture(scope="session")
def en_core_web_sm():
return spacy.load("en_core_web_sm")
@pytest.fixture(scope="session")
def en_lang_class():
lang_cls = spacy.util.get_lang_class("en")
return lang_cls()
def test(en_lang_class):
doc = en_lang_class("Hello world")