--- name: gliner_glirel_tuning lang: py domain: datascience description: "Estudio empirico de GLiNER y GLiREL: distribucion de scores, sensibilidad a threshold/top_k/labels/idioma, calibracion de thresholds para extract_graph_hybrid" tags: [nlp, gliner, glirel, thresholds] uses_functions: [] uses_types: [] framework: "jupyterlab" entry_point: "notebooks/main.ipynb" dir_path: "projects/osint_graph/analysis/gliner_glirel_tuning" repo_url: "" --- ## Notas Estudio empirico de GLiNER y GLiREL: distribucion de scores, sensibilidad a threshold/top_k/labels/idioma, calibracion de thresholds para extract_graph_hybrid. Tras varias jornadas el alcance se amplio: ahora cubre **6 modelos** (GLiNER, GLiREL, mREBEL, REBEL, GLiNER2, NuExtract 2.0-2B) + **OpenIE schema-less ES** con spaCy + reglas de dependencia. La conclusion ganadora vive en el vault `osint_nlp_models`. ## Notebooks (orden cronologico — ejecutados con outputs guardados) | # | Notebook | Hallazgo clave | |---|---|---| | 01 | `notebooks/01_gliner_glirel_tuning.ipynb` | Calibracion thresholds GLiNER+GLiREL. Multilingue: labels EN funcionan sobre texto ES. snake_case verbal labels >> natural_long en GLiREL. | | 02 | `notebooks/02_e2e_spanish_graph.ipynb` | E2E ES + grafo. Descubrimiento: GLiREL emite 51 falsos positivos en es_corporate_short a t=0.15; a t=0.30 solo 1 relacion (tambien falsa). **No hay sweet spot** en castellano. | | 03 | `notebooks/03_mrebel_vs_glirel.ipynb` | mREBEL frase-a-frase: 8 tripletas crudas, 5 alineables, 4 inequivocamente correctas. Cero falsos absurdos. **PERO** licencia CC BY-NC-SA 4.0 (no comercial). | | 04 | `notebooks/04_gliner2_winner.ipynb` ⭐ | GLiNER2 `fastino/gliner2-large-v1` (Apache 2.0, 340M, NER+RE joint). 6/8 correctas vs 4/5 mREBEL, 20× mas rapido. Funciona en OSINT castellano. **Modelo elegido**. | | 05 | `notebooks/05_long_text_and_pdf.ipynb` | Pipeline PDF E2E sobre `politica_proteccion_datos.pdf` (BBVA, 89.882 chars). 67 chunks, 378 entidades, 54 relaciones, 97.9s total. | | 06 | `notebooks/06_improvements.ipynb` | Mejoras GLiNER2: threshold 0.3 (vs 0.5 default) → +187% relaciones (71→204). Coref normalize+substring → −18% aislados (389→318). Descripciones por relacion **sin efecto**. `batch_extract` 25% **mas lento** en CPU. | | 07 | `notebooks/07_nuextract_vs_gliner2.ipynb` | NuExtract 2.0-2B GPU sobre RTX 3070: load 7.1s, T1 2.88s vs CPU 25s (8.7×). PDF entero extrapolado 5.2 min vs GLiNER2 CPU 2.2 min — **2.6× mas lento incluso con GPU**. Calidad similar. **Descartado por velocidad**. | | 08 | `notebooks/08_improving_gliner2.ipynb` | Label naming: snake_case verbal >> camelCase >> espacios. `include_confidence=True` permite threshold por relacion. Post-filter typed gratis y decisivo. GLiREL+allowed_head/tail post-hoc revive el modelo como complemento. | | 09 | `notebooks/09_spacy_es_openie.ipynb` | spaCy ES `es_core_news_md` + reglas de dependencia: OpenIE schema-less en castellano. `(Enmanuel, querer, Ashlly)` con verbo del texto, 5ms/frase. Reglas pendientes V2: pasiva refleja, copulares, coref pronombres. | ## Hallazgos operativos consolidados ### Stack final recomendado para `graph_explorer` ``` Capa 1 (NER + RE schema-driven): GLiNER2 (Apache 2.0) + threshold=0.3 (vs default 0.5) + snake_case verbal labels + include_confidence=True (para tuning fino) Capa 2 (post-procesado puro, gratis): filter_relations_by_entity_types ← descarta absurdos (Madrid president_of Persona) merge_entity_aliases ← BBVA ⊂ Banco Bilbao Vizcaya... aggregate_extraction_results ← dedupe + counter sobre N chunks Capa 3 (chunking para texto largo): chunk_with_overlap (max_chars=1500, overlap_sentences=2) Capa 4 opcional (OpenIE schema-less complementaria): spaCy es_core_news_md + extract_triples_spacy_es ``` Todo el stack esta como **funciones del registry** tras esta sesion (10 funciones en core/datascience/pipelines). ### Decisiones registradas en el vault `vaults/osint_nlp_models/decisions/`: - `2026-05-04-mrebel-over-glirel.md` — primera decision (mañana): mREBEL gana a GLiREL pero caveat licencia. - `2026-05-04-gliner2-over-mrebel.md` ⭐ — decision final (tarde): GLiNER2 reemplaza a todos por velocidad + Apache 2.0 + multilingue ES nativo. - `2026-05-04-license-constraint.md` — plan de contingencia si en algun momento se necesita comercial sin Apache 2.0. ### Modelos descartados y por que | Modelo | Razon | |---|---| | **GLiREL** `jackboyla/glirel-large-v0` | 51 falsos positivos en ES corporate, sin sweet spot. Util quiza en EN tecnico (no probado). | | **mREBEL large/base** | CC BY-NC-SA 4.0 (bloqueante comercial) + 25× mas lento que GLiNER2. Queda como fallback. | | **REBEL EN-only** | Apache 2.0 pero requiere traducir ES→EN, +500ms-1s + riesgo nombres propios. | | **NuExtract 2.0-2B** | 2.6× mas lento que GLiNER2 incluso con GPU. Mejor para "ficha rica" por entidad pero excesivo para grafo. | | **triplet-extract EN-only** | Pierdes verbos del texto castellano al traducir; `(quiere, loves)` no es lo mismo. | ## Pendientes (tracked en issues) - `dev/issues/0050-jupyter-exec-collab-client-failure.md` — bug `jupyter_exec` con cliente colaborativo. - `projects/osint_graph/apps/graph_explorer/issues/0041-split-confidence-thresholds.md` — split `confidence_threshold` en `entity_threshold` + `relation_threshold`. - `projects/osint_graph/apps/graph_explorer/issues/0042-gliner2-unified-extractor.md` ⭐ — sustituir GLiREL por GLiNER2 en `extract_graph_hybrid` del panel `paste_extract`. - `dev/issues/0051-extraction-pipeline-followups.md` — funciones aun por construir (NuExtract loader, extract_graph_from_pdf, spaCy ES V2 rules, kernel startup fix). Ver issue. ## Como reproducir cualquier experimento Cada notebook tiene su `build_notebook_*.py` y, cuando es pesado, su `run_*.py` que vuelca a JSON: ```bash cd projects/osint_graph/analysis/gliner_glirel_tuning ./.venv/bin/python3 -u run_benchmark_v2.py # genera benchmark_v2.json ./.venv/bin/python3 build_notebook_gliner2.py # genera notebooks/04_gliner2_winner.ipynb IPYTHONDIR=$(pwd)/.ipython ./.venv/bin/jupyter nbconvert \ --to notebook --execute notebooks/04_gliner2_winner.ipynb \ --output 04_gliner2_winner.ipynb --ExecutePreprocessor.timeout=600 ``` JSONs de resultados (todos en la raiz del analysis): - `benchmark_v2.json` — GLiNER2 sobre 4 corpora. - `improvements.json` — 5 configs A-E sobre el PDF + coref. - `nuextract_results.json` — NuExtract CPU baseline + GPU. - `nuextract_full.json` — NuExtract GPU sobre PDF entero (179 chunks parsed OK). - `mrebel_results.json` — mREBEL sobre es_corporate_short. - `openie_results.json` — comparativa 3 paradigmas (triplet-extract EN, spaCy ES, GLiNER2). ## Playground `projects/osint_graph/analysis/gliner_glirel_tuning/playground/` — server FastAPI + frontend Sigma.js sirviendo en `localhost:7878`. Aplica todo el stack de capas 1-3 sobre cualquier texto que pegues. Ver `playground/server.py` y `playground/index.html`.