У вас правильная идея, но проблема здесь заключается в 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
.