如何模拟spacy模型/ Doc对象进行单元测试?

时间:2019-06-23 22:43:20

标签: unit-testing spacy

加载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对象用于实体的单元测试?

1 个答案:

答案 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")