aphp / edsnlp

Modular, fast NLP framework, compatible with Pytorch and spaCy, offering tailored support for French clinical notes.
https://aphp.github.io/edsnlp/
BSD 3-Clause "New" or "Revised" License
116 stars 30 forks source link

TNM doesn't match regex in sentence #175

Closed PPPinson closed 11 months ago

PPPinson commented 1 year ago

Description

Le pipeline TNM ne renvoie pas les scores suivis par un espace. (match avec "aTxN1M0\nanything" mais pas avec "aTxN1M0 anything")

Plus généralement spacy n'a pas l'air ok avec les regex qui finissent par un espace ?

How to reproduce the bug

import spacy
nlp = spacy.blank("fr")
nlp.add_pipe("eds.TNM")

doc = nlp("aTxN1M0")
print(doc.ents)  # works
# (aTxN1M0,)
doc = nlp("aTxN1M0\n")
print(doc.ents)  # works
# (aTxN1M0,)
doc = nlp("aTxN1M0 ")
print(doc.ents)  # doesn't work
# ()

On peut simplifier le problème sous la forme:

import spacy
from edsnlp.matchers.regex import RegexMatcher
nlp = spacy.blank("fr")

matcher = RegexMatcher(attr="TEXT", alignment_mode="strict")
matcher.add("tnm", [r"a ?"]) #regex simple : _a_ peut être suivi par un espace
nlp = spacy.blank("fr")

doc = nlp("test: a") # exemple simple sans espace à la fin
for r in matcher(doc, as_spans=True,return_groupdict=True):
    print(r) 
#(a, {})
doc = nlp("test: a ")  # exemple simple avec espace à la fin
for r in matcher(doc, as_spans=True,return_groupdict=True):
    print(r) 
#ne renvoie rien

Pourtant, un simple match de regex fonctionne:

import re
re.search('a ?', "test: a ")
#<re.Match object; span=(6, 8), match='a '>

quelle est la différence entre re et spacy qui justifie ce comportement ?

Your Environment

PPPinson commented 1 year ago

Le problème semble venir du mode d'alignement: ça marche en remplaçant self.regex_matcher = RegexMatcher(attr=attr, alignment_mode="strict") par self.regex_matcher = RegexMatcher(attr=attr, alignment_mode="expand") dans edsnlp/pipelines/ner/scores/tnm/tnm.py. Je ne suis pas certain de bien comprendre le mode d'alignement spacy, qu'est ce qui a justifié de mettre "strict" pour le pipeline TNM ? @etienneguevel une idée ?

etienneguevel commented 1 year ago

Salut Pierre, Je ne suis pas à l'origine du pipeline eds.tnm et je ne sais pas pourquoi "strict" a été choisi à l'époque. J'ai testé ta proposition sur les cr anapathes de ma cohorte et je n'ai pas observé de nouveaux matchs incorrects. A part contre indication de @percevalw je serai assez pour intégrer ta proposition !

Thomzoy commented 1 year ago

Le problème semble venir du mode d'alignement: ça marche en remplaçant self.regex_matcher = RegexMatcher(attr=attr, alignment_mode="strict") par self.regex_matcher = RegexMatcher(attr=attr, alignment_mode="expand") dans edsnlp/pipelines/ner/scores/tnm/tnm.py. Je ne suis pas certain de bien comprendre le mode d'alignement spacy, qu'est ce qui a justifié de mettre "strict" pour le pipeline TNM ? @etienneguevel une idée ?

Le mode d'alignement permet de dire à spaCy comment passer d'un tuple (start, end) (extrait via regex) à un vrai objet Span. Dans le cas d'un alignement strict il faut que le tuple corresponde exactement à un Span, ce qui n'est pas toujours le cas. avec alignment_mode=expand cette contrainte est relaxée