El mapa del PLN: qué hacen spaCy, NLTK y FreeLing, y cómo lo hacemos nuestro
Tres bibliotecas cargan hoy casi todo el procesamiento del lenguaje en español del mundo: spaCy, NLTK y FreeLing. Las hemos montado como satélites para medirlas. Este ensayo es el mapa: función por función, qué hace cada una, cómo lo hace a grandes rasgos, con qué licencia, qué tenemos ya nosotros y dónde podemos hacerlo mejor —o por qué, honestamente, en algún sitio no vamos a poder. No es un plan para copiar: es un plan para rehacer la idea a nuestra manera, con datos libres y una API llana que se pueda explicar.
No necesitamos copiar; necesitamos ver la funcionalidad. Como hicimos con los diccionarios, con Molinarium y con Onoma: mirar qué resuelve cada función —su entrada, su salida, los casos que acierta y los que falla en español— y no su código. Es más que una cautela legal (el código de FreeLing es AGPL, y ahí la línea es tajante: se reimplementa la idea, nunca se copia una línea): es que el valor para un hispanohablante no está en el algoritmo ajeno, sino en tener datos propios —el diccionario, el conjugador, el silabeador, el Molino— y una salida que podamos justificar palabra por palabra.
La meta es clara: cuando lo nuestro esté, los satélites se quitan. Son andamio para medir, no producto. Y como la API es gratis y libre, todo lo que hagamos tiene que poder publicarse con licencia libre y datos libres.
Cómo lo hace cada una, en una frase
- spaCy (código MIT; modelo
es_core_news_lgMIT). Es una tubería neuronal entrenada sobre AnCora/UD: untok2vecalimenta un etiquetador morfológico, un analizador de dependencias y un NER, todos estadísticos. Trae 500 000 vectores de palabra (dim 300). Rápida, con el modelo en memoria. Su lematización y su PoS del español fallan de maneras concretas (lo medimos abajo). - NLTK (código Apache 2.0; datos con licencias variadas). Es una caja de herramientas académica: tokenizadores (Punkt no supervisado, Treebank, toktok), stemmer Snowball, stopwords, n-gramas, colocaciones, concordancias, WordNet y FrameNet. Casi todo su contenido léxico-semántico es inglés; para español da sobre todo tokenización, raíces y estadística de corpus.
- FreeLing (código AGPL; datos/treebanks tipo CC BY-SA). El más fuerte en español clásico: tokenizador y segmentador por reglas, analizador morfológico con diccionario EAGLES, PoS estadístico (HMM sobre AnCora), NER+NEC, WSD (UKB sobre WordNet), dependencias, roles semánticos, correferencia y fonética. Preciso y explicable, pero servirlo hoy es lento: relanza el binario
analyzeen cada petición (~9–18 s/frase en nuestro montaje).
Y lo nuestro hoy (60 endpoints en api.wordkers.com, todos algorítmicos): un motor léxico-morfológico muy completo —lematizar, morfología, silabeo, fonética, conjugación y flexión generativas, diccionario semántico con sinónimos, hiperónimos y colocaciones, refranero, niveles de dificultad y frecuencia— pero sin tokenizador de texto corrido, sin segmentador de oraciones, sin PoS/NER en contexto, sin dependencias y sin vectores. Justo el hueco que llenan los satélites.
Aviso sobre las mediciones. spaCy y FreeLing se midieron en vivo en Prepro (
127.0.0.1:8022y:8021); NLTK como librería. El core (api.wordkers.com) exige clave y este trabajo era «sin claves», así que su calidad en vivo no se mide aquí (igual que en E-465); lo que sí medimos es nuestra pieza nueva (wkpln), que no depende de claves. Para lema/PoS/NER en español, la tabla de 30 frases de E-465 (spaCy vs FreeLing) es la referencia y se cita.
El mapa función por función
Enfoque abreviado: R = reglas, L = léxico/diccionario, E = estadístico, N = red neuronal. Licencia = (código / datos).
| Función | Qué hace (entrada → salida) | spaCy | NLTK | FreeLing | Lo nuestro (endpoint) | Veredicto de calidad en español |
|---|---|---|---|---|---|---|
| Tokenizar | texto → palabras y signos con offsets | R+N sobre excepciones | R (Punkt/Treebank/toktok) | R (reglas) | **wkpln.tokenizar** (nuevo) | Empate técnico: los tres y el nuestro dan F1 1.0 en 30 frases; NLTK no separa ¿/¡. Nuestro ~400× más rápido |
| Segmentar oraciones | texto → oraciones con offsets | N (parser/senter) | R+E (Punkt) | R (reglas+abreviaturas) | **wkpln.oraciones** (nuevo) | Nuestro y spaCy/NLTK aciertan el párrafo duro (6/6); FreeLing falla uno (5/6) |
| Lematizar | palabra/forma → lema | N+lookup | — (solo inglés) | L (diccionario EAGLES) | /v1/morfologia/lematizar, /lemas | FreeLing y core, bien; spaCy comete errores claros (cocina→cocín, muy→mucho) |
| PoS / etiquetar | oración → categoría por token | N (morphologizer) | E (solo si se entrena, p. ej. CESS) | E (HMM sobre AnCora) | (no en contexto; sí análisis léxico) | FreeLing gana (EAGLES); spaCy falla con mayúscula inicial y verbos→PROPN |
| Morfología (rasgos) | forma → género, número, tiempo… | N | — | L+E | /v1/morfologia/*, /v1/genero/*, /v1/verbo/analizar | Empate: nuestro léxico da rasgos finos por palabra suelta |
| NER (entidades) | texto → PER/LOC/ORG con tramos | N | — | R+E (NEC) | (no) | Empate de recall; spaCy tipa mejor Apple=ORG, FreeLing Barcelona=club |
| Dependencias | oración → árbol (cabeza+relación) | N (parser) | — | R+E | (no) | spaCy gana (rápido y limpio); FreeLing lento |
| Chunking / sintagmas | oración → SN/SV superficiales | vía árbol | R (RegexpParser) | R (shallow) | (no) | Reproducible por reglas sobre PoS propio |
| Sinónimos / hiperónimos | palabra → red léxica | vectores (aprox.) | WordNet (inglés; spa OMW) | L (WordNet-ES) | /v1/diccionario/sinonimos, /hiperonimos, /hiponimos, /relacionadas | Nuestro, propio y en español; WordNet-ES es CC pero indirecto |
| WSD (sentidos) | palabra en contexto → acepción | — | Lesk (didáctico) | E (UKB sobre WordNet) | /v1/diccionario/definicion (lematiza+elige) | Parcial nuestro; UKB en contexto no lo tenemos |
| Roles semánticos (SRL) | oración → quién-hace-qué-a-quién | — | FrameNet (inglés) | E (sobre dependencias) | (no) | Difícil; FrameNet es inglés. Se pospone |
| Vectores / similitud | texto → vector; par → coseno | N (500k vectores) | — | — | (no) | spaCy gana de largo; nadie más lo ofrece de serie |
| Stemming (raíces) | palabra → raíz cruda | — | R (Snowball ES) | — (usa lema) | (lematizar es mejor que *stem)* | El stem es tosco; nuestro lema es superior para español |
| Stopwords | lista de palabras vacías | atributo is_stop | lista ES | — | (trivial; lista propia) | Trivial; lista propia libre en minutos |
| n-gramas | secuencia → bi/tri-gramas | — | R (ngrams) | — | *(trivial sobre wkpln)* | Trivial una vez tenemos tokenizador propio |
| Colocaciones | corpus → pares que van juntos (PMI) | — | E (PMI/χ²) | — | /v1/diccionario/colocaciones (por palabra) | Nuestro por palabra; el cálculo PMI sobre corpus es reproducible |
| Concordancias / frecuencias | corpus → KWIC, FreqDist | — | R+E | — (corpus) | /v1/nivel/frecuencia | Reproducible sobre wkpln + índice |
| Detección de idioma | texto → idioma | añadido externo | — | R (opcional) | (no) | Reproducible con n-gramas de caracteres (datos libres) |
| Silabeo / acentuación | palabra → sílabas, tilde, tipo | — | — | — | /v1/silabeo/* | Solo nuestro. Los tres no lo hacen |
| Fonética | palabra → IPA/SAMPA, rimas, homófonos | — | — | R (SAMPA) | /v1/fonetica/* | Nuestro es más rico (rimas, pares mínimos, homófonos) |
| Conjugación / flexión | verbo/adj → paradigma, plural, género | — | — | L (genera del diccionario) | /v1/verbo/*, /v1/flexion/*, /v1/cazar | Solo nuestro y generativo (conjuga hasta inventados) |
Cómo lo hacen, con qué recursos (sin mirar su código)
- spaCy aprende de treebanks UD/AnCora una red que predice etiquetas y árboles; sus vectores salen de un word2vec/fastText sobre corpus grandes. Es end-to-end estadístico: rápido y uniforme, pero opaco y con errores de español que no sabe explicar (etiqueta
salta→PROPN por la mayúscula, inventacocín). - NLTK es sobre todo reglas y estadística de corpus más envoltorios de recursos ajenos (WordNet, FrameNet). Su Punkt aprende abreviaturas de forma no supervisada; su Snowball es un stemmer de reglas. Poco específico del español.
- FreeLing combina un diccionario EAGLES enorme con etiquetadores estadísticos entrenados en AnCora y desambiguación UKB sobre WordNet. Es el más fino en español, y el más caro de servir tal como está.
Dónde podemos ser mejores (honesto, función a función)
Donde ya somos mejores o iguales, y libres:
- Silabeo, acentuación, fonética, conjugación y flexión. Los tres satélites no las hacen (o FreeLing solo SAMPA). Aquí no hay nada que sustituir: ya somos la referencia, y generativa (conjugamos e inventamos, cazamos infinitivos).
- Sinónimos, hiperónimos, hipónimos y relacionadas en español, desde nuestro diccionario, sin depender de WordNet.
- Lematización. Con diccionario + conjugador batimos a spaCy con claridad (E-465). El objetivo es igualar a FreeLing y quitarlo.
Donde podemos rehacerlo por reglas/léxico, con datos libres:
- Tokenizar y segmentar oraciones. Hecho ya (ver prototipo): F1 1.0 en 30 frases, 400× más rápido que spaCy, y acierta la segmentación donde FreeLing falla.
- PoS y morfología en contexto. Tenemos el léxico (candidatos EAGLES por palabra); falta la desambiguación en frase. Reproducible con un etiquetador propio (reglas + estadística ligera sobre AnCora, que es libre).
- Chunking, n-gramas, colocaciones (PMI), concordancias, stopwords, detección de idioma. Todo esto es reglas o estadística de corpus sobre nuestro tokenizador: barato y publicable.
Donde honestamente no compensa (todavía) o no vamos a reproducir:
- Vectores y similitud semántica. spaCy trae 500 000 vectores entrenados; no los vamos a reentrenar a corto plazo. Si queremos similitud, o usamos embeddings libres (fastText-es, CC BY-SA) o lo dejamos como está. Aquí spaCy gana.
- NER y dependencias neuronales de calidad. Reproducibles por reglas hasta cierto punto, pero igualar al modelo cuesta datos y tiempo. Prioridad media-baja.
- SRL / FrameNet. El recurso es inglés; para español habría que construirlo. Se pospone.
Plan por prioridad (tres tandas)
Estimaciones en minutos de Opus/DeepSeek. Métrica: conjunto de prueba con oro y F1 (tokenización), accuracy de frontera (oraciones), y tagging accuracy contra AnCora (PoS).
Tanda 1 — lo barato y de más valor (reglas, datos libres).
| Pieza | Datos | Métrica | Estimación |
|---|---|---|---|
| Tokenizador español | reglas + lista de abreviaturas propia | F1 vs oro (30→200 frases) | hecho (prototipo) |
| Segmentador de oraciones | reglas + abreviaturas | frontera vs oro | hecho (prototipo) |
| Stopwords | lista propia libre | — | 20 min |
| n-gramas + colocaciones (PMI) | corpus libre + wkpln | PMI reproducible | 60 min |
| Detección de idioma | perfiles de n-gramas de caracteres | accuracy | 60 min |
Tanda 2 — el corazón léxico-gramatical (sustituir a FreeLing/spaCy en PoS).
| Pieza | Datos | Métrica | Estimación |
|---|---|---|---|
| Analizador morfológico en contexto (candidatos EAGLES por token) | nuestro léxico + wkpln | cobertura | 120 min |
| Desambiguador PoS (reglas + HMM ligero) | AnCora/UD (libres) | tag accuracy vs FreeLing | 240 min |
| Lematizador de secuencia (usa PoS + conjugador) | nuestro | F1 de lema vs FreeLing | 120 min |
| Chunking superficial (SN/SV) | reglas sobre PoS | — | 90 min |
Tanda 3 — lo caro o lo que quizá no compense.
| Pieza | Datos | Métrica | Estimación |
|---|---|---|---|
| NER por reglas + gazetteers | listas libres + Molino | F1 vs FreeLing (E-465) | 240 min |
| Dependencias (parser ligero) | UD-Spanish (libre) | UAS/LAS | 480 min+ |
| Vectores/similitud | embeddings libres (fastText-es) o descartar | coseno | a decidir |
| SRL/FrameNet-es | no hay recurso libre en español | — | pospuesto |
Regla de oro de velocidad (Eduardo): se mide siempre tiempo por frase y por 1.000 frases del original y del nuestro; si el nuestro es más lento, no se da por bueno hasta proponer índices, precálculo, léxico compilado o caché.
Prototipo de la primera pieza: tokenizador + segmentador (apis/pln)
La pieza con mejor relación valor/coste es la que abre todas las demás: sin tokenizador y segmentador propios no hay n-gramas, ni colocaciones, ni PoS en contexto. Está hecha, en el repo nuevo **wordkers/apis/pln** (módulo wkpln, sin dependencias externas — solo librería estándar; se publica con licencia libre).
Qué hace. tokenizar(texto) y oraciones(texto), por reglas explícitas y una lista corta de abreviaturas propia, con offsets en caracteres. Decisiones documentadas: puntuación suelta, suspensivos un token, el punto de una abreviatura pegado a la palabra (Sr., etc.), números 1.234,56 / fechas 3/5/1999 / horas 12:30 / URLs enteros, contracciones del/al sin partir salvo que se pida (separar_contracciones → de+el, útil para lematizar), y clíticos enteros.
Pruebas. pytest en verde: 71 passed (sin red ni modelos).
Comparativa numérica (30 frases + párrafo duro), medida en Prepro:
| Motor | F1 tokenización | Frases exactas | Oraciones párrafo (oro 6) | ms/frase |
|---|---|---|---|---|
| wkpln (nuestro) | 1.0 | 30/30 | 6/6 ✓ | 0.02 |
spaCy (:8022) | 1.0 | 30/30 | 6/6 ✓ | 7.9 |
| NLTK (librería) | 0.983 | 27/30 | 6/6 ✓ | 7.1 |
FreeLing (:8021) | 1.0 | 30/30 | 5/6 ✗ | 8 836 |
Lectura honesta. El oro es la convención Wordkers; que spaCy y FreeLing también saquen 1.0 confirma que la convención es sensata (no está trucada a nuestro favor). Las diferencias reales:
- Velocidad.
wkplnhace tokenizar+segmentar 30 000 frases en 0,68 s (~0,023 ms/frase; ~23 ms por 1.000 frases), sin cargar ningún modelo ni ocupar los ~868 MB de RAM de spaCy. Es ~400× más rápido que la llamada a spaCy y ~440 000× más rápido que FreeLing tal como se sirve hoy. - Segmentación.
wkpln, spaCy y NLTK aciertan el párrafo duro (abreviaturas, decimales, horas,¿?, suspensivos): 6/6. FreeLing se deja una: 5/6. - Español. NLTK no separa el signo de apertura
¿/¡de la palabra (¿Cuándocomo un token): 3 fallos de 30.wkplnsí lo separa.
Conclusión del prototipo. En tokenización y segmentación, lo nuestro iguala en precisión a lo mejor (spaCy/FreeLing), mejora la segmentación frente a FreeLing, evita el fallo del ¿ de NLTK, y es cientos de veces más rápido sin depender de un modelo. Es el primer satélite que podemos empezar a retirar.