问题
I have used Entity Rule to add new label for social security number. I even set overwrite_ents=true but it still does't recognize
I verified regular expression is correct. not sure what else I need to do I tried before="ner" but same result
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:
Actually, the SSN you have is tokenized by spacy into 5 chunks:
print([token.text for token in nlp("690-96-4032")])
# => ['690', '-', '96', '-', '4032']
So, either use a custom tokenizer where -
between digits is not split out as a separate token, or - simpler - create a pattern for the consecutive 5 tokens:
patterns = [{"label": "SSN", "pattern": [{"TEXT": {"REGEX": r"^\d{3}$"}}, {"TEXT": "-"}, {"TEXT": {"REGEX": r"^\d{2}$"}}, {"TEXT": "-"}, {"TEXT": {"REGEX": r"^\d{4}$"}} ]}]
Full spacy demo:
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')]
So, {"TEXT": {"REGEX": r"^\d{3}$"}}
matches a token that only consists of three digits, {"TEXT": "-"}
is a -
char, etc.
Overriding hyphenated numbers tokenization with spacy
If you are interested in how it can be achieved by overriding default tokenization, pay attention to the infixes: the r"(?<=[0-9])[+\-\*^](?=[0-9-])"
regex make spacy split hyphen-separated numbers into separate tokens. To make 1-2-3
and 1-2
like substrings get tokenized as single tokens, remove the -
from the regex. Well, you can't do that, this is much trickier: you need to replace it with 2 regexps: r"(?<=[0-9])[+*^](?=[0-9-])"
and r"(?<=[0-9])-(?=-)"
because of the fact the -
is checked also between a digit ((?<=[0-9])
) and a hyphen (see (?=[0-9-])
).
So, the whole thing will look like
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')]
If you leave out r"(?<=[0-9])-(?=-)"
, the ['9', '-', '--al']
will turn into '9---al'
.
NOTE you need to use ^\d{3}\W\d{2}\W\d{4}$
regex: ^
and $
match start and end of the token (as otherwise, partially matched tokens will also be identified as SSNs) and [^\w]
is equal to \W
.
来源:https://stackoverflow.com/questions/58175591/spacy-entity-rule-doesnt-work-for-cardinal-social-security-number