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

swa*_*is8 9 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"
Run Code Online (Sandbox Code Playgroud)

根据文档,我的方法是

手动构建 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"

Run Code Online (Sandbox Code Playgroud)

是否有更简洁的 Pythonic 解决方案来模拟用于实体单元测试的 spacy 对象?

Ine*_*ani 14

这实际上是一个很好的问题!我想说您的直觉绝对是正确的:如果您只需要一个Doc处于给定状态和给定注释的对象,请始终尽可能手动创建它。除非您明确测试统计模型,否则请避免在单元测试中加载它。它使测试变慢,并引入了太多不必要的差异。这也非常符合单元测试的理念:您希望一次一件事情编写独立的测试(不是一件事情加上一堆第三方库代码加上一个统计模型)。

一些一般提示和想法:

  • 如果可能,请始终Doc手动构建一个。避免加载模型或Language子类。
  • 除非你的应用程序或测试具体需要doc.text,你不必须设置spaces。事实上,我在我编写的大约 80% 的测试中都忽略了这一点,因为只有当你将令牌重新组合在一起时,它才会真正变得相关。
  • 如果你需要Doc在你的测试套件中创建很多对象,你可以考虑使用一个实用函数,类似于我们在 spaCy 测试套件中使用的get_doc助手。(该功能还向您展示了如何手动设置单个注释,以防万一。)
  • 为共享对象使用(会话范围的)固定装置,例如Vocab. 根据您要测试的内容,您可能希望明确使用该English词汇。在spaCy测试套件,我们通过建立这样做en_vocab灯具conftest.py
  • 除了将 设置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"
Run Code Online (Sandbox Code Playgroud)
  • 如果您确实需要测试模型(例如,在确保自定义模型按预期加载和运行的测试套件中)或语言类(如 )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")
Run Code Online (Sandbox Code Playgroud)