Spacy Entity Rule не работает для кардинала (номер социального страхования) - PullRequest
1 голос
/ 01 октября 2019

Я использовал Entity Rule, чтобы добавить новый ярлык для номера социального страхования. Я даже установил overwrite_ents = true, но он все еще не распознает

Я подтвердил правильность регулярного выражения. не уверен, что еще мне нужно сделать, я пытался раньше = "нер", но тот же результат

text = "My name is yuyyvb and I leave on 605 W Clinton Street. My social security 690-96-4032"
nlp = spacy.load("en_core_web_sm")
ruler = EntityRuler(nlp, overwrite_ents=True)
ruler.add_patterns([{"label": "SSN", "pattern": [{"TEXT": {"REGEX": r"\d{3}[^\w]\d{2}[^\w]\d{4}"}}]}])
nlp.add_pipe(ruler)
doc  = nlp(text)
for ent in doc.ents:
    print("{} {}".format(ent.text, ent.label_))

1 Ответ

0 голосов
/ 02 октября 2019

На самом деле ваш SSN токенизируется посредством spacy на 5 блоков:

print([token.text for token in nlp("690-96-4032")])
# => ['690', '-', '96', '-', '4032']

Так что, либо используйте собственный токенизатор, где - между цифрами не выделяется в качестве отдельного токена, либо -проще - создайте шаблон для последовательных 5 токенов:

patterns = [{"label": "SSN", "pattern": [{"TEXT": {"REGEX": r"^\d{3}$"}}, {"TEXT": "-"}, {"TEXT": {"REGEX": r"^\d{2}$"}}, {"TEXT": "-"}, {"TEXT": {"REGEX": r"^\d{4}$"}} ]}]

Полная демонстрация простоты:

import spacy
from spacy.pipeline import EntityRuler

nlp = spacy.load("en_core_web_sm")
ruler = EntityRuler(nlp, overwrite_ents=True)
patterns = [{"label": "SSN", "pattern": [{"TEXT": {"REGEX": r"^\d{3}$"}}, {"TEXT": "-"}, {"TEXT": {"REGEX": r"^\d{2}$"}}, {"TEXT": "-"}, {"TEXT": {"REGEX": r"^\d{4}$"}} ]}]
ruler.add_patterns(patterns)
nlp.add_pipe(ruler)

text = "My name is yuyyvb and I leave on 605 W Clinton Street. My social security 690-96-4032"
doc = nlp(text)
print([(ent.text, ent.label_) for ent in doc.ents])
# => [('605', 'CARDINAL'), ('690-96-4032', 'SSN')]

Итак, {"TEXT": {"REGEX": r"^\d{3}$"}} соответствует токену, который состоит только из трех цифр, {"TEXT": "-"} - это - символ и т. д.

Переопределение токенизации чисел с переносами с помощью spacy

Если вас интересует, как этого можно добиться путем переопределения токенизации по умолчанию, обратите внимание на infixes: регулярное выражение r"(?<=[0-9])[+\-\*^](?=[0-9-])" делает пространственное разбиение разделенных дефисом чисел на отдельные токены. Чтобы подстроки, подобные 1-2-3 и 1-2, были токенизированы как одиночные токены, удалите - из регулярного выражения. Ну, вы не можете этого сделать, это гораздо сложнее: вам нужно заменить его на 2 регулярных выражения: r"(?<=[0-9])[+*^](?=[0-9-])" и r"(?<=[0-9])-(?=-)", потому что - проверяется также между цифрой ((?<=[0-9])) идефис (см. (?=[0-9-])).

Итак, все это будет выглядеть как

import spacy
from spacy.tokenizer import Tokenizer
from spacy.pipeline import EntityRuler
from spacy.util import compile_infix_regex

def custom_tokenizer(nlp):
    # Take out the existing rule and replace it with a custom one:
    inf = list(nlp.Defaults.infixes)
    inf.remove(r"(?<=[0-9])[+\-\*^](?=[0-9-])")
    inf = tuple(inf)
    infixes = inf + tuple([r"(?<=[0-9])[+*^](?=[0-9-])", r"(?<=[0-9])-(?=-)"]) 
    infix_re = compile_infix_regex(infixes)

    return Tokenizer(nlp.vocab, prefix_search=nlp.tokenizer.prefix_search,
                                suffix_search=nlp.tokenizer.suffix_search,
                                infix_finditer=infix_re.finditer,
                                token_match=nlp.tokenizer.token_match,
                                rules=nlp.Defaults.tokenizer_exceptions)

nlp = spacy.load("en_core_web_sm")
nlp.tokenizer = custom_tokenizer(nlp)
ruler = EntityRuler(nlp, overwrite_ents=True)
ruler.add_patterns([{"label": "SSN", "pattern": [{"TEXT": {"REGEX": r"^\d{3}\W\d{2}\W\d{4}$"}}]}])
nlp.add_pipe(ruler)

text = "My name is yuyyvb and I leave on 605 W Clinton Street. My social security 690-96-4032. Some 9---al"
doc = nlp(text)
print([t.text for t in doc])
# =>  ['My', 'name', 'is', 'yuyyvb', 'and', 'I', 'leave', 'on', '605', 'W', 'Clinton', 'Street', '.', 'My', 'social', 'security', '690-96-4032', '.', 'Some', '9', '-', '--al']
print([(ent.text, ent.label_) for ent in doc.ents])
# => [('605', 'CARDINAL'), ('690-96-4032', 'SSN'), ('9', 'CARDINAL')]

Если вы пропустите r"(?<=[0-9])-(?=-)", ['9', '-', '--al'] превратится в '9---al'.

ПРИМЕЧАНИЕ необходимо использовать ^\d{3}\W\d{2}\W\d{4}$ регулярное выражение: ^ и $ совпадать с началом и концом токена (в противном случае частично совпадающие токены также будут идентифицироваться как SSN)) и [^\w] равно \W.

...