Как макетировать пространственные модели / объекты Doc для юнит-тестов? - PullRequest
1 голос
/ 24 июня 2019

Загрузка просторных моделей замедляет выполнение моих модульных тестов. Есть ли способ макетировать пространственные модели или объекты 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 для насмешливых пространственных объектов для юнит-тестов для сущностей?

1 Ответ

2 голосов
/ 24 июня 2019

На самом деле это отличный вопрос!Я бы сказал, что ваш инстинкт определенно прав: если все, что вам нужно, это Doc объект в заданном состоянии и с заданными аннотациями, всегда создавайте его вручную, где это возможно.И если вы не явно тестируете статистическую модель, не загружайте ее в свои модульные тесты.Это замедляет тестирование и вносит слишком много ненужных отклонений.Это также очень согласуется с философией модульного тестирования: вы хотите писать независимые тесты для по одной вещи за (не одна вещь плюс куча стороннего библиотечного кода плюс статистическая модель).

Некоторые общие советы и идеи:

  • Если возможно, всегда создавайте Doc вручную.Избегайте загрузки моделей или Language подклассов.
  • Если вашему приложению или тесту не требуется конкретно doc.text, не нужно устанавливать spaces.Фактически, я пропускаю это примерно в 80% написанных мною тестов, потому что это действительно становится актуальным, только когда вы собираете токены обратно.
  • Если вам нужно создать много Docобъекты в вашем тестовом наборе, вы можете рассмотреть возможность использования служебной функции, аналогичной get_doc helper , который мы используем в тестовом наборе spaCy.(Эта функция также показывает вам, как отдельные аннотации устанавливаются вручную, если вам это нужно.)
  • Использование (в рамках сеанса) осветителей для общих объектов, таких как Vocab.В зависимости от того, что вы тестируете, вы можете явно использовать English vocab.В тестовом наборе 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"
  • Если вам нужнопротестируйте модель (например, в наборе тестов, который гарантирует, что ваши пользовательские модели загружаются и работают должным образом) или языковой класс, такой как 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")
...