Es importante comprender cómo usar reglas de Spacy para identificar patrones dentro de algún texto. Hay entidades como tiempos, fechas, ibans y correos electrónicos que siguen una estructura estricta, por lo que es posible identificarlas con reglas deterministas, por ejemplo, utilizando Expresiones regulares (regio).
Spacy simplifica el uso de reglas haciéndolos más legibles por humanos, por lo que en lugar de símbolos extraños, usará descripciones reales utilizando la clase Matcher.
Coincidencia de token
Una regex es una secuencia de caracteres que especifica un patrón de búsqueda. Hay una biblioteca incorporada de Python para trabajar con reglas llamadas re: https://docs.python.org/3/library/re.html
Veamos un ejemplo.
"Marcello Politi"
"Marcello Politi"
"Marcello Danilo Politi"
reg = r"Marcello\s(Danilo\a)?Politi"
En este ejemplo, el patrón REG captura todas las cadenas anteriores. Este patrón dice que “Marcello” puede ser seguido opcionalmente por la palabra “Danilo” (ya que estamos usando el símbolo “?”). Además, el símbolo “\ s” dice que no importa entre las palabras que usamos un espacio, una pestaña o múltiples espacios.
El problema con las reglas, y la razón por la cual muchos programadores no los aman, es que son difíciles de leer. Es por eso que Spacy proporciona una alternativa limpia y de nivel de producción con el Clase Matcher.
Importemos la clase y veamos cómo podemos usarla. (Explicaré qué tramo es más adelante).
import spacy
from spacy.matcher import Matcher
from spacy.tokens import Span
nlp = spacy.load("en_core_web_sm")
Ahora podemos definir un patrón que coincida con algunos saludos matutinos, y etiquetamos este patrón “Morninggreeting”. Definir un patrón con Matcher es sencillo. En este patrón, esperamos una palabra que, cuando se convierta en minúsculas, coincida con la palabra “buena”, luego la misma para “mañana”, y luego aceptamos tan puntuación al final.
matcher = Matcher(nlp.vocab)
pattern = [
{"LOWER": "good"},
{"LOWER": "morning"},
{"IS_PUNCT": True},
]
matcher.add("monrningGreeting", [pattern])
Un tramo es una oración singular, por lo que la matcher puede encontrar el punto de inicio y finalización de múltiples tramos que iteramos con un bucle for.
Agregamos todos los tramos en una lista y asignamos la lista al doc.spans[“sc”]. Entonces podemos usar la desplazamiento para visualizar el tramo
doc = nlp("Good morning, My name is Marcello Politi!")
matches = matcher(doc)
spans = []
for match_id, start, end in matches:
spans.append(
Span(doc, start, end, nlp.vocab.strings[match_id])
)
doc.spans["sc"] = spans
from spacy import displacy
displacy.render(doc, style = "span")
¡Un objeto Matcher acepta más de un patrón a la vez!
Definamos un greeting de la mañana y una grega por la noche.
pattern1 = [
{"LOWER": "good"},
{"LOWER": "morning"},
{"IS_PUNCT": True},
]
pattern2 = [
{"LOWER": "good"},
{"LOWER": "evening"},
{"IS_PUNCT": True},
]
Luego agregamos estos patrones a la matcher.
doc = nlp("Good morning, I want to attend the lecture. I will then say good evening!")
matcher = Matcher(nlp.vocab)
matcher.add("morningGreetings", [pattern1])
matcher.add("eveningGreetings", [pattern2])
matches = matcher(doc)
Como antes, iteramos sobre los tramos y los mostramos.
spans = []
for match_id, start, end in matches:
spans.append(
Span(doc, start, end, nlp.vocab.strings[match_id])
)
doc.spans["sc"] = spans
from spacy import displacy
displacy.render(doc, style = "span")
La sintaxis compatible con Spacy es enorme. Aquí reporto algunos de los patrones más comunes.
Atributos basados en texto
| Atributo | Descripción | Ejemplo |
|---|---|---|
"ORTH" |
Texto literal exacto | {"ORTH": "Hello"} |
"LOWER" |
Forma minúscula del token | {"LOWER": "hello"} |
"TEXT" |
Lo mismo que "ORTH" |
{"TEXT": "World"} |
"LEMMA" |
Lema (forma base) del token | {"LEMMA": "run"} |
"SHAPE" |
Forma de la palabra (por ejemplo, Xxxx, dd) |
{"SHAPE": "Xxxx"} |
"PREFIX" |
Primer carácter (s) del token | {"PREFIX": "un"} |
"SUFFIX" |
Últimos personajes del token | {"SUFFIX": "ing"} |
Características lingüísticas
| Atributo | Descripción | Ejemplo |
|---|---|---|
"POS" |
Etiqueta universal | {"POS": "NOUN"} |
"TAG" |
Etiqueta POS detallada | {"TAG": "NN"} |
"DEP" |
Dependencia sintáctica | {"DEP": "nsubj"} |
"ENT_TYPE" |
Tipo de entidad nombrado | {"ENT_TYPE": "PERSON"} |
Banderas booleanas
| Atributo | Descripción | Ejemplo |
|---|---|---|
"IS_ALPHA" |
Token consiste en caracteres alfabéticos | {"IS_ALPHA": True} |
"IS_ASCII" |
Token consiste en caracteres ASCII | {"IS_ASCII": True} |
"IS_DIGIT" |
Token es un dígito | {"IS_DIGIT": True} |
"IS_LOWER" |
El token es minúscula | {"IS_LOWER": True} |
"IS_UPPER" |
El token está en mayúscula | {"IS_UPPER": True} |
"IS_TITLE" |
Token está en caso de título | {"IS_TITLE": True} |
"IS_PUNCT" |
Token es puntuación | {"IS_PUNCT": True} |
"IS_SPACE" |
Token es Whitpace | {"IS_SPACE": True} |
"IS_STOP" |
Token es una palabra de parada | {"IS_STOP": True} |
"LIKE_NUM" |
Token parece un número | {"LIKE_NUM": True} |
"LIKE_EMAIL" |
Token parece una dirección de correo electrónico | {"LIKE_EMAIL": True} |
"LIKE_URL" |
Token parece una URL | {"LIKE_URL": True} |
"IS_SENT_START" |
El token está en el comienzo de la oración | {"IS_SENT_START": True} |
Operadores
Se usa para repetir o hacer que los patrones opcieran:
| Operador | Descripción | Ejemplo |
|---|---|---|
"OP" |
Operador de patrones: | |
"?" – cero o uno |
{"LOWER": "is", "OP": "?"} |
|
"*" – cero o más |
{"IS_DIGIT": True, "OP": "*"} |
|
"+" – uno o más |
{"IS_ALPHA": True, "OP": "+"} |
Ejemplo:
¿Qué es un patrón que coincide con una cadena como: “Tengo 2 manzanas rojas”, “Compramos 5 plátanos verdes”, o “encontraron 3 naranjas maduras” ?
Requisitos del patrón:
- Pronombre sujeto (por ejemplo, “i”, “nosotros”, “ellos”)
- Un verbo (por ejemplo, “tener”, “comprado”, “encontrado”)
- Un número (dígito o escrito, como “2”, “cinco”)
- Un adjetivo opcional (por ejemplo, “rojo”, “maduro”)
- Un sustantivo plural (fruta, por ejemplo)
pattern = [
{"POS": "PRON"}, # Subject pronoun: I, we, they
{"POS": "VERB"}, # Verb: have, bought, found
{"LIKE_NUM": True}, # Number: 2, five
{"POS": "ADJ", "OP": "?"}, # Optional adjective: red, ripe
{"POS": "NOUN", "TAG": "NNS"} # Plural noun: apples, bananas
]
Patrones con fraseMatcher
Trabajamos en un dominio vertical, como médico o científico, generalmente tenemos un conjunto de palabras que Spacy podría no tener en cuenta, y queremos encontrarlas en algún texto.
La clase de Phrasematcher es la solución espacial para Comparación de texto con diccionarios largos. El uso es bastante similar a la clase Matcher, pero además, necesitamos definir la lista de términos importantes que queremos rastrear. Comencemos con las importaciones.
import spacy
from spacy.tokens import Span
from spacy.matcher import PhraseMatcher
from spacy import displacy
nlp = spacy.load("en_core_web_sm")
Ahora definimos nuestro Matcher y nuestra lista de palabras, y le decimos a Spacy que cree un patrón solo para reconocer esa lista. Aquí, quiero identificar los nombres de los líderes y lugares tecnológicos.
terms = ["Sundar Pichai", "Tim Cook", "Silicon Valley"]
matcher = PhraseMatcher(nlp.vocab)
patterns = [nlp.make_doc(text) for text in terms]
matcher.add("TechLeadersAndPlaces", patterns)
Finalmente verifique los partidos.
doc = nlp("Tech CEOs like Sundar Pichai and Tim Cook met in Silicon Valley to discuss AI regulation.")
matches = matcher(doc)
spans= []
for match_id, start, end in matches:
pattern_name = nlp.vocab.strings[match_id]
spans.append(Span(doc, start, end, pattern_name))
doc.spans["sc"] = spans
displacy.render(doc, style = "span")
Podemos mejorar las capacidades del PhraseMatcher agregando algunos atributos. Por ejemplo, si necesitamos cachorar las direcciones IP en un texto, tal vez en algunos registros, no podemos escribir todas las combinaciones posibles de las direcciones IP, eso sería una locura. Pero podemos pedirle a Spacy que atraiga la forma de algunas cadenas IP y verifique la misma forma en un texto.
matcher = PhraseMatcher(nlp.vocab, attr= "SHAPE")
ips = ["127.0.0.0", "127.256.0.0"]
patterns = [nlp.make_doc(ip) for ip in ips]
matcher.add("IP-pattern", patterns)
doc = nlp("This fastAPI server can run on 192.1.1.1 or on 192.170.1.1")
matches = matcher(doc)
spans= []
for match_id, start, end in matches:
pattern_name = nlp.vocab.strings[match_id]
spans.append(Span(doc, start, end, pattern_name))
doc.spans["sc"] = spans
displacy.render(doc, style = "span")
Extracción iban
El IBAN es una información importante que a menudo necesitamos extraer cuando trabajamos en los campos financieros, por ejemplo, si analizan las facturas o transacciones. Pero, ¿cómo podemos hacer eso?
Cada Iban tiene un formato de número internacional fijo, comenzando con dos letras para identificar al país.
Estamos seguros de que cada Iban comienza con dos letras mayúsculas xx seguido de al menos dos dígitos DD. Entonces podemos escribir un patrón para identificar esta primera parte del Iban.
{"SHAPE":"XXdd"}
Todavía no ha hecho. Para el resto del bloque podríamos tener de 1 a 4 dígitos que podemos expresar con el símbolo “\ D {1,4}”.
{"TEXT":{"REGEX:"\d{1,4"}}
Podemos tener uno o más de estos bloques, para que podamos usar el operador “+” para identificarlos todos.
{"TEXT":{"REGEX":"\d{1,4}, "OP":"+"}
Ahora podemos combinar la forma con la identificación de bloques.
pattern =[
{"SHAPE":"XXdd"},
{"TEXT":{"REGEX":"\d{1,4}, "OP":"+"}
]
matcher = Matcher(nlp.vocab)
matcher.add("IBAN", [patttern])
¡Ahora usemos esto!
text = "Please transfer the money to the following account: DE44 5001 0517 5407 3249 31 by Monday."
doc = nlp(text)
matches = matcher(doc)
spans = []
for match_id, start, end in matches:
span = Span(doc, start, end, label=nlp.vocab.strings[match_id])
spans.append(span)
doc.spans["sc"] = spans
displacy.render(doc, style="span")
Pensamientos finales
Espero que este artículo te haya ayudado a ver cuánto podemos hacer en la PNL sin usar siempre modelos enormes. Muchas veces, solo necesitamos encontrar cosas que sigan reglas, como fechas, ibans, nombres o saludos, y para eso, Spacy nos ofrece excelentes herramientas como Matcher y PhraseMatcher.
En mi opinión, trabajar con patrones como estos es una buena manera de comprender mejor cómo se estructura el texto. Además, hace que su trabajo sea más eficiente cuando no desea desperdiciar recursos en algo simple.
Todavía creo que Regex es poderoso, pero a veces difícil de leer y depurar. Con Spacy, las cosas se ven más claras y más fáciles de mantener en un proyecto real.
LinkedIn ️ | X (Twitter) | Sitio web