Files
graph_explorer/issues/0042-mrebel-relation-extractor.md.superseded
T
egutierrez 5e6023f639 docs: issues 0041 (split thresholds) + 0042 (GLiNER2), supersedes mREBEL
Cierra el ciclo del analysis gliner_glirel_tuning: documenta en app.md el
pipeline NER+RE disponible en el registry y abre los dos issues que faltan
para cablearlo en extract_graph_hybrid + panel paste_extract. Archiva el
0042 original (mREBEL) tras la decision a favor de GLiNER2 (Apache 2.0,
joint NER+RE, 20-30x mas rapido en CPU).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 10:07:43 +02:00

175 lines
7.6 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: 0042
title: Sustituir GLiREL por mREBEL como extractor de relaciones (uso no comercial)
status: pending
priority: high
created: 2026-05-04
parent: 0013
depends_on: [0041]
---
## Contexto
Tras integrar GLiREL en `extract_graph_hybrid` (issue 0013), el analisis empirico (`projects/osint_graph/analysis/gliner_glirel_tuning/`) revelo que GLiREL falla estrepitosamente sobre narrativa empresarial castellana: 51 falsos positivos a `t=0.15`, 1 unica relacion (tambien falsa) a `t=0.30`. **No hay sweet spot**.
Probado el sustituto **`Babelscape/mrebel-large`** (mBART seq2seq que genera tripletas directamente del texto) sobre el mismo corpus: **8 tripletas crudas, 5 alineables con entidades GLiNER, 4 inequivocamente correctas, cero falsos absurdos**.
Decision documentada en `vaults/osint_nlp_models/decisions/2026-05-04-mrebel-over-glirel.md`.
## Restriccion de licencia
mREBEL es **CC BY-NC-SA 4.0 — uso no comercial**. Compatible con `graph_explorer` actual (uso personal de investigacion). Bloqueante si pasa a producto comercial. Plan de contingencia documentado en `vaults/osint_nlp_models/decisions/2026-05-04-license-constraint.md`. **Hay que marcar la restriccion en el codigo y en el `app.md`.**
## Objetivo
Anadir mREBEL como capa 4 alternativa en `extract_graph_hybrid`, configurable por el usuario en el panel `paste_extract`. GLiREL no se borra — queda disponible para textos en ingles donde su comportamiento puede ser razonable.
## Diseño
### 1. Funciones nuevas en el registry
```
python/functions/datascience/
├── mrebel_load_model.py # carga + cache (mBART, src_lang, tgt_lang)
├── extract_relations_mrebel.py # frase a frase, devuelve list[RelationCandidate]
└── _mrebel_parser.py # parser del wire format <triplet>...</s>
```
Plantilla `mrebel_load_model.py`:
```python
"""LICENSE: CC BY-NC-SA 4.0 — non-commercial use only.
Carga (y cachea) Babelscape/mrebel-large para extraccion de relaciones.
"""
from typing import Any
_MODEL_CACHE: dict[tuple[str, str], Any] = {}
def mrebel_load_model(
model_name: str = "Babelscape/mrebel-large",
src_lang: str = "es_XX",
tgt_lang: str = "tp_XX",
) -> tuple[Any, Any]:
key = (model_name, src_lang)
if key in _MODEL_CACHE: return _MODEL_CACHE[key]
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
tok = AutoTokenizer.from_pretrained(model_name, src_lang=src_lang, tgt_lang=tgt_lang)
mdl = AutoModelForSeq2SeqLM.from_pretrained(model_name)
_MODEL_CACHE[key] = (tok, mdl)
return tok, mdl
```
`extract_relations_mrebel.py` toma `text`, `entities` (de GLiNER), trocea por frases con `re.split`, llama a mREBEL frase a frase, parsea tripletas, hace **string-match con `entity.name`**, y devuelve `list[RelationCandidate]` con `relation_type` igual al `type` que emite mREBEL (vocabulario Wikidata). Las tripletas sin match se descartan silenciosamente.
### 2. Cambio en `extract_graph_hybrid.py`
Anadir parametro `relation_engine` con tres valores:
```python
def extract_graph_hybrid(
chunks, entity_schema, relation_types,
gliner_model,
relation_engine: str = "mrebel", # 'glirel' | 'mrebel' | 'none'
glirel_model=None, # solo si engine == 'glirel'
mrebel_models=None, # tuple (tok, mdl) si engine == 'mrebel'
llm_chat_json=None,
entity_threshold=0.5,
relation_threshold=0.15,
):
...
if relation_engine == "glirel":
rels = extract_relations_glirel(chunk, ents, relation_types, glirel_model, threshold=relation_threshold)
elif relation_engine == "mrebel":
rels = extract_relations_mrebel(chunk, ents, mrebel_models)
elif relation_engine == "none":
rels = []
...
```
**`relation_types` es ignorado** cuando `engine == 'mrebel'` (el modelo emite vocab Wikidata cerrado). Quiza convenga dejar un parametro opcional `accept_relation_types` que filtre la salida si el usuario quiere taxonomia controlada.
### 3. Cambio en `enrichers/paste_extract/run.py`
```yaml
# manifest
params:
text: { type: string, required: true }
use_hybrid: { type: bool, default: true }
relation_engine: { type: enum, values: [glirel, mrebel, none], default: mrebel }
entity_threshold: { type: float, default: 0.5 }
relation_threshold: { type: float, default: 0.15 } # solo aplicable si engine=glirel
```
### 4. Cambios en el panel C++ (`extract_panel.cpp`)
- Combo `Relation engine: [GLiREL | mREBEL | None]` debajo del toggle hybrid.
- **Banner de licencia** cuando `engine == mREBEL`: amarillo, texto fijo `"mREBEL: CC BY-NC-SA — non-commercial use only"`.
- **Barra de progreso por frase** cuando engine=mREBEL (latencia ~3s/frase). El subprocess emite `PROGRESS:0.X frase_N` cada frase, el panel lo lee y refresca un `ImGui::ProgressBar`.
### 5. `app.md` — declarar la restriccion
```yaml
---
...
notes: |
El motor de relaciones por defecto es mREBEL (Babelscape/mrebel-large),
bajo licencia CC BY-NC-SA 4.0. NO usar en contextos comerciales sin
cambiar el motor a GLiREL (Apache 2.0) o LLM externo.
optional_dependencies:
- "transformers (mREBEL/GLiREL)"
- "sentencepiece (mBART tokenizer)"
---
```
### 6. Tests pytest
`tests/test_extract_relations_mrebel.py`:
- Test parser de output mREBEL (formato `<triplet> ... </s>`).
- Test string-match con entidades fuzzy (substring + exact).
- Test que tripletas sin match desaparecen.
- Test smoke con stub de modelo (mock que retorna decoded canónico).
- Test integracion: pasar texto ES → ents GLiNER fake → tripletas alineadas.
`tests/test_paste_extract.py`: añadir test del path `relation_engine=mrebel`.
### 7. Documentacion
- `app.md` — seccion de relaciones explica los 3 engines + licencia.
- `python/functions/datascience/mrebel_load_model.md` — frontmatter completo + restriccion de licencia.
## Definicion de hecho
- Pego el texto del corpus es_corporate, selecciono `engine=mREBEL`, pulso Extract.
- Tras ~25s veo las 4 relaciones limpias en la tabla (Pablo Isla→Inditex, Endesa→Marina Serrano, BBVA→Carlos Torres, Arteixo→A Coruna).
- Cambio a `engine=GLiREL` y veo las 51 espurias (validacion contraria).
- Apply solo guarda las marcadas; dedupe sigue funcionando.
- Banner de licencia amarillo es visible cuando engine=mREBEL.
- Tests verdes en WSL.
## Latencia / UX
- mREBEL es ~50× mas lento que GLiREL (3s/frase vs 50ms/grafo entero).
- Para texto largo (20+ frases) son ~60s. **Barra de progreso es obligatoria.**
- Plan: emitir `PROGRESS:f frase=N/M` cada frase; panel C++ refresca ProgressBar.
- Cancelacion: boton "Cancel" mata el subprocess limpiamente (issue futuro si no es trivial).
## Out of scope (issues separados)
- LLM como validator semantico de tripletas mREBEL — futuro.
- mREBEL en GPU (issue de infra, requires CUDA). En CPU es usable.
- Mapeo del vocabulario Wikidata a una taxonomia OSINT custom — issue futuro.
- Cancelacion del subprocess (Cancel button) — micro-issue.
## Riesgos
- **Latencia.** El usuario puede sentir que la app esta colgada sin la barra de progreso. Mitigacion: progress por frase + tip "modelo grande, primer texto descarga 2.4 GB".
- **Cobertura del vocabulario Wikidata** en dominios fuera de business/journalism. Para narrativa funciona; para OSINT, drama tipico, puede no encajar — habra que probar y posiblemente añadir mapeo custom (issue futuro).
- **Licencia.** Reducible solo via diseño claro de UI (banner) + documentacion (app.md, vault).
## Relacionado
- `vaults/osint_nlp_models/models/mrebel.md` — observaciones empiricas
- `vaults/osint_nlp_models/decisions/2026-05-04-mrebel-over-glirel.md` — ADR
- Notebooks 01, 02, 03 en `analysis/gliner_glirel_tuning/`