Паттерны с ENT_TYPE из вручную помеченного диапазона не работают - PullRequest
1 голос
/ 26 мая 2020

В качестве альтернативы выполнению этого: Шаблоны с несколькими терминами в атрибуте IN

Я написал следующий код, чтобы сопоставить фразы, пометить их, а затем использовать в EntityRuler шаблоны:

# %%
import spacy
from spacy.matcher import PhraseMatcher
from spacy.pipeline import EntityRuler
from spacy.tokens import Span

class PhraseRuler(object):
    name = 'phrase_ruler'

    def __init__(self, nlp, terms, label):
        patterns = [nlp(term) for term in terms]
        self.matcher = PhraseMatcher(nlp.vocab)
        self.matcher.add(label, None, *patterns)

    def __call__(self, doc):
        matches = self.matcher(doc)
        spans = []
        for label, start, end in matches:
            span = Span(doc, start, end, label=label)
            spans.append(span)
        doc.ents = spans
        return doc

nlp = spacy.load("en_core_web_lg")

entity_matcher = PhraseRuler(nlp, ["Best Wishes", "Warm Welcome"], "GREETING")
nlp.add_pipe(entity_matcher, before="ner")


ruler = EntityRuler(nlp)
patterns = [{"label": "SUPER_GREETING", "pattern": [{"LOWER": "super"}, {"ENT_TYPE": "GREETING"}]}]
ruler.add_patterns(patterns)
#ruler.to_disk("./data/patterns.jsonl")
nlp.add_pipe(ruler)

print(nlp.pipe_names) 

doc = nlp("Mary said Best Wishes and I said super Warm Welcome.")
print(doc.to_json())

К сожалению, это не работает, так как не возвращает мои SUPER_GREETING:

'ents': [
   {'start': 0, 'end': 4, 'label': 'PERSON'}, 
   {'start': 10, 'end': 21, 'label': 'GREETING'}, 
   {'start': 39, 'end': 51, 'label': 'GREETING'}
]

Что я делаю не так? Как исправить?

1 Ответ

2 голосов
/ 26 мая 2020

У вас правильная идея, но проблема здесь заключается в c внутреннем выборе дизайна в spaCy, что любой токен может быть частью только одной именованной сущности. Таким образом, «Теплое приветствие» не может быть одновременно «ПРИВЕТСТВИЕМ» и частью «SUPER_GREETING».

Один из способов обойти это - использовать пользовательские расширения . Например, одним из решений было бы сохранить бит GREETING на уровне токена:

Token.set_extension("mylabel", default="")

А затем мы настраиваем PhraseRuler.__call__ так, чтобы он не записывал в doc.ents, а вместо этого делал следующее:

for token in span:
    token._.mylabel = "MY_GREETING"

Теперь мы можем переписать шаблон SUPER_GREETING на:

patterns = [{"label": "SUPER_GREETING", "pattern": [{"LOWER": "super"}, {"_": {"mylabel": "MY_GREETING"}, "OP": "+"}]}]

, который будет соответствовать «super», за которым следует один или несколько токенов «MY_GREETING». Он будет жадно соответствовать и вывести «super Warm Welcome» как попадание.

Вот полученный фрагмент кода, начиная с вашего кода и выполняя настройки, как описано:

    Token.set_extension("mylabel", default="")

    class PhraseRuler(object):
        name = 'phrase_ruler'

        def __init__(self, nlp, terms, label):
            patterns = [nlp(term) for term in terms]
            self.matcher = PhraseMatcher(nlp.vocab)
            self.matcher.add(label, None, *patterns)

        def __call__(self, doc):
            matches = self.matcher(doc)
            for label, start, end in matches:
                span = Span(doc, start, end, label=label)
                for token in span:
                    token._.mylabel = "MY_GREETING"
            return doc

    nlp = spacy.load("en_core_web_lg")

    entity_matcher = PhraseRuler(nlp, ["Best Wishes", "Warm Welcome"], "GREETING")
    nlp.add_pipe(entity_matcher, name="entity_matcher", before="ner")

    ruler = EntityRuler(nlp)
    patterns = [{"label": "SUPER_GREETING", "pattern": [{"LOWER": "super"}, {"_": {"mylabel": "MY_GREETING"}, "OP": "+"}]}]
    ruler.add_patterns(patterns)
    nlp.add_pipe(ruler, after="entity_matcher")

    print(nlp.pipe_names)

    doc = nlp("Mary said Best Wishes and I said super Warm Welcome.")
    print("TOKENS:")
    for token in doc:
        print(token.text, token._.mylabel)
    print()

    print("ENTITIES:")
    for ent in doc.ents:
        print(ent.text, ent.label_)

Что выдает

TOKENS:
Mary 
said 
Best MY_GREETING
Wishes MY_GREETING
and 
I 
said 
super 
Warm MY_GREETING
Welcome MY_GREETING
. 

ENTITIES:
Mary PERSON
super Warm Welcome SUPER_GREETING

Возможно, это не совсем то, что вам нужно / нужно, но я надеюсь, что это поможет вам продвинуться вперед с альтернативным решением для вашего конкретного c варианта использования. Если вам нужны обычные "ПРИВЕТСТВУЮЩИЕ" промежутки в финальном doc.ents, возможно, вы сможете повторно собрать их в постобработке после запуска EntityRuler, например, переместив настраиваемые атрибуты в doc.ents, если они этого не делают. перекрытия, или сохраняя где-нибудь в кэше spans.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...