Skip to main content

Neural document OCR for Chilean territorial planning documents

Project description

SpharagusNet

SpharagusNet Logo

Sobre el Nombre

SpharagusNet combina dos figuras mitológicas que representan las capacidades del sistema:

  • Sphinx (Esfinge): La criatura que lee enigmas y extrae significado. Representa la capacidad del sistema para procesar documentos complejos y extraer información relevante de textos escaneados, interpretando el contenido legal y normativo de los instrumentos de planificación territorial.

  • Argus (Argos): El gigante de 100 ojos que todo lo ve. Representa la capacidad del sistema para observar y procesar múltiples fuentes de información simultáneamente.

Juntos, SpharagusNet es un sistema que observa todo (Argus) y extrae significado (Sphinx) de los documentos de planificación territorial de Chile.

¿Por Qué no Solo OCR Convencional?

La diferencia es fundamental. Tesseract OCR puro extrae todo texto de una imagen: encabezados, números de página, pies, metadata, contenido real... todo mezclado.

Con documentos escaneados complejos, el resultado es 90% ruido, 10% contenido útil.

┌─────────────────────────────────────────────┐
│ OCR CONVENCIONAL (Tesseract solo)           │
├─────────────────────────────────────────────┤
│ ES ÓN = PATA > Me A A E A AS               │
│ 1 A sas "e . Cr, - . MN y                  │
│ Ps CINARIO SU TOC NTNURLICADE CHILE        │
│ ; A Maries £5 de Septiémbre de 1990 Pág   │
│ ... (caracteres aleatorios, basura)        │
└─────────────────────────────────────────────┘
❌ Inutilizable. 90% ruido, errores OCR severos.

┌─────────────────────────────────────────────┐
│ CON RED NEURONAL (SpharagusNet)             │
├─────────────────────────────────────────────┤
│ Valparaíso, de Septiembre de Andrés Couve  │
│ Rioseco, Subsecretario de Educación        │
│ Ministerio de Educación AUTORIZA...        │
│ Que es necesario establecer procedimientos │
│ que permitan una eficaz inserción de...    │
└─────────────────────────────────────────────┘
✅ Legible. 85% contenido relevante, 15% ruido residual.

¿Cómo? La red aprende a clasificar

La red neuronal no "mejora" Tesseract. En cambio:

  1. Tesseract extrae todo texto y metadatos
  2. Red neuronal clasifica cada bloque: ¿es relevante o basura?
  3. Mantiene solo lo relevante, descarta el resto

Entrenada con 100+ documentos legislativos, la red aprendió:

  • Qué se parece a "Artículo", "inciso", "párrafo" (patrones legislativos)
  • Qué es "Diario Oficial", "Página 5", números aislados (metadata)
  • Qué es ruido puro (caracteres aleatorios, errores OCR)

Resultado: Texto Procesable

Métrica OCR Puro SpharagusNet
Contenido útil 10% 85%
Ruido/Basura 90% 15%
Usable para NLP ❌ No ✅ Sí
Extracción de datos ❌ Falla ✅ Funciona
Análisis sintáctico ❌ Imposible ✅ Posible
Buscabilidad ❌ Pobre ✅ Excelente

Ventajas Prácticas

Mejor ratio signal-to-noise: La red filtra ~80% del ruido antes de que salga del pipeline

Dominio específico: Entrenada en documentos legislativos chilenos (MINVU, LeyChile), entiende la estructura y vocabulario

Adaptativo: Con más datos de entrenamiento, la red mejora automáticamente. Las heurísticas hardcodeadas requieren cambios manuales

Contextual: Entiende que "Diario" en "Diario de Implementación" es relevante, pero "Diario Oficial" en header es basura

Métricas medibles: Accuracy, Precision, Recall, F1 por epoch. Sabes exactamente qué tan bien está funcionando

Instalación

1. Dependencia de sistema: Tesseract OCR

pytesseract es solo un wrapper Python — el binario tesseract debe estar instalado en el sistema operativo:

Ubuntu / Debian / WSL:

sudo apt install tesseract-ocr tesseract-ocr-spa

macOS:

brew install tesseract tesseract-lang

Windows (nativo): Descarga el instalador desde UB-Mannheim/tesseract y asegúrate de marcar el idioma Spanish durante la instalación.

Verifica que quedó bien instalado:

tesseract --version
tesseract --list-langs   # debe aparecer "spa"

2. Instalar el paquete

pip install spharagusnet

Primera vez — descargar el modelo pre-entrenado (~300 MB):

spharagusnet download

Para desarrollo local (editable):

git clone https://github.com/jsmdev/spharagusnet.git
cd spharagusnet
pip install -e ".[train]"

Uso

Como librería

import spharagusnet

# Descargar modelo (primera vez, después se cachea)
spharagusnet.download_model()

# Extraer texto
texto = spharagusnet.extract("documento_escaneado.pdf")
texto = spharagusnet.extract("documento.pdf", confidence=0.6)

# Verificar si el modelo está disponible
spharagusnet.model_is_available()  # True/False

CLI

# Extraer texto de un PDF
spharagusnet extract documento.pdf
spharagusnet extract documento.pdf -o salida.txt
spharagusnet extract documento.pdf --confidence 0.6

# Descargar/actualizar modelo
spharagusnet download
spharagusnet download --force

# Info del paquete
spharagusnet info

Buscar documentos en BCN vía SPARQL

# Solo comunas de comunas_interes.txt
python scripts/bcn_sparql_planes.py

# Todas las comunas del CSV
python scripts/bcn_sparql_planes.py --todas

Nota: El script solo incluye registros cuya columna urls_documentos contenga un link de instrumentosdeplanificacion.minvu.cl. Registros sin URL de MINVU (en desarrollo, históricos sin digitalizar, o alojados en sitios municipales) se omiten del CSV de salida.

Preparar dataset y entrenar modelo

# 1. Preparar dataset (descarga PDFs, genera imágenes, cachea OCR y etiqueta bloques)
python scripts/preparar_dataset_entrenamiento.py [--max-pares N]

# 2. Entrenar modelo (con métricas en consola)
python scripts/entrenar_modelo_extraccion.py

# 3. Extraer texto de un PDF escaneado (con modelo entrenado)
python scripts/extraer_texto_con_modelo.py <ruta_pdf> [salida.txt] [--confidence 0.5]

Mejorar el modelo iterativamente

# 1. Incrementar datos gradualmente (50 → 100 → 200 → todos)
python scripts/preparar_dataset_entrenamiento.py --max-pares 80

# 2. Entrenar sobre lo existente
python scripts/entrenar_modelo_extraccion.py --resume

# 3. Si el val-F1 mejora, genial. Si estanca:
#    - Agregar más datos (paso 1 con más pares)
#    - Regenerar labels: borrar los .labels.json y correr paso 1 de nuevo
#    - Ajustar patience/epochs

# 4. Probar en un documento nuevo
python scripts/extraer_texto_con_modelo.py nuevo.pdf salida.txt

preparar_dataset_entrenamiento.py

  • --max-pares N limita la cantidad de filas a procesar (por defecto todas).
  • Incremental: solo descarga/procesa pares nuevos; los ya existentes se saltan.
  • Cache OCR: guarda bloques Tesseract como .ocr.json junto a cada imagen, así el entrenamiento posterior no re-corre OCR.
  • Etiquetado mejorado: usa trigramas + similitud Levenshtein para decidir relevancia (en vez de overlap de palabras sueltas que matchea "de", "la", etc.).
  • Convierte PDFs página por página a 200 DPI para evitar consumo de RAM excesivo en WSL.
  • Exige par completo (PDF LeyChile + PDF MINVU + imágenes generadas); archivos huérfanos se eliminan automáticamente.

entrenar_modelo_extraccion.py

  • --resume: carga el último checkpoint y continúa entrenando (no parte de cero). Expande el vocabulario automáticamente si hay palabras nuevas.
  • --epochs N: número de epochs (default: 20).
  • --patience N: early stopping patience (default: 5).
  • Carga desde cache: lee .labels.json generados por preparar_dataset_entrenamiento.py en vez de re-correr OCR.
  • Métricas por epoch: Loss, Accuracy, Precision, Recall, F1 en train y validación.
  • Guarda:
    • modelo_entrenado.pth (mejor modelo por F1)
    • vocab.json (vocabulario para tokenización)
    • historial_entrenamiento.json (métricas para análisis)

Arquitectura:

  • Embedding layer (vocab → 512-D vectors)
  • Block classifier (2 capas FC con ReLU + dropout) → clasificación binaria {irrelevante, relevante}
  • LSTM (generador de texto, para futuras extensiones)
  • Class weights para compensar desbalance (más bloques irrelevantes que relevantes)

extraer_texto_con_modelo.py

Flujo de inferencia:

  1. PDF escaneado → OCR (Tesseract) extrae bloques de texto
  2. Cada bloque → Tokenización + Embedding + Red neuronal
  3. Red predice: 0 (irrelevante) ó 1 (relevante)
  4. Reconstruye documento solo con bloques relevantes

Parámetros:

  • --confidence THRESHOLD: Umbral de confianza [0-1]
    • Default: 0.5 (balance entre precision/recall)
    • Menor threshold → más bloques (recall ↑, pero más falsos positivos)
    • Mayor threshold → menos bloques (precision ↑, pero pierdes contenido)

Fallback: Si el modelo no está disponible, usa heurísticas (filtros manuales)

Visualizar pipeline completo

python scripts/pipeline_completo.py

Muestra documentación completa del flujo, arquitectura y ejemplos de ejecución.

Arquitectura de la Red Neuronal

Diagrama del Flujo de Inferencia

┌──────────────────────────────────────────────────────────┐
│                    PDF MINVU (escaneado)                 │
└────────────────────────┬─────────────────────────────────┘
                         │
        ┌────────────────▼────────────────┐
        │ 1. CONVERSIÓN PDF → IMÁGENES    │
        │    (page-by-page, 200 DPI)      │
        └────────────────┬─────────────────┘
                         │
        ┌────────────────▼────────────────────────────────┐
        │ Para cada página:                               │
        │                                                 │
        │  ┌─ 2. OCR (Tesseract)                         │
        │  │    Imagen → bloques de texto + coordenadas  │
        │  │    (filtro: confianza > 30%)                │
        │  │                                              │
        │  │    [                                         │
        │  │      {"texto": "Artículo 1", ...},          │
        │  │      {"texto": "Página 1", ...},            │
        │  │      {...}                                  │
        │  │    ]                                        │
        │  │                                              │
        │  └─ 3. CLASIFICACIÓN (MODELO NEURONAL)         │
        │     Para cada bloque:                          │
        │     a) Tokenizar → [idx_1, idx_2, ...]        │
        │     b) Embedding → 512-D vectors               │
        │     c) Pooling → reducir a (512,)              │
        │     d) Block Classifier (2 FC layers)          │
        │     e) Softmax → prob(relevante)               │
        │                                                │
        │     ┌─────────────────────────────┐            │
        │     │ Clasificador Binario:       │            │
        │     │                             │            │
        │     │ FC(512 → 256) ─ ReLU ─      │            │
        │     │ Dropout(0.1) ─ FC(256 → 2) │            │
        │     │                             │            │
        │     │ Output: [logit_neg, logit_pos]│          │
        │     │ Pred: argmax() → {0, 1}     │            │
        │     └─────────────────────────────┘            │
        │                                                │
        │  └─ 4. FILTRADO                                │
        │     Si pred=1 AND confidence >= threshold:    │
        │       → MANTENER bloque                        │
        │     Sino:                                      │
        │       → DESCARTAR bloque                       │
        │                                                │
        └─────────────────┬──────────────────────────────┘
                          │
        ┌─────────────────▼──────────────┐
        │ 5. RECONSTRUCCIÓN              │
        │    Unir bloques relevantes     │
        │    + limpieza de espacios      │
        │    + separar por páginas       │
        └─────────────────┬──────────────┘
                          │
        ┌─────────────────▼──────────────┐
        │ 📄 TEXTO LIMPIO (formato      │
        │    LeyChile, sin headers/pies) │
        └───────────────────────────────┘

Métricas Reportadas During Training

La tabla de entrenamiento muestra:

    Epoch │   Loss    Acc   Prec   Rec    F1 │  vLoss  vAcc  vPrec  vRec   vF1 │     t     ETA
     1/20 │  0.6932  0.650  0.580  0.720  0.642 │  0.6801  0.670  0.600  0.700  0.646 │  2.3s   41.4s
     2/20 │  0.5234  0.710  0.620  0.750  0.680 │  0.5102  0.725  0.640  0.770  0.700 │  2.1s   39.2s
     3/20 │  0.4521  0.760  0.680  0.800  0.735 │  0.4398  0.770  0.690  0.810  0.745 │  2.0s   37.0s

Columnas:

  • Epoch: Época actual / total
  • Loss / vLoss: Pérdida de entrenamiento / validación (menor es mejor)
  • Acc: Accuracy (% bloques clasificados correctamente)
  • Prec: Precision (% de positivos predichos que son realmente positivos)
  • Rec: Recall (% de positivos reales que fueron detectados)
  • F1: Media armónica de Precision y Recall (métrica de balance)
  • t: Tiempo que tardó la época
  • ETA: Tiempo estimado para terminar

Early Stopping: El entrenamiento se detiene automáticamente si val-F1 no mejora durante 5 epochs consecutivos, previniendo overfitting.

Publicar en PyPI

# 1. Subir modelo a GitHub Releases (tag v0.2.0)
#    - modelo_entrenado.pth
#    - vocab.json

# 2. Build
pip install build twine
python -m build

# 3. Publicar
twine upload dist/*

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spharagusnet-0.2.0.tar.gz (21.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spharagusnet-0.2.0-py3-none-any.whl (17.2 kB view details)

Uploaded Python 3

File details

Details for the file spharagusnet-0.2.0.tar.gz.

File metadata

  • Download URL: spharagusnet-0.2.0.tar.gz
  • Upload date:
  • Size: 21.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for spharagusnet-0.2.0.tar.gz
Algorithm Hash digest
SHA256 83f32a294bc62e03a14e6c9ca8a4a810499586e01ba499de71fca33f8cf933ce
MD5 c6a91c95c68df6935f520fb4198d99cf
BLAKE2b-256 a86a3adc844cccf2da7e107eb821cb0840492bc5561ca35615b604aa402fef91

See more details on using hashes here.

File details

Details for the file spharagusnet-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: spharagusnet-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 17.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for spharagusnet-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 21f7d58ee58c41845f2d16d3a64884a050855c136cadb7749a97c451a0c2875c
MD5 7b331b847f0c5350bec33ef03147f281
BLAKE2b-256 215195963d632a9231e6cd3083b3dc44b314cb8d03447ade4553acc35ee61374

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page