5e6023f639
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>
103 lines
4.5 KiB
Markdown
103 lines
4.5 KiB
Markdown
---
|
|
id: 0041
|
|
title: Split confidence_threshold en entity_threshold + relation_threshold
|
|
status: pending
|
|
priority: medium
|
|
created: 2026-05-04
|
|
parent: 0013
|
|
---
|
|
|
|
## Objetivo
|
|
|
|
`extract_graph_hybrid` y el panel `paste_extract` actualmente comparten un solo `confidence_threshold`. El analisis empirico (`projects/osint_graph/analysis/gliner_glirel_tuning/`) demuestra que las dos capas tienen distribuciones de score radicalmente distintas:
|
|
|
|
- **GLiNER** emite scores 0.92-0.99 en narrativa, 0.5-0.95 en OSINT. Threshold sano: **0.50-0.70**.
|
|
- **GLiREL** emite scores 0.05-0.34. Threshold sano: **0.10-0.20**.
|
|
- **mREBEL** (issue 0042) emite confianzas implicitas via `num_beams` — distinto modelo, distinto rango.
|
|
|
|
Un solo threshold global o filtra todas las relaciones (`t=0.6` mata GLiREL) o satura el grafo de entidades dudosas (`t=0.15` deja pasar entidades borderline).
|
|
|
|
## Cambios
|
|
|
|
### En `python/functions/pipelines/extract_graph_hybrid.py`
|
|
|
|
Aceptar dos thresholds opcionales con back-compat:
|
|
|
|
```python
|
|
def extract_graph_hybrid(
|
|
chunks, entity_schema, relation_types,
|
|
gliner_model, glirel_model,
|
|
llm_chat_json=None,
|
|
confidence_threshold=0.6, # legacy, valor por defecto si los dos siguientes no se pasan
|
|
entity_threshold=None, # nuevo — gobierna GLiNER + LLM-fallback de entidades
|
|
relation_threshold=None, # nuevo — gobierna GLiREL + LLM-fallback de relaciones
|
|
):
|
|
if entity_threshold is None:
|
|
entity_threshold = confidence_threshold
|
|
if relation_threshold is None:
|
|
relation_threshold = confidence_threshold
|
|
...
|
|
```
|
|
|
|
Pasar cada threshold a su capa respectiva (GLiNER `predict_entities(threshold=entity_threshold)`, GLiREL `predict_relations(threshold=relation_threshold)`).
|
|
|
|
### En `enrichers/paste_extract/run.py`
|
|
|
|
Aceptar `entity_threshold` y `relation_threshold` en `params`. Mantener `confidence_threshold` por compatibilidad. Defaults sugeridos en el manifest:
|
|
|
|
```yaml
|
|
params:
|
|
text: { type: string, required: true }
|
|
use_hybrid: { type: bool, default: true }
|
|
entity_threshold: { type: float, default: 0.50 }
|
|
relation_threshold: { type: float, default: 0.15 }
|
|
```
|
|
|
|
### En `extract_panel.cpp` / `extract_panel.h`
|
|
|
|
Reemplazar el slider unico por **dos sliders verticales** (mas el toggle `use_hybrid`):
|
|
|
|
```
|
|
┌─ Extract config ─────────────────────────────┐
|
|
│ ☑ Use hybrid (GLiNER + GLiREL) │
|
|
│ │
|
|
│ Entity threshold 0.50 [==== ] │
|
|
│ Relation threshold 0.15 [== ] │
|
|
└──────────────────────────────────────────────┘
|
|
```
|
|
|
|
Defaults: `entity_threshold=0.50`, `relation_threshold=0.15`.
|
|
|
|
### Tests
|
|
|
|
`tests/test_paste_extract.py`:
|
|
|
|
- Test que pasar solo `confidence_threshold` mantiene comportamiento legacy.
|
|
- Test que pasar `entity_threshold=0.5, relation_threshold=0.1` aplica thresholds distintos a cada capa.
|
|
- Test que la UI envia los dos parametros correctamente al subprocess.
|
|
|
|
## Definicion de hecho
|
|
|
|
- Pego un texto y veo entidades con confianza 0.5+ Y relaciones con confianza 0.15+ por defecto.
|
|
- Mover el slider de relaciones a 0.05 me muestra mas relaciones (potencialmente espurias) sin afectar las entidades.
|
|
- Mover el slider de entidades a 0.9 reduce las entidades sin tocar las relaciones.
|
|
- El test legacy (`confidence_threshold` solo) sigue pasando.
|
|
|
|
## Datos que respaldan los defaults
|
|
|
|
Notebook `01_gliner_glirel_tuning.ipynb`, tabla "GLiNER thresholds" (corpus es_corporate, en_corporate, es_journalism). Notebook `02_e2e_spanish_graph.ipynb`, comparacion recall (t=0.15) vs precision (t=0.30) — ambos modos producen grafos validos por separado, no hay un punto medio compartido bueno.
|
|
|
|
## Out of scope
|
|
|
|
- Optimizacion automatica de thresholds (issue futuro).
|
|
- Threshold por tipo de entidad (`person` mas estricto que `location`) — issue futuro.
|
|
- Calibracion automatica con feedback del usuario (al hacer Apply, ajustar thresholds aprendiendo).
|
|
|
|
## Reversibilidad
|
|
|
|
Si los nuevos defaults producen mas ruido del esperado, basta con cambiarlos en el manifest. El esquema de dos thresholds es estrictamente mas expresivo que el legacy.
|
|
|
|
## Relacionado
|
|
|
|
- Issue 0042 — sustituir GLiREL por mREBEL. Si 0042 va primero, este issue cambia: `relation_threshold` se vuelve menos relevante (mREBEL no usa threshold continuo del mismo modo) pero `entity_threshold` sigue siendo necesario.
|